diff --git a/x-pack/plugins/canvas/public/app.js b/x-pack/plugins/canvas/public/app.js index 51d60146b075f6..30bf9e90e8abc1 100644 --- a/x-pack/plugins/canvas/public/app.js +++ b/x-pack/plugins/canvas/public/app.js @@ -8,6 +8,8 @@ import 'ui/autoload/all'; import chrome from 'ui/chrome'; import './angular/config'; import './angular/services'; +import React from 'react'; +import ReactDOM from 'react-dom'; import { CanvasRootController } from './angular/controllers'; // Import the uiExports that the application uses @@ -24,5 +26,12 @@ import './lib/load_expression_types'; import './lib/load_transitions'; import 'uiExports/canvas'; +import { HelpMenu } from './components/help_menu/help_menu'; + // load the application chrome.setRootController('canvas', CanvasRootController); + +// add Canvas docs to help menu in global nav +chrome.helpExtension.set(domNode => { + ReactDOM.render(, domNode); +}); diff --git a/x-pack/plugins/canvas/public/components/fullscreen_control/fullscreen_control.js b/x-pack/plugins/canvas/public/components/fullscreen_control/fullscreen_control.js index 7985e468d8f57b..5a4f73775f6ca7 100644 --- a/x-pack/plugins/canvas/public/components/fullscreen_control/fullscreen_control.js +++ b/x-pack/plugins/canvas/public/components/fullscreen_control/fullscreen_control.js @@ -25,7 +25,13 @@ export class FullscreenControl extends React.PureComponent { return ( - + {children({ isFullscreen, toggleFullscreen: this.toggleFullscreen })} ); diff --git a/x-pack/plugins/canvas/public/components/help_menu/help_menu.js b/x-pack/plugins/canvas/public/components/help_menu/help_menu.js new file mode 100644 index 00000000000000..528545360a2baf --- /dev/null +++ b/x-pack/plugins/canvas/public/components/help_menu/help_menu.js @@ -0,0 +1,48 @@ +/* + * 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. + */ + +import React, { Fragment, PureComponent } from 'react'; +import { EuiButton, EuiHorizontalRule, EuiText, EuiSpacer, EuiPortal } from '@elastic/eui'; +import { documentationLinks } from '../../lib/documentation_links'; +import { KeyboardShortcutsDoc } from '../keyboard_shortcuts_doc'; + +export class HelpMenu extends PureComponent { + state = { isFlyoutVisible: false }; + + showFlyout = () => { + this.setState({ isFlyoutVisible: true }); + }; + + hideFlyout = () => { + this.setState({ isFlyoutVisible: false }); + }; + + render() { + return ( + + + + +

For Canvas specific information

+
+ + + Canvas documentation + + + + Keyboard shortcuts + + + {this.state.isFlyoutVisible && ( + + + + )} +
+ ); + } +} diff --git a/x-pack/plugins/canvas/public/components/help_menu/index.js b/x-pack/plugins/canvas/public/components/help_menu/index.js new file mode 100644 index 00000000000000..47682169c51e03 --- /dev/null +++ b/x-pack/plugins/canvas/public/components/help_menu/index.js @@ -0,0 +1,7 @@ +/* + * 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. + */ + +export { HelpMenu } from './help_menu'; diff --git a/x-pack/plugins/canvas/public/components/keyboard_shortcuts_doc/index.js b/x-pack/plugins/canvas/public/components/keyboard_shortcuts_doc/index.js new file mode 100644 index 00000000000000..8ee3da4fc35a91 --- /dev/null +++ b/x-pack/plugins/canvas/public/components/keyboard_shortcuts_doc/index.js @@ -0,0 +1,7 @@ +/* + * 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. + */ + +export { KeyboardShortcutsDoc } from './keyboard_shortcuts_doc'; diff --git a/x-pack/plugins/canvas/public/components/keyboard_shortcuts_doc/keyboard_shortcuts_doc.js b/x-pack/plugins/canvas/public/components/keyboard_shortcuts_doc/keyboard_shortcuts_doc.js new file mode 100644 index 00000000000000..f39ffaeeb4b18e --- /dev/null +++ b/x-pack/plugins/canvas/public/components/keyboard_shortcuts_doc/keyboard_shortcuts_doc.js @@ -0,0 +1,81 @@ +/* + * 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. + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import { + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiDescriptionList, + EuiHorizontalRule, + EuiCode, + EuiSpacer, +} from '@elastic/eui'; +import { keymap } from '../../lib/keymap'; +import { getClientPlatform } from '../../lib/get_client_platform'; +import { getId } from '../../lib/get_id'; + +const getPrettyShortcut = shortcut => { + if (!shortcut) { + return ''; + } + let result = shortcut.replace(/command/i, '⌘'); + result = result.replace(/option/i, '⌥'); + result = result.replace(/\+/gi, ' + '); + return ( + + {result + .split(/(\+)/g) //splits the array by '+' and keeps the '+'s as elements in the array + .map(key => (key === '+' ? key : {key}))} + + ); +}; + +const getDescriptionListItems = shortcuts => + Object.values(shortcuts).map(shortcutKeyMap => { + const os = getClientPlatform(); + const osShortcuts = shortcutKeyMap[os]; + return { + title: shortcutKeyMap.help, + description: osShortcuts.reduce((acc, shortcut, i) => { + if (i !== 0) { + acc.push(' or '); + } + acc.push(getPrettyShortcut(shortcut)); + return acc; + }, []), + }; + }); + +export const KeyboardShortcutsDoc = props => ( + + +

Keyboard Shortcuts

+
+ + {Object.values(keymap).map(namespace => { + const { displayName, ...shortcuts } = namespace; + return ( +
+

{displayName}

+ + + +
+ ); + })} +
+
+); + +KeyboardShortcutsDoc.propTypes = { + onClose: PropTypes.func.isRequired, +}; diff --git a/x-pack/plugins/canvas/public/lib/get_client_platform.js b/x-pack/plugins/canvas/public/lib/get_client_platform.js new file mode 100644 index 00000000000000..e64289f786d886 --- /dev/null +++ b/x-pack/plugins/canvas/public/lib/get_client_platform.js @@ -0,0 +1,19 @@ +/* + * 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. + */ + +export const getClientPlatform = () => { + const platform = navigator.platform.toLowerCase(); + if (platform.indexOf('mac') >= 0) { + return 'osx'; + } + if (platform.indexOf('win') >= 0) { + return 'windows'; + } + if (platform.indexOf('linux') >= 0) { + return 'linux'; + } + return 'other'; +}; diff --git a/x-pack/plugins/canvas/public/lib/keymap.js b/x-pack/plugins/canvas/public/lib/keymap.js index 88a553fe8a6ba4..ba988edf147cb3 100644 --- a/x-pack/plugins/canvas/public/lib/keymap.js +++ b/x-pack/plugins/canvas/public/lib/keymap.js @@ -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 elements' }, + CLONE: { ...getCtrlShortcuts('d'), help: 'Clone elements' }, + CUT: { ...getCtrlShortcuts('x'), help: 'Cut elements' }, + PASTE: { ...getCtrlShortcuts('v'), help: 'Paste' }, + DELETE: { + osx: ['backspace'], + windows: ['del', 'backspace'], + linux: ['del', 'backspace'], + other: ['del', 'backspace'], + help: 'Delete elements', + }, + BRING_FORWARD: { + ...getCtrlShortcuts('up'), + help: 'Send element forward one layer', + }, + SEND_BACKWARD: { + ...getCtrlShortcuts('down'), + help: 'Send element back one layer', + }, + BRING_TO_FRONT: { + ...getCtrlShortcuts('shift+up'), + help: 'Send element to front', + }, + SEND_TO_BACK: { + ...getCtrlShortcuts('shift+down'), + help: 'Send element to back', + }, + GROUP: { + osx: ['g'], + windows: ['g'], + linux: ['g'], + other: ['g'], + help: 'Group elements', + }, + UNGROUP: { + osx: ['u'], + windows: ['u'], + linux: ['u'], + other: ['u'], + help: 'Ungroup elements', + }, + }, 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, }, };