mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 16:33:15 +01:00
action info outline
This commit is contained in:
parent
dfae3bd967
commit
ea5a3ae93e
16 changed files with 120 additions and 52 deletions
|
|
@ -70,6 +70,10 @@ export default class Bus {
|
||||||
this.dispatch(token, Object.assign({}, this.state[token], partialState));
|
this.dispatch(token, Object.assign({}, this.state[token], partialState));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getState(fqn) {
|
||||||
|
return this.state[createToken(fqn)];
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(key, data) {
|
dispatch(key, data) {
|
||||||
if (this.lock.has(key)) {
|
if (this.lock.has(key)) {
|
||||||
console.warn('recursive dispatch');
|
console.warn('recursive dispatch');
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,12 @@ export default class AdjustableAbs extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {left, top, right, bottom, children, style, zIndex, ...props} = this.props;
|
let {left, top, right, bottom, children, style, zIndex, visible, ...props} = this.props;
|
||||||
return <div ref={el => this.el = el}
|
return <div ref={el => this.el = el}
|
||||||
style={{position: 'absolute', left, top, right, bottom, zIndex, ...style}} {...props}>
|
style={{
|
||||||
|
display: visible ? 'block' : 'none',
|
||||||
|
position: 'absolute', left, top, right, bottom, zIndex,
|
||||||
|
...style}} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
@ -49,6 +52,7 @@ export default class AdjustableAbs extends React.Component {
|
||||||
|
|
||||||
AdjustableAbs.defaultProps = {
|
AdjustableAbs.defaultProps = {
|
||||||
zIndex: 100,
|
zIndex: 100,
|
||||||
|
visible: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,9 @@ import ls from './Menu.less';
|
||||||
import AuxWidget from "./AuxWidget";
|
import AuxWidget from "./AuxWidget";
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
|
||||||
export default function Menu({children, visible, x, y, orientationUp, style, ...props}) {
|
export default function Menu({children, x, y, orientationUp, ...props}) {
|
||||||
return <AuxWidget
|
return <AuxWidget
|
||||||
className={cx(ls.root, 'disable-selection')}
|
className={cx(ls.root, 'disable-selection')}
|
||||||
style={{
|
|
||||||
display: visible ? 'block' : 'none',
|
|
||||||
...style
|
|
||||||
}}
|
|
||||||
zIndex={500}
|
zIndex={500}
|
||||||
left={x}
|
left={x}
|
||||||
top={orientationUp ? undefined : y}
|
top={orientationUp ? undefined : y}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ export default function Toolbar({children, className, small, ...props}) {
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ToolbarButton({children, disabled}) {
|
export function ToolbarButton({children, disabled, ...props}) {
|
||||||
return <div className={cx(ls.button, {disabled})}>
|
return <div className={cx(ls.button, disabled && ls.disabled)} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
web/app/cad/actions/actionButtonBehavior.js
Normal file
12
web/app/cad/actions/actionButtonBehavior.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
import {TOKENS as ACTION_TOKENS} from "./actionSystemPlugin";
|
||||||
|
|
||||||
|
export function mapActionBehavior(actionId) {
|
||||||
|
let actionRunToken = ACTION_TOKENS.actionRun(actionId);
|
||||||
|
|
||||||
|
return dispatch => ({
|
||||||
|
onClick: data => dispatch(actionRunToken, data),
|
||||||
|
onMouseEnter: ({pageX, pageY}) => dispatch(ACTION_TOKENS.SHOW_HINT_FOR, [actionId, pageX, pageY]),
|
||||||
|
onMouseLeave: () => dispatch(ACTION_TOKENS.SHOW_HINT_FOR, null)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -40,7 +40,8 @@ export function activate(context) {
|
||||||
}
|
}
|
||||||
bus.subscribe(TOKENS.actionRun(action.id), (data) => action.invoke(context, data));
|
bus.subscribe(TOKENS.actionRun(action.id), (data) => action.invoke(context, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bus.enableState(TOKENS.HINT, null);
|
||||||
function registerAction(action) {
|
function registerAction(action) {
|
||||||
register(action);
|
register(action);
|
||||||
}
|
}
|
||||||
|
|
@ -48,16 +49,57 @@ export function activate(context) {
|
||||||
function registerActions(actions) {
|
function registerActions(actions) {
|
||||||
actions.forEach(action => register(action));
|
actions.forEach(action => register(action));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchActionHint(bus);
|
||||||
|
|
||||||
context.services.action = {run, registerAction, registerActions}
|
context.services.action = {run, registerAction, registerActions}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TOKENS = {
|
|
||||||
ACTION_STATE_NS: 'action.state',
|
|
||||||
ACTION_APPEARANCE_NS: 'action.appearance',
|
function synchActionHint(bus) {
|
||||||
ACTION_RUN_NS: 'action.run',
|
|
||||||
|
|
||||||
actionState: (actionId) => createToken(TOKENS.ACTION_STATE_NS, actionId),
|
let lastRequest = null;
|
||||||
actionAppearance: (actionId) => createToken(TOKENS.ACTION_APPEARANCE_NS, actionId),
|
|
||||||
actionRun: (actionId) => createToken(TOKENS.ACTION_RUN_NS, actionId),
|
// bus.subscribe(TOKENS.REQUEST_SHOW_HINT_FOR
|
||||||
|
bus.subscribe(TOKENS.SHOW_HINT_FOR, request => {
|
||||||
|
if (lastRequest !== null) {
|
||||||
|
if (request !== null) {
|
||||||
|
if (request[0] === lastRequest[0]) {
|
||||||
|
Object.assign(lastRequest, request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastRequest.spoiled = true;
|
||||||
|
}
|
||||||
|
lastRequest = request;
|
||||||
|
if (request) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!request.spoiled) {
|
||||||
|
let [actionId, x, y] = request;
|
||||||
|
let actionState = bus.getState(TOKENS.actionState(actionId));
|
||||||
|
let actionAppearance = bus.getState(TOKENS.actionAppearance(actionId));
|
||||||
|
if (actionState && actionAppearance) {
|
||||||
|
bus.dispatch(TOKENS.HINT, {
|
||||||
|
actionId, x: x + 10, y: y + 10,
|
||||||
|
info: actionAppearance.info,
|
||||||
|
hint: actionState.hint
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
} else {
|
||||||
|
bus.dispatch(TOKENS.HINT, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ACTION_NS = 'action';
|
||||||
|
export const TOKENS = {
|
||||||
|
actionState: (actionId) => createToken(ACTION_NS, 'state', actionId),
|
||||||
|
actionAppearance: (actionId) => createToken(ACTION_NS, 'appearance', actionId),
|
||||||
|
actionRun: (actionId) => createToken(ACTION_NS, 'run', actionId),
|
||||||
|
|
||||||
|
SHOW_HINT_FOR: createToken(ACTION_NS, 'showHintFor'),
|
||||||
|
HINT: createToken(ACTION_NS, 'hint'),
|
||||||
};
|
};
|
||||||
24
web/app/cad/dom/actionInfo/ActionInfo.jsx
Normal file
24
web/app/cad/dom/actionInfo/ActionInfo.jsx
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React, {Fragment} from 'react';
|
||||||
|
import ls from './ActionInfo.less';
|
||||||
|
|
||||||
|
import AuxWidget from '../../../../../modules/ui/components/AuxWidget';
|
||||||
|
import connect from '../../../../../modules/ui/connect';
|
||||||
|
import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin';
|
||||||
|
import {TOKENS as KeyboardTokens} from "../../keyboard/keyboardPlugin";
|
||||||
|
|
||||||
|
function ActionInfo({actionId, x, y, info, hint, hotKey}) {
|
||||||
|
let visible = !!actionId;
|
||||||
|
|
||||||
|
return <AuxWidget visible={visible}
|
||||||
|
left={x} top={y} className={ls.root} zIndex={550}>
|
||||||
|
{visible && <Fragment>
|
||||||
|
<div className='hint'>{hint}</div>
|
||||||
|
<div className='info'>{info}</div>
|
||||||
|
{hotKey && <div className='hotKey'>hotkey: {hotKey}</div>}
|
||||||
|
</Fragment>}
|
||||||
|
</AuxWidget>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect([ACTION_TOKENS.HINT, KeyboardTokens.KEYMAP], ActionInfo, undefined,
|
||||||
|
([ hintInfo, keymap ]) => (Object.assign({hotKey: hintInfo && keymap[hintInfo.actionId]}, hintInfo)) );
|
||||||
|
|
||||||
3
web/app/cad/dom/actionInfo/ActionInfo.less
Normal file
3
web/app/cad/dom/actionInfo/ActionInfo.less
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.root {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
4
web/app/cad/dom/actionInfo/actionInfoPlugin.js
Normal file
4
web/app/cad/dom/actionInfo/actionInfoPlugin.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
export function activate() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function ActionInfo({children}) {
|
|
||||||
|
|
||||||
return <AuxWidget>
|
|
||||||
{children}
|
|
||||||
</AuxWidget>;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -15,9 +15,9 @@ export default function ControlBar({left, right}) {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ControlBarButton({onClick, onElement, disabled, children}) {
|
export function ControlBarButton({onElement, disabled, children, onClick, ...props}) {
|
||||||
return <span className={cx(ls.button, 'disable-selection', {disabled})}
|
return <span className={cx(ls.button, 'disable-selection', {disabled})}
|
||||||
onClick={disabled || onClick} ref={onElement}>
|
onClick={disabled || onClick} ref={onElement} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import AuxWidget from './../../../../../modules/ui/components/AuxWidget';
|
|
||||||
|
|
||||||
export default function MessageSink({children, ...props}) {
|
|
||||||
|
|
||||||
return <AuxWidget {...props}>
|
|
||||||
{children}
|
|
||||||
</AuxWidget>;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -5,6 +5,7 @@ import Fa from 'ui/components/Fa';
|
||||||
import {TOKENS as UI_TOKENS} from '../uiEntryPointsPlugin';
|
import {TOKENS as UI_TOKENS} from '../uiEntryPointsPlugin';
|
||||||
import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin';
|
import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin';
|
||||||
import {toIdAndOverrides} from "../../actions/actionRef";
|
import {toIdAndOverrides} from "../../actions/actionRef";
|
||||||
|
import {mapActionBehavior} from "../../actions/actionButtonBehavior";
|
||||||
|
|
||||||
|
|
||||||
export default function PlugableControlBar() {
|
export default function PlugableControlBar() {
|
||||||
|
|
@ -14,11 +15,10 @@ export default function PlugableControlBar() {
|
||||||
function ButtonGroup({actions}) {
|
function ButtonGroup({actions}) {
|
||||||
return actions.map(actionRef => {
|
return actions.map(actionRef => {
|
||||||
let [id, overrides] = toIdAndOverrides(actionRef);
|
let [id, overrides] = toIdAndOverrides(actionRef);
|
||||||
let actionRunToken = ACTION_TOKENS.actionRun(id);
|
|
||||||
let Comp = connect([ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)],
|
let Comp = connect([ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)],
|
||||||
ActionButton, {actionId: id},
|
ActionButton, {actionId: id},
|
||||||
([appearance, state]) => Object.assign({}, appearance, state, overrides),
|
([appearance, state]) => Object.assign({}, appearance, state, overrides),
|
||||||
dispatch => ({runAction: (data) => dispatch(actionRunToken, data)})
|
mapActionBehavior(id)
|
||||||
);
|
);
|
||||||
return <Comp key={id}/>;
|
return <Comp key={id}/>;
|
||||||
});
|
});
|
||||||
|
|
@ -31,12 +31,16 @@ function isMenuAction(actionId) {
|
||||||
class ActionButton extends React.Component {
|
class ActionButton extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {label, cssIcons, runAction, enabled, visible, actionId} = this.props;
|
let {label, cssIcons, enabled, visible, actionId, ...props} = this.props;
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const onClick = e => runAction(isMenuAction(actionId) ? getMenuData(this.el) : undefined);
|
if (isMenuAction(actionId)) {
|
||||||
return <ControlBarButton {...{onClick, disabled: !enabled}} onElement={el => this.el = el}>
|
let onClick = props.onClick;
|
||||||
|
props.onClick = e => onClick(getMenuData(this.el));
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ControlBarButton disabled={!enabled} onElement={el => this.el = el} {...props} >
|
||||||
{cssIcons && <Fa fa={cssIcons} fw/>} {label}
|
{cssIcons && <Fa fa={cssIcons} fw/>} {label}
|
||||||
</ControlBarButton>;
|
</ControlBarButton>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,14 @@ import MenuHolder from "../menu/MenuHolder";
|
||||||
import {TOKENS as MENU_TOKENS} from '../menu/menuPlugin';
|
import {TOKENS as MENU_TOKENS} from '../menu/menuPlugin';
|
||||||
|
|
||||||
import WindowSystem from 'ui/WindowSystem';
|
import WindowSystem from 'ui/WindowSystem';
|
||||||
import MessageSink from './MessageSink';
|
import ActionInfo from "../actionInfo/ActionInfo";
|
||||||
|
|
||||||
|
|
||||||
export default class UISystem extends React.Component {
|
export default class UISystem extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <div {...this.props} onMouseDown={this.closeAllUpPopups}>
|
return <div {...this.props} onMouseDown={this.closeAllUpPopups}>
|
||||||
<MenuHolder />
|
<MenuHolder />
|
||||||
<MessageSink />
|
<ActionInfo />
|
||||||
<WindowSystem />
|
<WindowSystem />
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import NumberControl from "ui/components/controls/NumberControl";
|
||||||
import ButtonGroup from "ui/components/controls/ButtonGroup";
|
import ButtonGroup from "ui/components/controls/ButtonGroup";
|
||||||
import Button from "ui/components/controls/Button";
|
import Button from "ui/components/controls/Button";
|
||||||
import TextControl from './../../../../../modules/ui/components/controls/TextControl';
|
import TextControl from './../../../../../modules/ui/components/controls/TextControl';
|
||||||
import MessageSink from './MessageSink';
|
|
||||||
import UISystem from './UISystem';
|
import UISystem from './UISystem';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue