action info outline

This commit is contained in:
Val Erastov 2018-01-17 19:19:33 -08:00
parent dfae3bd967
commit ea5a3ae93e
16 changed files with 120 additions and 52 deletions

View file

@ -70,6 +70,10 @@ export default class Bus {
this.dispatch(token, Object.assign({}, this.state[token], partialState));
}
getState(fqn) {
return this.state[createToken(fqn)];
}
dispatch(key, data) {
if (this.lock.has(key)) {
console.warn('recursive dispatch');

View file

@ -39,9 +39,12 @@ export default class AdjustableAbs extends React.Component {
}
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}
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}
</div>;
}
@ -49,6 +52,7 @@ export default class AdjustableAbs extends React.Component {
AdjustableAbs.defaultProps = {
zIndex: 100,
visible: true
};

View file

@ -5,13 +5,9 @@ import ls from './Menu.less';
import AuxWidget from "./AuxWidget";
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
className={cx(ls.root, 'disable-selection')}
style={{
display: visible ? 'block' : 'none',
...style
}}
zIndex={500}
left={x}
top={orientationUp ? undefined : y}

View file

@ -9,8 +9,8 @@ export default function Toolbar({children, className, small, ...props}) {
</div>;
}
export function ToolbarButton({children, disabled}) {
return <div className={cx(ls.button, {disabled})}>
export function ToolbarButton({children, disabled, ...props}) {
return <div className={cx(ls.button, disabled && ls.disabled)} {...props}>
{children}
</div>;
}

View 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)
});
}

View file

@ -41,6 +41,7 @@ export function activate(context) {
bus.subscribe(TOKENS.actionRun(action.id), (data) => action.invoke(context, data));
}
bus.enableState(TOKENS.HINT, null);
function registerAction(action) {
register(action);
}
@ -49,15 +50,56 @@ export function activate(context) {
actions.forEach(action => register(action));
}
synchActionHint(bus);
context.services.action = {run, registerAction, registerActions}
}
export const TOKENS = {
ACTION_STATE_NS: 'action.state',
ACTION_APPEARANCE_NS: 'action.appearance',
ACTION_RUN_NS: 'action.run',
actionState: (actionId) => createToken(TOKENS.ACTION_STATE_NS, actionId),
actionAppearance: (actionId) => createToken(TOKENS.ACTION_APPEARANCE_NS, actionId),
actionRun: (actionId) => createToken(TOKENS.ACTION_RUN_NS, actionId),
function synchActionHint(bus) {
let lastRequest = null;
// 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'),
};

View 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)) );

View file

@ -0,0 +1,3 @@
.root {
display: flex;
}

View file

@ -0,0 +1,4 @@
export function activate() {
}

View file

@ -1,12 +0,0 @@
import React from 'react';
export default function ActionInfo({children}) {
return <AuxWidget>
{children}
</AuxWidget>;
}

View file

@ -15,9 +15,9 @@ export default function ControlBar({left, right}) {
</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})}
onClick={disabled || onClick} ref={onElement}>
onClick={disabled || onClick} ref={onElement} {...props}>
{children}
</span>
}

View file

@ -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>;
}

View file

@ -5,6 +5,7 @@ import Fa from 'ui/components/Fa';
import {TOKENS as UI_TOKENS} from '../uiEntryPointsPlugin';
import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin';
import {toIdAndOverrides} from "../../actions/actionRef";
import {mapActionBehavior} from "../../actions/actionButtonBehavior";
export default function PlugableControlBar() {
@ -14,11 +15,10 @@ export default function PlugableControlBar() {
function ButtonGroup({actions}) {
return actions.map(actionRef => {
let [id, overrides] = toIdAndOverrides(actionRef);
let actionRunToken = ACTION_TOKENS.actionRun(id);
let Comp = connect([ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)],
ActionButton, {actionId: id},
([appearance, state]) => Object.assign({}, appearance, state, overrides),
dispatch => ({runAction: (data) => dispatch(actionRunToken, data)})
mapActionBehavior(id)
);
return <Comp key={id}/>;
});
@ -31,12 +31,16 @@ function isMenuAction(actionId) {
class ActionButton extends React.Component {
render() {
let {label, cssIcons, runAction, enabled, visible, actionId} = this.props;
let {label, cssIcons, enabled, visible, actionId, ...props} = this.props;
if (!visible) {
return null;
}
const onClick = e => runAction(isMenuAction(actionId) ? getMenuData(this.el) : undefined);
return <ControlBarButton {...{onClick, disabled: !enabled}} onElement={el => this.el = el}>
if (isMenuAction(actionId)) {
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}
</ControlBarButton>;
}

View file

@ -4,15 +4,14 @@ import MenuHolder from "../menu/MenuHolder";
import {TOKENS as MENU_TOKENS} from '../menu/menuPlugin';
import WindowSystem from 'ui/WindowSystem';
import MessageSink from './MessageSink';
import ActionInfo from "../actionInfo/ActionInfo";
export default class UISystem extends React.Component {
render() {
return <div {...this.props} onMouseDown={this.closeAllUpPopups}>
<MenuHolder />
<MessageSink />
<ActionInfo />
<WindowSystem />
{this.props.children}
</div>

View file

@ -23,7 +23,6 @@ import NumberControl from "ui/components/controls/NumberControl";
import ButtonGroup from "ui/components/controls/ButtonGroup";
import Button from "ui/components/controls/Button";
import TextControl from './../../../../../modules/ui/components/controls/TextControl';
import MessageSink from './MessageSink';
import UISystem from './UISystem';