Skip to content

Commit

Permalink
[Canvas] Adds doc links and keyboard shortcut cheatsheat to help menu (
Browse files Browse the repository at this point in the history
…#31335) (#31729)

* Fixed tooltip in fullscreen_control

    * Removed aeroelastic keyboard event handlers

    * Added input target check to workpad page keyhandler

    * Fixed ungrouping

    * Removed tabIndex

    * Fix: reintroduce additional call on keyboard event
  • Loading branch information
cqliu1 authored Feb 22, 2019
1 parent 5d59537 commit 7488b22
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ export class FullscreenControl extends React.PureComponent {

return (
<span>
<Shortcuts name="EDITOR" handler={keyHandler} targetNodeSelector="body" global isolate />
<Shortcuts
name="PRESENTATION"
handler={keyHandler}
targetNodeSelector="body"
global
isolate
/>
{children({ isFullscreen, toggleFullscreen: this.toggleFullscreen })}
</span>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const WorkpadHeader = ({
<EuiFlexItem grow={false}>
<FullscreenControl>
{({ toggleFullscreen }) => (
<EuiToolTip position="bottom" content="Toggle fullscreen mode">
<EuiToolTip position="bottom" content="Enter fullscreen mode">
<EuiButtonIcon
iconType="fullScreen"
aria-label="View fullscreen"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,70 +124,10 @@ const handleMouseDown = (commit, e, isEditable) => {
);
};

const keyCode = key => (key === 'Meta' ? 'MetaLeft' : 'Key' + key.toUpperCase());

const isTextInput = ({ tagName, type }) => {
// input types that aren't variations of text input
const nonTextInputs = [
'button',
'checkbox',
'color',
'file',
'image',
'radio',
'range',
'reset',
'submit',
];

switch (tagName.toLowerCase()) {
case 'input':
return !nonTextInputs.includes(type);
case 'textarea':
return true;
default:
return false;
}
};

const modifierKey = key => ['KeyALT', 'KeyCONTROL'].indexOf(keyCode(key)) > -1;

const handleKeyDown = (commit, e, isEditable) => {
const { key } = e;

if (isEditable && !modifierKey(key)) {
commit('keyboardEvent', {
event: 'keyDown',
code: keyCode(key), // convert to standard event code
});
}
};

const handleKeyPress = (commit, e, isEditable) => {
const { key, target } = e;
const upcaseKey = key && key.toUpperCase();
if (isEditable && !isTextInput(target) && 'GU'.indexOf(upcaseKey) !== -1) {
commit('actionEvent', {
event: upcaseKey === 'G' ? 'group' : 'ungroup',
});
}
};

const handleKeyUp = (commit, { key }, isEditable) => {
if (isEditable && !modifierKey(key)) {
commit('keyboardEvent', {
event: 'keyUp',
code: keyCode(key), // convert to standard event code
});
}
};

export const eventHandlers = {
onMouseDown: props => e => handleMouseDown(props.commit, e, props.isEditable),
onMouseMove: props => e => handleMouseMove(props.commit, e, props.isEditable),
onKeyDown: props => e => handleKeyDown(props.commit, e, props.isEditable),
onKeyPress: props => e => handleKeyPress(props.commit, e, props.isEditable),
onKeyUp: props => e => handleKeyUp(props.commit, e, props.isEditable),
onKeyDown: props => () => props.commit('keyboardEvent'), // dummy event
onWheel: props => e => handleWheel(props.commit, e, props.isEditable),
resetHandler: () => () => resetHandler(),
};
17 changes: 14 additions & 3 deletions x-pack/plugins/canvas/public/components/workpad_page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ export const WorkpadPage = compose(
];
};

const selectedPrimaryShapeObjects = selectedPrimaryShapes.map(id =>
shapes.find(s => s.id === id)
);
const selectedPrimaryShapeObjects = selectedPrimaryShapes
.map(id => shapes.find(s => s.id === id))
.filter(shape => shape);

const selectedPersistentPrimaryShapes = flatten(
selectedPrimaryShapeObjects.map(shape =>
shape.subtype === 'adHocGroup'
Expand Down Expand Up @@ -217,6 +218,16 @@ export const WorkpadPage = compose(
};
}
), // Updates states; needs to have both local and global
withHandlers({
groupElements: ({ commit }) => () =>
commit('actionEvent', {
event: 'group',
}),
ungroupElements: ({ commit }) => () =>
commit('actionEvent', {
event: 'ungroup',
}),
}),
withHandlers(eventHandlers) // Captures user intent, needs to have reconciled state
)(Component);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { TooltipAnnotation } from '../tooltip_annotation';
import { RotationHandle } from '../rotation_handle';
import { BorderConnection } from '../border_connection';
import { BorderResizeHandle } from '../border_resize_handle';
import { isTextInput } from '../../lib/is_text_input';

// NOTE: the data-shared-* attributes here are used for reporting
export class WorkpadPage extends PureComponent {
Expand All @@ -39,7 +40,6 @@ export class WorkpadPage extends PureComponent {
isEditable: PropTypes.bool.isRequired,
onDoubleClick: PropTypes.func,
onKeyDown: PropTypes.func,
onKeyUp: PropTypes.func,
onMouseDown: PropTypes.func,
onMouseMove: PropTypes.func,
onMouseUp: PropTypes.func,
Expand Down Expand Up @@ -72,8 +72,6 @@ export class WorkpadPage extends PureComponent {
isEditable,
onDoubleClick,
onKeyDown,
onKeyPress,
onKeyUp,
onMouseDown,
onMouseMove,
onMouseUp,
Expand All @@ -88,38 +86,48 @@ export class WorkpadPage extends PureComponent {
bringToFront,
sendBackward,
sendToBack,
groupElements,
ungroupElements,
} = this.props;

const keyHandler = (action, event) => {
event.preventDefault();
switch (action) {
case 'COPY':
copyElements();
break;
case 'CLONE':
duplicateElements();
break;
case 'CUT':
cutElements();
break;
case 'DELETE':
removeElements();
break;
case 'PASTE':
pasteElements();
break;
case 'BRING_FORWARD':
bringForward();
break;
case 'BRING_TO_FRONT':
bringToFront();
break;
case 'SEND_BACKWARD':
sendBackward();
break;
case 'SEND_TO_BACK':
sendToBack();
break;
if (!isTextInput(event.target)) {
event.preventDefault();
switch (action) {
case 'COPY':
copyElements();
break;
case 'CLONE':
duplicateElements();
break;
case 'CUT':
cutElements();
break;
case 'DELETE':
removeElements();
break;
case 'PASTE':
pasteElements();
break;
case 'BRING_FORWARD':
bringForward();
break;
case 'BRING_TO_FRONT':
bringToFront();
break;
case 'SEND_BACKWARD':
sendBackward();
break;
case 'SEND_TO_BACK':
sendToBack();
break;
case 'GROUP':
groupElements();
break;
case 'UNGROUP':
ungroupElements();
break;
}
}
};

Expand All @@ -137,16 +145,13 @@ export class WorkpadPage extends PureComponent {
width,
cursor,
}}
onKeyDown={onKeyDown}
onMouseMove={onMouseMove}
onMouseUp={onMouseUp}
onMouseDown={onMouseDown}
onKeyDown={onKeyDown}
onKeyPress={onKeyPress}
onKeyUp={onKeyUp}
onDoubleClick={onDoubleClick}
onAnimationEnd={onAnimationEnd}
onWheel={onWheel}
tabIndex={0} // needed to capture keyboard events; focusing is also needed but React apparently does so implicitly
>
{isEditable && (
<Shortcuts
Expand Down
29 changes: 29 additions & 0 deletions x-pack/plugins/canvas/public/lib/is_text_input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

// input types that aren't variations of text input
const nonTextInputs = [
'button',
'checkbox',
'color',
'file',
'image',
'radio',
'range',
'reset',
'submit',
];

export const isTextInput = ({ tagName, type }) => {
switch (tagName.toLowerCase()) {
case 'input':
return !nonTextInputs.includes(type);
case 'textarea':
return true;
default:
return false;
}
};
89 changes: 67 additions & 22 deletions x-pack/plugins/canvas/public/lib/keymap.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,36 +38,81 @@ const getCtrlShortcuts = shortcuts => {
};
};

const refreshShortcut = getAltShortcuts('r');
const previousPageShortcut = getAltShortcuts('[');
const nextPageShortcut = getAltShortcuts(']');
const refreshShortcut = { ...getAltShortcuts('r'), help: 'Refresh workpad' };
const previousPageShortcut = { ...getAltShortcuts('['), help: 'Go to previous page' };
const nextPageShortcut = { ...getAltShortcuts(']'), help: 'Go to next page' };

export const keymap = {
ELEMENT: {
displayName: 'Element controls',
COPY: { ...getCtrlShortcuts('c'), help: 'Copy' },
CLONE: { ...getCtrlShortcuts('d'), help: 'Clone' },
CUT: { ...getCtrlShortcuts('x'), help: 'Cut' },
PASTE: { ...getCtrlShortcuts('v'), help: 'Paste' },
DELETE: {
osx: ['backspace'],
windows: ['del', 'backspace'],
linux: ['del', 'backspace'],
other: ['del', 'backspace'],
help: 'Delete',
},
BRING_FORWARD: {
...getCtrlShortcuts('up'),
help: 'Send forward',
},
BRING_TO_FRONT: {
...getCtrlShortcuts('shift+up'),
help: 'Send to front',
},
SEND_BACKWARD: {
...getCtrlShortcuts('down'),
help: 'Send backward',
},
SEND_TO_BACK: {
...getCtrlShortcuts('shift+down'),
help: 'Send to back',
},
GROUP: {
osx: ['g'],
windows: ['g'],
linux: ['g'],
other: ['g'],
help: 'Group',
},
UNGROUP: {
osx: ['u'],
windows: ['u'],
linux: ['u'],
other: ['u'],
help: 'Ungroup',
},
},
EDITOR: {
UNDO: getCtrlShortcuts('z'),
REDO: getCtrlShortcuts('shift+z'),
displayName: 'Editor controls',
UNDO: { ...getCtrlShortcuts('z'), help: 'Undo last action' },
REDO: { ...getCtrlShortcuts('shift+z'), help: 'Redo last action' },
PREV: previousPageShortcut,
NEXT: nextPageShortcut,
FULLSCREEN: getAltShortcuts(['p', 'f']),
FULLSCREEN_EXIT: ['escape'],
EDITING: getAltShortcuts('e'),
GRID: getAltShortcuts('g'),
EDITING: { ...getAltShortcuts('e'), help: 'Toggle edit mode' },
GRID: { ...getAltShortcuts('g'), help: 'Show grid' },
REFRESH: refreshShortcut,
},
ELEMENT: {
COPY: getCtrlShortcuts('c'),
CLONE: getCtrlShortcuts('d'),
CUT: getCtrlShortcuts('x'),
PASTE: getCtrlShortcuts('v'),
DELETE: ['del', 'backspace'],
BRING_FORWARD: getCtrlShortcuts('up'),
SEND_BACKWARD: getCtrlShortcuts('down'),
BRING_TO_FRONT: getCtrlShortcuts('shift+up'),
SEND_TO_BACK: getCtrlShortcuts('shift+down'),
},
PRESENTATION: {
PREV: mapValues(previousPageShortcut, osShortcuts => osShortcuts.concat(['backspace', 'left'])),
NEXT: mapValues(nextPageShortcut, osShortcuts => osShortcuts.concat(['space', 'right'])),
displayName: 'Presentation mode',
FULLSCREEN: { ...getAltShortcuts(['p', 'f']), help: 'Enter presentation mode' },
FULLSCREEN_EXIT: {
osx: ['esc'],
windows: ['esc'],
linux: ['esc'],
other: ['esc'],
help: 'Exit presentation mode',
},
PREV: mapValues(previousPageShortcut, (osShortcuts, key) =>
key === 'help' ? osShortcuts : osShortcuts.concat(['backspace', 'left'])
),
NEXT: mapValues(nextPageShortcut, (osShortcuts, key) =>
key === 'help' ? osShortcuts : osShortcuts.concat(['space', 'right'])
),
REFRESH: refreshShortcut,
},
};

0 comments on commit 7488b22

Please sign in to comment.