mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* Added shortcuts to tooltips * Added stories and tests * Added snapshots * Updated copy * Updated snapshots * Pulled out OS in KeyboardShortcutsDoc * Updated snapshot
This commit is contained in:
parent
4e3e9c1431
commit
87b909397e
18 changed files with 1865 additions and 237 deletions
|
@ -19,6 +19,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { Shortcuts } from 'react-shortcuts';
|
||||
import { ExpressionInput } from '../expression_input';
|
||||
import { ToolTipShortcut } from '../tool_tip_shortcut';
|
||||
|
||||
const { useRef } = React;
|
||||
|
||||
|
@ -131,14 +132,22 @@ export const Expression = ({
|
|||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill
|
||||
disabled={!!error}
|
||||
onClick={() => setExpression(formState.expression)}
|
||||
size="s"
|
||||
<EuiToolTip
|
||||
content={
|
||||
<span>
|
||||
Run the expression <ToolTipShortcut namespace="EXPRESSION" action="RUN" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
Run
|
||||
</EuiButton>
|
||||
<EuiButton
|
||||
fill
|
||||
disabled={!!error}
|
||||
onClick={() => setExpression(formState.expression)}
|
||||
size="s"
|
||||
>
|
||||
Run
|
||||
</EuiButton>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
@ -156,4 +165,5 @@ Expression.propTypes = {
|
|||
error: PropTypes.string,
|
||||
isAutocompleteEnabled: PropTypes.bool,
|
||||
toggleAutocompleteEnabled: PropTypes.func,
|
||||
runExpressionShortcut: PropTypes.string.isRequired,
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 { storiesOf } from '@storybook/react';
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { KeyboardShortcutsDoc } from '../keyboard_shortcuts_doc';
|
||||
|
||||
storiesOf('components/KeyboardShortcutsDoc', module).add('default', () => (
|
||||
<KeyboardShortcutsDoc onClose={action('onClose')} />
|
||||
));
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* 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,
|
||||
EuiTitle,
|
||||
} 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(/left/i, '←');
|
||||
result = result.replace(/right/i, '→');
|
||||
result = result.replace(/up/i, '↑');
|
||||
result = result.replace(/down/i, '↓');
|
||||
|
||||
return (
|
||||
<span key={getId('span')}>
|
||||
{result
|
||||
.split(/(\+)/g) //splits the array by '+' and keeps the '+'s as elements in the array
|
||||
.map(key => (key === '+' ? ` ${key} ` : <EuiCode key={getId('shortcut')}>{key}</EuiCode>))}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
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 => (
|
||||
<EuiFlyout closeButtonAriaLabel="Closes keyboard shortcuts reference" size="s" {...props}>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="s">
|
||||
<h2>Keyboard Shortcuts</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
{Object.values(keymap).map(namespace => {
|
||||
const { displayName, ...shortcuts } = namespace;
|
||||
return (
|
||||
<div key={getId('shortcuts')} className="canvasKeyboardShortcut">
|
||||
<EuiTitle size="xs">
|
||||
<h4>{displayName}</h4>
|
||||
</EuiTitle>
|
||||
<EuiHorizontalRule margin="s" />
|
||||
<EuiDescriptionList
|
||||
textStyle="reverse"
|
||||
type="column"
|
||||
listItems={getDescriptionListItems(shortcuts)}
|
||||
compressed
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
);
|
||||
|
||||
KeyboardShortcutsDoc.propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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, { FunctionComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
EuiFlyout,
|
||||
EuiFlyoutHeader,
|
||||
EuiFlyoutBody,
|
||||
EuiDescriptionList,
|
||||
EuiHorizontalRule,
|
||||
EuiCode,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { keymap, ShortcutMap, ShortcutNameSpace } from '../../lib/keymap';
|
||||
import { getClientPlatform } from '../../lib/get_client_platform';
|
||||
import { getId } from '../../lib/get_id';
|
||||
import { getPrettyShortcut } from '../../lib/get_pretty_shortcut';
|
||||
|
||||
interface DescriptionListItem {
|
||||
title: string;
|
||||
description: JSX.Element[];
|
||||
}
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* click handler for closing flyout
|
||||
*/
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const os = getClientPlatform();
|
||||
|
||||
const getDescriptionListItems = (shortcuts: ShortcutMap[]): DescriptionListItem[] =>
|
||||
shortcuts.map(
|
||||
(shortcutKeyMap: ShortcutMap): DescriptionListItem => {
|
||||
const osShortcuts = shortcutKeyMap[os];
|
||||
return {
|
||||
title: shortcutKeyMap.help,
|
||||
description: osShortcuts.reduce((acc: JSX.Element[], shortcut, i): JSX.Element[] => {
|
||||
if (i !== 0) {
|
||||
acc.push(<span key={getId('span')}> or </span>);
|
||||
}
|
||||
acc.push(
|
||||
<span key={getId('span')}>
|
||||
{getPrettyShortcut(shortcut)
|
||||
.split(/(\+)/g) // splits the array by '+' and keeps the '+'s as elements in the array
|
||||
.map(key => (key === '+' ? ` ` : <EuiCode key={getId('shortcut')}>{key}</EuiCode>))}
|
||||
</span>
|
||||
);
|
||||
return acc;
|
||||
}, []),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
export const KeyboardShortcutsDoc: FunctionComponent<Props> = ({ onClose }) => (
|
||||
<EuiFlyout closeButtonAriaLabel="Closes keyboard shortcuts reference" size="s" onClose={onClose}>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="s">
|
||||
<h2>Keyboard Shortcuts</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
{Object.values(keymap).map((namespace: ShortcutNameSpace) => {
|
||||
const { displayName, ...shortcuts } = namespace;
|
||||
return (
|
||||
<div key={getId('shortcuts')} className="canvasKeyboardShortcut">
|
||||
<EuiTitle size="xs">
|
||||
<h4>{displayName}</h4>
|
||||
</EuiTitle>
|
||||
<EuiHorizontalRule margin="s" />
|
||||
<EuiDescriptionList
|
||||
textStyle="reverse"
|
||||
type="column"
|
||||
listItems={getDescriptionListItems(Object.values(shortcuts) as ShortcutMap[])}
|
||||
compressed
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
);
|
||||
|
||||
KeyboardShortcutsDoc.propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
|
@ -20,6 +20,7 @@ import {
|
|||
// @ts-ignore unconverted component
|
||||
import { Popover } from '../popover';
|
||||
import { CustomElementModal } from '../custom_element_modal';
|
||||
import { ToolTipShortcut } from '../tool_tip_shortcut/';
|
||||
|
||||
const topBorderClassName = 'canvasContextMenu--topBorder';
|
||||
|
||||
|
@ -153,7 +154,15 @@ export class SidebarHeader extends Component<Props, State> {
|
|||
return (
|
||||
<Fragment>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip position="bottom" content="Move element to top layer">
|
||||
<EuiToolTip
|
||||
position="bottom"
|
||||
content={
|
||||
<span>
|
||||
Bring to front
|
||||
<ToolTipShortcut namespace="ELEMENT" action="BRING_TO_FRONT" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
color="text"
|
||||
iconType="sortUp"
|
||||
|
@ -163,7 +172,15 @@ export class SidebarHeader extends Component<Props, State> {
|
|||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip position="bottom" content="Move element up one layer">
|
||||
<EuiToolTip
|
||||
position="bottom"
|
||||
content={
|
||||
<span>
|
||||
Bring forward
|
||||
<ToolTipShortcut namespace="ELEMENT" action="BRING_FORWARD" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
color="text"
|
||||
iconType="arrowUp"
|
||||
|
@ -173,7 +190,15 @@ export class SidebarHeader extends Component<Props, State> {
|
|||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip position="bottom" content="Move element down one layer">
|
||||
<EuiToolTip
|
||||
position="bottom"
|
||||
content={
|
||||
<span>
|
||||
Send backward
|
||||
<ToolTipShortcut namespace="ELEMENT" action="SEND_BACKWARD" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
color="text"
|
||||
iconType="arrowDown"
|
||||
|
@ -183,7 +208,15 @@ export class SidebarHeader extends Component<Props, State> {
|
|||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip position="bottom" content="Move element to bottom layer">
|
||||
<EuiToolTip
|
||||
position="bottom"
|
||||
content={
|
||||
<span>
|
||||
Send to back
|
||||
<ToolTipShortcut namespace="ELEMENT" action="SEND_TO_BACK" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
color="text"
|
||||
iconType="sortDown"
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots components/ToolTipShortcut with alt 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#343741",
|
||||
"padding": "5px",
|
||||
"width": "100px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--extraSmall"
|
||||
>
|
||||
<div
|
||||
className="euiTextAlign euiTextAlign--center"
|
||||
>
|
||||
<div
|
||||
className="euiTextColor euiTextColor--ghost"
|
||||
>
|
||||
⌥ + P
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots components/ToolTipShortcut with cmd 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#343741",
|
||||
"padding": "5px",
|
||||
"width": "100px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--extraSmall"
|
||||
>
|
||||
<div
|
||||
className="euiTextAlign euiTextAlign--center"
|
||||
>
|
||||
<div
|
||||
className="euiTextColor euiTextColor--ghost"
|
||||
>
|
||||
⌘ + D
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots components/ToolTipShortcut with down arrow 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#343741",
|
||||
"padding": "5px",
|
||||
"width": "100px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--extraSmall"
|
||||
>
|
||||
<div
|
||||
className="euiTextAlign euiTextAlign--center"
|
||||
>
|
||||
<div
|
||||
className="euiTextColor euiTextColor--ghost"
|
||||
>
|
||||
⌘ + SHIFT + ↓
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots components/ToolTipShortcut with left arrow 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#343741",
|
||||
"padding": "5px",
|
||||
"width": "100px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--extraSmall"
|
||||
>
|
||||
<div
|
||||
className="euiTextAlign euiTextAlign--center"
|
||||
>
|
||||
<div
|
||||
className="euiTextColor euiTextColor--ghost"
|
||||
>
|
||||
←
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots components/ToolTipShortcut with right arrow 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#343741",
|
||||
"padding": "5px",
|
||||
"width": "100px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--extraSmall"
|
||||
>
|
||||
<div
|
||||
className="euiTextAlign euiTextAlign--center"
|
||||
>
|
||||
<div
|
||||
className="euiTextColor euiTextColor--ghost"
|
||||
>
|
||||
→
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots components/ToolTipShortcut with shortcut 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#343741",
|
||||
"padding": "5px",
|
||||
"width": "100px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--extraSmall"
|
||||
>
|
||||
<div
|
||||
className="euiTextAlign euiTextAlign--center"
|
||||
>
|
||||
<div
|
||||
className="euiTextColor euiTextColor--ghost"
|
||||
>
|
||||
G
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots components/ToolTipShortcut with up arrow 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#343741",
|
||||
"padding": "5px",
|
||||
"width": "100px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--extraSmall"
|
||||
>
|
||||
<div
|
||||
className="euiTextAlign euiTextAlign--center"
|
||||
>
|
||||
<div
|
||||
className="euiTextColor euiTextColor--ghost"
|
||||
>
|
||||
⌘ + SHIFT + ↑
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 { storiesOf } from '@storybook/react';
|
||||
import React from 'react';
|
||||
import { ToolTipShortcut } from '../tool_tip_shortcut';
|
||||
|
||||
storiesOf('components/ToolTipShortcut', module)
|
||||
.addDecorator(story => (
|
||||
<div style={{ width: '100px', backgroundColor: '#343741', padding: '5px' }}>{story()}</div>
|
||||
))
|
||||
.add('with shortcut', () => <ToolTipShortcut shortcut="G" />)
|
||||
.add('with cmd', () => <ToolTipShortcut shortcut="⌘ + D" />)
|
||||
.add('with alt', () => <ToolTipShortcut shortcut="⌥ + P" />)
|
||||
.add('with left arrow', () => <ToolTipShortcut shortcut="←" />)
|
||||
.add('with right arrow', () => <ToolTipShortcut shortcut="→" />)
|
||||
.add('with up arrow', () => <ToolTipShortcut shortcut="⌘ + SHIFT + ↑" />)
|
||||
.add('with down arrow', () => <ToolTipShortcut shortcut="⌘ + SHIFT + ↓" />);
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 { compose, mapProps } from 'recompose';
|
||||
import { ToolTipShortcut as Component, Props as ComponentProps } from './tool_tip_shortcut';
|
||||
import { getClientPlatform } from '../../lib/get_client_platform';
|
||||
import { keymap } from '../../lib/keymap';
|
||||
import { getPrettyShortcut } from '../../lib/get_pretty_shortcut';
|
||||
|
||||
const os = getClientPlatform();
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* namespace defined in the keymap to look for shortcut in
|
||||
*/
|
||||
namespace: keyof typeof keymap;
|
||||
/**
|
||||
* key of the shortcut defined in the keymap
|
||||
*/
|
||||
action: string;
|
||||
}
|
||||
|
||||
export const ToolTipShortcut = compose<ComponentProps, Props>(
|
||||
mapProps(
|
||||
({ namespace, action }: Props): ComponentProps => {
|
||||
const shortcutMap = keymap[namespace][action];
|
||||
if (typeof shortcutMap === 'string') {
|
||||
return { shortcut: '' };
|
||||
}
|
||||
|
||||
const shortcuts = shortcutMap[os] || [];
|
||||
return { shortcut: getPrettyShortcut(shortcuts[0]) };
|
||||
}
|
||||
)
|
||||
)(Component);
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 { EuiText } from '@elastic/eui';
|
||||
import { FunctionComponent } from 'react';
|
||||
|
||||
export interface Props {
|
||||
/**
|
||||
* keyboard shortcut to display in a tooltip
|
||||
*/
|
||||
shortcut: string;
|
||||
}
|
||||
|
||||
export const ToolTipShortcut: FunctionComponent<Props> = ({ shortcut }) => (
|
||||
<EuiText size="xs" textAlign="center" color="ghost">
|
||||
{shortcut.replace(/\+/g, ' + ')}
|
||||
</EuiText>
|
||||
);
|
||||
|
||||
ToolTipShortcut.propTypes = {
|
||||
shortcut: PropTypes.string.isRequired,
|
||||
};
|
|
@ -7,9 +7,18 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
|
||||
import { ToolTipShortcut } from '../../tool_tip_shortcut';
|
||||
|
||||
export const RefreshControl = ({ doRefresh, inFlight }) => (
|
||||
<EuiToolTip position="bottom" content="Refresh data">
|
||||
<EuiToolTip
|
||||
position="bottom"
|
||||
content={
|
||||
<span>
|
||||
Refresh data
|
||||
<ToolTipShortcut namespace="EDITOR" action="REFRESH" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
disabled={inFlight}
|
||||
iconType="refresh"
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { AssetManager } from '../asset_manager';
|
||||
import { ElementTypes } from '../element_types';
|
||||
import { ToolTipShortcut } from '../tool_tip_shortcut/';
|
||||
import { ControlSettings } from './control_settings';
|
||||
import { RefreshControl } from './refresh_control';
|
||||
import { FullscreenControl } from './fullscreen_control';
|
||||
|
@ -33,7 +34,14 @@ export class WorkpadHeader extends React.PureComponent {
|
|||
state = { isModalVisible: false };
|
||||
|
||||
_fullscreenButton = ({ toggleFullscreen }) => (
|
||||
<EuiToolTip position="bottom" content="Enter fullscreen mode">
|
||||
<EuiToolTip
|
||||
position="bottom"
|
||||
content={
|
||||
<span>
|
||||
Enter fullscreen mode <ToolTipShortcut namespace="PRESENTATION" action="FULLSCREEN" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
iconType="fullScreen"
|
||||
aria-label="View fullscreen"
|
||||
|
@ -73,7 +81,12 @@ export class WorkpadHeader extends React.PureComponent {
|
|||
if (!this.props.canUserWrite) {
|
||||
return "You don't have permission to edit this workpad";
|
||||
} else {
|
||||
return this.props.isWriteable ? 'Hide editing controls' : 'Show editing controls';
|
||||
const content = this.props.isWriteable ? `Hide editing controls` : `Show editing controls`;
|
||||
return (
|
||||
<span>
|
||||
{content} <ToolTipShortcut namespace="EDITOR" action="EDITING" />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 { getPrettyShortcut } from '../get_pretty_shortcut';
|
||||
|
||||
describe('getPrettyShortcut', () => {
|
||||
test('uppercases shortcuts', () => {
|
||||
expect(getPrettyShortcut('g')).toBe('G');
|
||||
expect(getPrettyShortcut('shift+click')).toBe('SHIFT+CLICK');
|
||||
expect(getPrettyShortcut('backspace')).toBe('BACKSPACE');
|
||||
});
|
||||
test('preserves shortcut order', () => {
|
||||
expect(getPrettyShortcut('command+c')).toBe('⌘+C');
|
||||
expect(getPrettyShortcut('c+command')).toBe('C+⌘');
|
||||
});
|
||||
test(`replaces 'command' with ⌘`, () => {
|
||||
expect(getPrettyShortcut('command')).toBe('⌘');
|
||||
expect(getPrettyShortcut('command+c')).toBe('⌘+C');
|
||||
expect(getPrettyShortcut('command+shift+b')).toBe('⌘+SHIFT+B');
|
||||
});
|
||||
test(`replaces 'option' with ⌥`, () => {
|
||||
expect(getPrettyShortcut('option')).toBe('⌥');
|
||||
expect(getPrettyShortcut('option+f')).toBe('⌥+F');
|
||||
expect(getPrettyShortcut('option+shift+G')).toBe('⌥+SHIFT+G');
|
||||
expect(getPrettyShortcut('command+option+shift+G')).toBe('⌘+⌥+SHIFT+G');
|
||||
});
|
||||
test(`replaces 'left' with ←`, () => {
|
||||
expect(getPrettyShortcut('left')).toBe('←');
|
||||
expect(getPrettyShortcut('command+left')).toBe('⌘+←');
|
||||
expect(getPrettyShortcut('option+left')).toBe('⌥+←');
|
||||
expect(getPrettyShortcut('option+shift+left')).toBe('⌥+SHIFT+←');
|
||||
expect(getPrettyShortcut('command+shift+left')).toBe('⌘+SHIFT+←');
|
||||
expect(getPrettyShortcut('command+option+shift+left')).toBe('⌘+⌥+SHIFT+←');
|
||||
});
|
||||
test(`replaces 'right' with →`, () => {
|
||||
expect(getPrettyShortcut('right')).toBe('→');
|
||||
expect(getPrettyShortcut('command+right')).toBe('⌘+→');
|
||||
expect(getPrettyShortcut('option+right')).toBe('⌥+→');
|
||||
expect(getPrettyShortcut('option+shift+right')).toBe('⌥+SHIFT+→');
|
||||
expect(getPrettyShortcut('command+shift+right')).toBe('⌘+SHIFT+→');
|
||||
expect(getPrettyShortcut('command+option+shift+right')).toBe('⌘+⌥+SHIFT+→');
|
||||
});
|
||||
test(`replaces 'up' with ←`, () => {
|
||||
expect(getPrettyShortcut('up')).toBe('↑');
|
||||
expect(getPrettyShortcut('command+up')).toBe('⌘+↑');
|
||||
expect(getPrettyShortcut('option+up')).toBe('⌥+↑');
|
||||
expect(getPrettyShortcut('option+shift+up')).toBe('⌥+SHIFT+↑');
|
||||
expect(getPrettyShortcut('command+shift+up')).toBe('⌘+SHIFT+↑');
|
||||
expect(getPrettyShortcut('command+option+shift+up')).toBe('⌘+⌥+SHIFT+↑');
|
||||
});
|
||||
test(`replaces 'down' with ↓`, () => {
|
||||
expect(getPrettyShortcut('down')).toBe('↓');
|
||||
expect(getPrettyShortcut('command+down')).toBe('⌘+↓');
|
||||
expect(getPrettyShortcut('option+down')).toBe('⌥+↓');
|
||||
expect(getPrettyShortcut('option+shift+down')).toBe('⌥+SHIFT+↓');
|
||||
expect(getPrettyShortcut('command+shift+down')).toBe('⌘+SHIFT+↓');
|
||||
expect(getPrettyShortcut('command+option+shift+down')).toBe('⌘+⌥+SHIFT+↓');
|
||||
});
|
||||
});
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export const getClientPlatform = () => {
|
||||
export const getClientPlatform = (): 'osx' | 'windows' | 'linux' | 'other' => {
|
||||
const platform = navigator.platform.toLowerCase();
|
||||
if (platform.indexOf('mac') >= 0) {
|
||||
return 'osx';
|
21
x-pack/plugins/canvas/public/lib/get_pretty_shortcut.ts
Normal file
21
x-pack/plugins/canvas/public/lib/get_pretty_shortcut.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 getPrettyShortcut = (shortcut: string): string => {
|
||||
if (!shortcut) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let result = shortcut.toUpperCase();
|
||||
result = result.replace(/command/i, '⌘');
|
||||
result = result.replace(/option/i, '⌥');
|
||||
result = result.replace(/left/i, '←');
|
||||
result = result.replace(/right/i, '→');
|
||||
result = result.replace(/up/i, '↑');
|
||||
result = result.replace(/down/i, '↓');
|
||||
|
||||
return result;
|
||||
};
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* 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 { mapValues } from 'lodash';
|
||||
|
||||
// maps key for all OS's with optional modifiers
|
||||
const getShortcuts = (shortcuts, modifiers = []) => {
|
||||
// normalize shortcut values
|
||||
if (!Array.isArray(shortcuts)) {
|
||||
shortcuts = [shortcuts];
|
||||
}
|
||||
|
||||
// normalize modifier values
|
||||
if (!Array.isArray(modifiers)) {
|
||||
modifiers = [modifiers];
|
||||
}
|
||||
|
||||
let macShortcuts = shortcuts;
|
||||
|
||||
// handle shift modifier
|
||||
if (modifiers.includes('shift')) {
|
||||
macShortcuts = shortcuts.map(shortcut => `shift+${shortcut}`);
|
||||
shortcuts = shortcuts.map(shortcut => `shift+${shortcut}`);
|
||||
}
|
||||
|
||||
// handle alt modifier
|
||||
if (modifiers.includes('alt') || modifiers.includes('option')) {
|
||||
macShortcuts = shortcuts.map(shortcut => `option+${shortcut}`);
|
||||
shortcuts = shortcuts.map(shortcut => `alt+${shortcut}`);
|
||||
}
|
||||
|
||||
// handle ctrl modifier
|
||||
if (modifiers.includes('ctrl') || modifiers.includes('command')) {
|
||||
macShortcuts = shortcuts.map(shortcut => `command+${shortcut}`);
|
||||
shortcuts = shortcuts.map(shortcut => `ctrl+${shortcut}`);
|
||||
}
|
||||
|
||||
return {
|
||||
osx: macShortcuts,
|
||||
windows: shortcuts,
|
||||
linux: shortcuts,
|
||||
other: shortcuts,
|
||||
};
|
||||
};
|
||||
|
||||
const refreshShortcut = { ...getShortcuts('r', ['alt']), help: 'Refresh workpad' };
|
||||
const previousPageShortcut = { ...getShortcuts('[', ['alt']), help: 'Go to previous page' };
|
||||
const nextPageShortcut = { ...getShortcuts(']', ['alt']), help: 'Go to next page' };
|
||||
const deleteElementShortcuts = ['del', 'backspace'];
|
||||
const groupShortcut = ['g'];
|
||||
const ungroupShortcut = ['u'];
|
||||
const fullscreentExitShortcut = ['esc'];
|
||||
const fullscreenPageCycle = ['p'];
|
||||
|
||||
export const keymap = {
|
||||
ELEMENT: {
|
||||
displayName: 'Element controls',
|
||||
CUT: { ...getShortcuts('x', ['ctrl']), help: 'Cut' },
|
||||
COPY: { ...getShortcuts('c', ['ctrl']), help: 'Copy' },
|
||||
PASTE: { ...getShortcuts('v', ['ctrl']), help: 'Paste' },
|
||||
CLONE: { ...getShortcuts('d', ['ctrl']), help: 'Clone' },
|
||||
DELETE: {
|
||||
osx: deleteElementShortcuts,
|
||||
windows: deleteElementShortcuts,
|
||||
linux: deleteElementShortcuts,
|
||||
other: deleteElementShortcuts,
|
||||
help: 'Delete',
|
||||
},
|
||||
BRING_FORWARD: {
|
||||
...getShortcuts('up', ['ctrl']),
|
||||
help: 'Bring to front',
|
||||
},
|
||||
BRING_TO_FRONT: {
|
||||
...getShortcuts('up', ['ctrl', 'shift']),
|
||||
help: 'Bring forward',
|
||||
},
|
||||
SEND_BACKWARD: {
|
||||
...getShortcuts('down', ['ctrl']),
|
||||
help: 'Send backward',
|
||||
},
|
||||
SEND_TO_BACK: {
|
||||
...getShortcuts('down', ['ctrl', 'shift']),
|
||||
help: 'Send to back',
|
||||
},
|
||||
GROUP: {
|
||||
osx: groupShortcut,
|
||||
windows: groupShortcut,
|
||||
linux: groupShortcut,
|
||||
other: groupShortcut,
|
||||
help: 'Group',
|
||||
},
|
||||
UNGROUP: {
|
||||
osx: ungroupShortcut,
|
||||
windows: ungroupShortcut,
|
||||
linux: ungroupShortcut,
|
||||
other: ungroupShortcut,
|
||||
help: 'Ungroup',
|
||||
},
|
||||
},
|
||||
EDITOR: {
|
||||
displayName: 'Editor controls',
|
||||
UNDO: { ...getShortcuts('z', ['ctrl']), help: 'Undo last action' },
|
||||
REDO: { ...getShortcuts('z', ['ctrl', 'shift']), help: 'Redo last action' },
|
||||
PREV: previousPageShortcut,
|
||||
NEXT: nextPageShortcut,
|
||||
EDITING: { ...getShortcuts('e', ['alt']), help: 'Toggle edit mode' },
|
||||
GRID: { ...getShortcuts('g', ['alt']), help: 'Show grid' },
|
||||
REFRESH: refreshShortcut,
|
||||
},
|
||||
PRESENTATION: {
|
||||
displayName: 'Presentation controls',
|
||||
FULLSCREEN: { ...getShortcuts(['p', 'f'], ['alt']), help: 'Enter presentation mode' },
|
||||
FULLSCREEN_EXIT: { ...getShortcuts(fullscreentExitShortcut), 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,
|
||||
PAGE_CYCLE_TOGGLE: { ...getShortcuts(fullscreenPageCycle), help: 'Toggle page cycling' },
|
||||
},
|
||||
EXPRESSION: {
|
||||
displayName: 'Expression controls',
|
||||
RUN: { ...getShortcuts('enter', ['ctrl']), help: 'Run whole expression' },
|
||||
},
|
||||
};
|
136
x-pack/plugins/canvas/public/lib/keymap.ts
Normal file
136
x-pack/plugins/canvas/public/lib/keymap.ts
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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 { mapValues } from 'lodash';
|
||||
|
||||
export interface ShortcutMap {
|
||||
osx: string[];
|
||||
windows: string[];
|
||||
linux: string[];
|
||||
other: string[];
|
||||
help: string;
|
||||
}
|
||||
|
||||
export interface ShortcutNameSpace {
|
||||
displayName: string;
|
||||
[shortcut: string]: string | ShortcutMap;
|
||||
}
|
||||
|
||||
interface KeyMap {
|
||||
[category: string]: ShortcutNameSpace;
|
||||
}
|
||||
type Modifier = 'ctrl' | 'command' | 'shift' | 'alt' | 'option';
|
||||
|
||||
// maps key for all OS's with optional modifiers
|
||||
const getShortcuts = (
|
||||
shortcuts: string | string[],
|
||||
{ modifiers = [], help }: { modifiers?: Modifier | Modifier[]; help: string }
|
||||
): ShortcutMap => {
|
||||
// normalize shortcut values
|
||||
if (!Array.isArray(shortcuts)) {
|
||||
shortcuts = [shortcuts];
|
||||
}
|
||||
|
||||
// normalize modifier values
|
||||
if (!Array.isArray(modifiers)) {
|
||||
modifiers = [modifiers];
|
||||
}
|
||||
|
||||
let macShortcuts = shortcuts;
|
||||
|
||||
// handle shift modifier
|
||||
if (modifiers.includes('shift')) {
|
||||
macShortcuts = shortcuts.map(shortcut => `shift+${shortcut}`);
|
||||
shortcuts = shortcuts.map(shortcut => `shift+${shortcut}`);
|
||||
}
|
||||
|
||||
// handle alt modifier
|
||||
if (modifiers.includes('alt') || modifiers.includes('option')) {
|
||||
macShortcuts = shortcuts.map(shortcut => `option+${shortcut}`);
|
||||
shortcuts = shortcuts.map(shortcut => `alt+${shortcut}`);
|
||||
}
|
||||
|
||||
// handle ctrl modifier
|
||||
if (modifiers.includes('ctrl') || modifiers.includes('command')) {
|
||||
macShortcuts = shortcuts.map(shortcut => `command+${shortcut}`);
|
||||
shortcuts = shortcuts.map(shortcut => `ctrl+${shortcut}`);
|
||||
}
|
||||
|
||||
return {
|
||||
osx: macShortcuts,
|
||||
windows: shortcuts,
|
||||
linux: shortcuts,
|
||||
other: shortcuts,
|
||||
help,
|
||||
};
|
||||
};
|
||||
|
||||
const refreshShortcut = getShortcuts('r', { modifiers: 'alt', help: 'Refresh workpad' });
|
||||
const previousPageShortcut = getShortcuts('[', { modifiers: 'alt', help: 'Go to previous page' });
|
||||
const nextPageShortcut = getShortcuts(']', { modifiers: 'alt', help: 'Go to next page' });
|
||||
|
||||
export const keymap: KeyMap = {
|
||||
ELEMENT: {
|
||||
displayName: 'Element controls',
|
||||
CUT: getShortcuts('x', { modifiers: 'ctrl', help: 'Cut' }),
|
||||
COPY: getShortcuts('c', { modifiers: 'ctrl', help: 'Copy' }),
|
||||
PASTE: getShortcuts('v', { modifiers: 'ctrl', help: 'Paste' }),
|
||||
CLONE: getShortcuts('d', { modifiers: 'ctrl', help: 'Clone' }),
|
||||
DELETE: getShortcuts(['del', 'backspace'], { help: 'Delete' }),
|
||||
BRING_FORWARD: getShortcuts('up', { modifiers: 'ctrl', help: 'Bring to front' }),
|
||||
BRING_TO_FRONT: getShortcuts('up', { modifiers: ['ctrl', 'shift'], help: 'Bring forward' }),
|
||||
SEND_BACKWARD: getShortcuts('down', { modifiers: 'ctrl', help: 'Send backward' }),
|
||||
SEND_TO_BACK: getShortcuts('down', { modifiers: ['ctrl', 'shift'], help: 'Send to back' }),
|
||||
GROUP: getShortcuts('g', { help: 'Group' }),
|
||||
UNGROUP: getShortcuts('u', { help: 'Ungroup' }),
|
||||
},
|
||||
EXPRESSION: {
|
||||
displayName: 'Expression controls',
|
||||
RUN: getShortcuts('enter', { modifiers: 'ctrl', help: 'Run whole expression' }),
|
||||
},
|
||||
EDITOR: {
|
||||
displayName: 'Editor controls',
|
||||
// added for documentation purposes, not handled by `react-shortcuts`
|
||||
MULTISELECT: getShortcuts('click', { modifiers: 'shift', help: 'Select multiple elements' }),
|
||||
// added for documentation purposes, not handled by `react-shortcuts`
|
||||
RESIZE_FROM_CENTER: getShortcuts('drag', {
|
||||
modifiers: 'alt',
|
||||
help: 'Resize from center',
|
||||
}),
|
||||
// added for documentation purposes, not handled by `react-shortcuts`
|
||||
IGNORE_SNAP: getShortcuts('drag', {
|
||||
modifiers: 'ctrl',
|
||||
help: 'Move, resize, and rotate without snapping',
|
||||
}),
|
||||
// added for documentation purposes, not handled by `react-shortcuts`
|
||||
SELECT_BEHIND: getShortcuts('click', {
|
||||
modifiers: 'ctrl',
|
||||
help: 'Select element below',
|
||||
}),
|
||||
UNDO: getShortcuts('z', { modifiers: 'ctrl', help: 'Undo last action' }),
|
||||
REDO: getShortcuts('z', { modifiers: ['ctrl', 'shift'], help: 'Redo last action' }),
|
||||
PREV: previousPageShortcut,
|
||||
NEXT: nextPageShortcut,
|
||||
EDITING: getShortcuts('e', { modifiers: 'alt', help: 'Toggle edit mode' }),
|
||||
GRID: getShortcuts('g', { modifiers: 'alt', help: 'Show grid' }),
|
||||
REFRESH: refreshShortcut,
|
||||
},
|
||||
PRESENTATION: {
|
||||
displayName: 'Presentation controls',
|
||||
FULLSCREEN: getShortcuts(['f', 'p'], { modifiers: 'alt', help: 'Enter presentation mode' }),
|
||||
FULLSCREEN_EXIT: getShortcuts('esc', { help: 'Exit presentation mode' }),
|
||||
PREV: mapValues(previousPageShortcut, (osShortcuts: string[], key?: string) =>
|
||||
// adds 'backspace' and 'left' to list of shortcuts per OS
|
||||
key === 'help' ? osShortcuts : osShortcuts.concat(['backspace', 'left'])
|
||||
),
|
||||
NEXT: mapValues(nextPageShortcut, (osShortcuts: string[], key?: string) =>
|
||||
// adds 'space' and 'right' to list of shortcuts per OS
|
||||
key === 'help' ? osShortcuts : osShortcuts.concat(['space', 'right'])
|
||||
),
|
||||
REFRESH: refreshShortcut,
|
||||
PAGE_CYCLE_TOGGLE: getShortcuts('p', { help: 'Toggle page cycling' }),
|
||||
},
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue