diff --git a/x-pack/legacy/plugins/canvas/i18n/components.ts b/x-pack/legacy/plugins/canvas/i18n/components.ts
index 7bd16c4933ce1..1736b98663315 100644
--- a/x-pack/legacy/plugins/canvas/i18n/components.ts
+++ b/x-pack/legacy/plugins/canvas/i18n/components.ts
@@ -804,17 +804,6 @@ export const ComponentStrings = {
}),
},
SidebarHeader: {
- getAlignmentMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.alignmentMenuItemLabel', {
- defaultMessage: 'Alignment',
- description:
- 'This refers to the vertical (i.e. left, center, right) and horizontal (i.e. top, middle, bottom) ' +
- 'alignment options of the selected elements',
- }),
- getBottomAlignMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.bottomAlignMenuItemLabel', {
- defaultMessage: 'Bottom',
- }),
getBringForwardAriaLabel: () =>
i18n.translate('xpack.canvas.sidebarHeader.bringForwardArialLabel', {
defaultMessage: 'Move element up one layer',
@@ -823,56 +812,6 @@ export const ComponentStrings = {
i18n.translate('xpack.canvas.sidebarHeader.bringToFrontArialLabel', {
defaultMessage: 'Move element to top layer',
}),
- getCenterAlignMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.centerAlignMenuItemLabel', {
- defaultMessage: 'Center',
- description: 'This refers to alignment centered horizontally.',
- }),
- getContextMenuTitle: () =>
- i18n.translate('xpack.canvas.sidebarHeader.contextMenuAriaLabel', {
- defaultMessage: 'Element options',
- }),
- getCreateElementModalTitle: () =>
- i18n.translate('xpack.canvas.sidebarHeader.createElementModalTitle', {
- defaultMessage: 'Create new element',
- }),
- getDistributionMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.distributionMenutItemLabel', {
- defaultMessage: 'Distribution',
- description:
- 'This refers to the options to evenly spacing the selected elements horizontall or vertically.',
- }),
- getGroupMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.groupMenuItemLabel', {
- defaultMessage: 'Group',
- description: 'This refers to grouping multiple selected elements.',
- }),
- getHorizontalDistributionMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.horizontalDistributionMenutItemLabel', {
- defaultMessage: 'Horizontal',
- }),
- getLeftAlignMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.leftAlignMenuItemLabel', {
- defaultMessage: 'Left',
- }),
- getMiddleAlignMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.middleAlignMenuItemLabel', {
- defaultMessage: 'Middle',
- description: 'This refers to alignment centered vertically.',
- }),
- getOrderMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.orderMenuItemLabel', {
- defaultMessage: 'Order',
- description: 'Refers to the order of the elements displayed on the page from front to back',
- }),
- getRightAlignMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.rightAlignMenuItemLabel', {
- defaultMessage: 'Right',
- }),
- getSaveElementMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.savedElementMenuItemLabel', {
- defaultMessage: 'Save as new element',
- }),
getSendBackwardAriaLabel: () =>
i18n.translate('xpack.canvas.sidebarHeader.sendBackwardArialLabel', {
defaultMessage: 'Move element down one layer',
@@ -881,19 +820,6 @@ export const ComponentStrings = {
i18n.translate('xpack.canvas.sidebarHeader.sendToBackArialLabel', {
defaultMessage: 'Move element to bottom layer',
}),
- getTopAlignMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.topAlignMenuItemLabel', {
- defaultMessage: 'Top',
- }),
- getUngroupMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.ungroupMenuItemLabel', {
- defaultMessage: 'Ungroup',
- description: 'This refers to ungrouping a grouped element',
- }),
- getVerticalDistributionMenuItemLabel: () =>
- i18n.translate('xpack.canvas.sidebarHeader.verticalDistributionMenutItemLabel', {
- defaultMessage: 'Vertical',
- }),
},
TextStylePicker: {
getAlignCenterOption: () =>
@@ -1105,6 +1031,94 @@ export const ComponentStrings = {
defaultMessage: 'Set a custom interval',
}),
},
+ WorkpadHeaderEditMenu: {
+ getAlignmentMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.alignmentMenuItemLabel', {
+ defaultMessage: 'Alignment',
+ description:
+ 'This refers to the vertical (i.e. left, center, right) and horizontal (i.e. top, middle, bottom) ' +
+ 'alignment options of the selected elements',
+ }),
+ getBottomAlignMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.bottomAlignMenuItemLabel', {
+ defaultMessage: 'Bottom',
+ }),
+ getCenterAlignMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.centerAlignMenuItemLabel', {
+ defaultMessage: 'Center',
+ description: 'This refers to alignment centered horizontally.',
+ }),
+ getCreateElementModalTitle: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.createElementModalTitle', {
+ defaultMessage: 'Create new element',
+ }),
+ getDistributionMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.distributionMenutItemLabel', {
+ defaultMessage: 'Distribution',
+ description:
+ 'This refers to the options to evenly spacing the selected elements horizontall or vertically.',
+ }),
+ getEditMenuButtonLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.editMenuButtonLabel', {
+ defaultMessage: 'Edit',
+ }),
+ getEditMenuLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.editMenuLabel', {
+ defaultMessage: 'Edit options',
+ }),
+ getGroupMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.groupMenuItemLabel', {
+ defaultMessage: 'Group',
+ description: 'This refers to grouping multiple selected elements.',
+ }),
+ getHorizontalDistributionMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.horizontalDistributionMenutItemLabel', {
+ defaultMessage: 'Horizontal',
+ }),
+ getLeftAlignMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.leftAlignMenuItemLabel', {
+ defaultMessage: 'Left',
+ }),
+ getMiddleAlignMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.middleAlignMenuItemLabel', {
+ defaultMessage: 'Middle',
+ description: 'This refers to alignment centered vertically.',
+ }),
+ getOrderMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.orderMenuItemLabel', {
+ defaultMessage: 'Order',
+ description: 'Refers to the order of the elements displayed on the page from front to back',
+ }),
+ getRedoMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.redoMenuItemLabel', {
+ defaultMessage: 'Redo',
+ }),
+ getRightAlignMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.rightAlignMenuItemLabel', {
+ defaultMessage: 'Right',
+ }),
+ getSaveElementMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.savedElementMenuItemLabel', {
+ defaultMessage: 'Save as new element',
+ }),
+ getTopAlignMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.topAlignMenuItemLabel', {
+ defaultMessage: 'Top',
+ }),
+ getUndoMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.undoMenuItemLabel', {
+ defaultMessage: 'Undo',
+ }),
+ getUngroupMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.ungroupMenuItemLabel', {
+ defaultMessage: 'Ungroup',
+ description: 'This refers to ungrouping a grouped element',
+ }),
+ getVerticalDistributionMenuItemLabel: () =>
+ i18n.translate('xpack.canvas.workpadHeaderEditMenu.verticalDistributionMenutItemLabel', {
+ defaultMessage: 'Vertical',
+ }),
+ },
WorkpadHeaderElementMenu: {
getAssetsMenuItemLabel: () =>
i18n.translate('xpack.canvas.workpadHeaderElementMenu.manageAssetsMenuItemLabel', {
diff --git a/x-pack/legacy/plugins/canvas/i18n/shortcuts.ts b/x-pack/legacy/plugins/canvas/i18n/shortcuts.ts
index 32b07a45c17db..124d70ff3095f 100644
--- a/x-pack/legacy/plugins/canvas/i18n/shortcuts.ts
+++ b/x-pack/legacy/plugins/canvas/i18n/shortcuts.ts
@@ -42,10 +42,10 @@ export const ShortcutStrings = {
defaultMessage: 'Delete',
}),
BRING_FORWARD: i18n.translate('xpack.canvas.keyboardShortcuts.bringFowardShortcutHelpText', {
- defaultMessage: 'Bring to front',
+ defaultMessage: 'Bring forward',
}),
BRING_TO_FRONT: i18n.translate('xpack.canvas.keyboardShortcuts.bringToFrontShortcutHelpText', {
- defaultMessage: 'Bring forward',
+ defaultMessage: 'Bring to front',
}),
SEND_BACKWARD: i18n.translate('xpack.canvas.keyboardShortcuts.sendBackwardShortcutHelpText', {
defaultMessage: 'Send backward',
diff --git a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js
index b8e1bece6ac30..fc3ac9922355a 100644
--- a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js
+++ b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js
@@ -43,7 +43,7 @@ export class WorkpadApp extends React.PureComponent {
-
+ {})} />
- {})} />
+
)}
diff --git a/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.stories.storyshot b/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.stories.storyshot
index 503677687ba12..d59d03578a363 100644
--- a/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.stories.storyshot
+++ b/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.stories.storyshot
@@ -212,7 +212,7 @@ exports[`Storyshots components/KeyboardShortcutsDoc default 1`] = `
- Bring forward
+ Bring to front
- Bring to front
+ Bring forward
({
selectedToplevelNodes: getSelectedToplevelNodes(state),
selectedElementId: getSelectedElementId(state),
- state,
});
-const mergeProps = (
- { state, ...restStateProps },
- { dispatch, ...restDispatchProps },
- ownProps
-) => ({
- ...ownProps,
- ...restDispatchProps,
- ...restStateProps,
- updateGlobalState: globalStateUpdater(dispatch, state),
-});
-
-const withGlobalState = (commit, updateGlobalState) => (type, payload) => {
- const newLayoutState = commit(type, payload);
- if (newLayoutState.currentScene.gestureEnd) {
- updateGlobalState(newLayoutState);
- }
-};
-
-const MultiElementSidebar = ({ commit, updateGlobalState }) => (
+const MultiElementSidebar = () => (
-
+
);
-const GroupedElementSidebar = ({ commit, updateGlobalState }) => (
+const GroupedElementSidebar = () => (
-
+
@@ -92,7 +65,4 @@ const branches = [
),
];
-export const SidebarContent = compose(
- connect(mapStateToProps, null, mergeProps),
- ...branches
-)(GlobalConfig);
+export const SidebarContent = compose(connect(mapStateToProps), ...branches)(GlobalConfig);
diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot
index ac25cbe0b6832..4d5b9570ee20f 100644
--- a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot
+++ b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot
@@ -20,80 +20,6 @@ exports[`Storyshots components/Sidebar/SidebarHeader default 1`] = `
Selected layer
-
`;
@@ -224,72 +150,6 @@ exports[`Storyshots components/Sidebar/SidebarHeader with layer controls 1`] = `
-
-
-
-
diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/sidebar_header.stories.tsx b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/sidebar_header.stories.tsx
index 9baff380fc3eb..11c66906a6ef6 100644
--- a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/sidebar_header.stories.tsx
+++ b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/sidebar_header.stories.tsx
@@ -10,26 +10,10 @@ import { action } from '@storybook/addon-actions';
import { SidebarHeader } from '../sidebar_header';
const handlers = {
- cloneNodes: action('cloneNodes'),
- copyNodes: action('copyNodes'),
- cutNodes: action('cutNodes'),
- pasteNodes: action('pasteNodes'),
- deleteNodes: action('deleteNodes'),
bringToFront: action('bringToFront'),
bringForward: action('bringForward'),
sendBackward: action('sendBackward'),
sendToBack: action('sendToBack'),
- createCustomElement: action('createCustomElement'),
- groupNodes: action('groupNodes'),
- ungroupNodes: action('ungroupNodes'),
- alignLeft: action('alignLeft'),
- alignMiddle: action('alignMiddle'),
- alignRight: action('alignRight'),
- alignTop: action('alignTop'),
- alignCenter: action('alignCenter'),
- alignBottom: action('alignBottom'),
- distributeHorizontally: action('distributeHorizontally'),
- distributeVertically: action('distributeVertically'),
};
storiesOf('components/Sidebar/SidebarHeader', module)
diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx
index 925e68a565f04..024a2dbb41a24 100644
--- a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx
+++ b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx
@@ -4,25 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { Component, Fragment } from 'react';
+import React, { FunctionComponent } from 'react';
import PropTypes from 'prop-types';
-import {
- EuiFlexGroup,
- EuiFlexItem,
- EuiTitle,
- EuiButtonIcon,
- EuiContextMenu,
- EuiToolTip,
- EuiContextMenuPanelItemDescriptor,
- EuiContextMenuPanelDescriptor,
- EuiOverlayMask,
-} from '@elastic/eui';
-import { Popover } from '../popover';
-import { CustomElementModal } from '../custom_element_modal';
+import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import { ToolTipShortcut } from '../tool_tip_shortcut/';
import { ComponentStrings } from '../../../i18n/components';
import { ShortcutStrings } from '../../../i18n/shortcuts';
-import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../common/lib/constants';
const { SidebarHeader: strings } = ComponentStrings;
const shortcutHelp = ShortcutStrings.getShortcutHelp();
@@ -36,26 +23,6 @@ interface Props {
* indicated whether or not layer controls should be displayed
*/
showLayerControls?: boolean;
- /**
- * cuts selected elements
- */
- cutNodes: () => void;
- /**
- * copies selected elements to clipboard
- */
- copyNodes: () => void;
- /**
- * pastes elements stored in clipboard to page
- */
- pasteNodes: () => void;
- /**
- * clones selected elements
- */
- cloneNodes: () => void;
- /**
- * deletes selected elements
- */
- deleteNodes: () => void;
/**
* moves selected element to top layer
*/
@@ -72,493 +39,117 @@ interface Props {
* moves selected element to bottom layer
*/
sendToBack: () => void;
- /**
- * saves the selected elements as an custom-element saved object
- */
- createCustomElement: (name: string, description: string, image: string) => void;
- /**
- * indicated whether the selected element is a group or not
- */
- groupIsSelected: boolean;
- /**
- * only more than one selected element can be grouped
- */
- selectedNodes: string[];
- /**
- * groups selected elements
- */
- groupNodes: () => void;
- /**
- * ungroups selected group
- */
- ungroupNodes: () => void;
- /**
- * left align selected elements
- */
- alignLeft: () => void;
- /**
- * center align selected elements
- */
- alignCenter: () => void;
- /**
- * right align selected elements
- */
- alignRight: () => void;
- /**
- * top align selected elements
- */
- alignTop: () => void;
- /**
- * middle align selected elements
- */
- alignMiddle: () => void;
- /**
- * bottom align selected elements
- */
- alignBottom: () => void;
- /**
- * horizontally distribute selected elements
- */
- distributeHorizontally: () => void;
- /**
- * vertically distribute selected elements
- */
- distributeVertically: () => void;
-}
-
-interface State {
- /**
- * indicates whether or not the custom element modal is open
- */
- isModalVisible: boolean;
-}
-
-interface MenuTuple {
- menuItem: EuiContextMenuPanelItemDescriptor;
- panel: EuiContextMenuPanelDescriptor;
}
-const contextMenuButton = (handleClick: React.MouseEventHandler) => (
-
-);
-
-export class SidebarHeader extends Component {
- public static propTypes = {
- title: PropTypes.string.isRequired,
- showLayerControls: PropTypes.bool, // TODO: remove when we support relayering multiple elements
- cutNodes: PropTypes.func.isRequired,
- copyNodes: PropTypes.func.isRequired,
- pasteNodes: PropTypes.func.isRequired,
- cloneNodes: PropTypes.func.isRequired,
- deleteNodes: PropTypes.func.isRequired,
- bringToFront: PropTypes.func.isRequired,
- bringForward: PropTypes.func.isRequired,
- sendBackward: PropTypes.func.isRequired,
- sendToBack: PropTypes.func.isRequired,
- createCustomElement: PropTypes.func.isRequired,
- groupIsSelected: PropTypes.bool,
- selectedNodes: PropTypes.array,
- groupNodes: PropTypes.func.isRequired,
- ungroupNodes: PropTypes.func.isRequired,
- alignLeft: PropTypes.func.isRequired,
- alignCenter: PropTypes.func.isRequired,
- alignRight: PropTypes.func.isRequired,
- alignTop: PropTypes.func.isRequired,
- alignMiddle: PropTypes.func.isRequired,
- alignBottom: PropTypes.func.isRequired,
- distributeHorizontally: PropTypes.func.isRequired,
- distributeVertically: PropTypes.func.isRequired,
- };
-
- public static defaultProps = {
- groupIsSelected: false,
- showLayerControls: false,
- selectedNodes: [],
- };
-
- public state = {
- isModalVisible: false,
- };
-
- private _isMounted = false;
- private _showModal = () => this._isMounted && this.setState({ isModalVisible: true });
- private _hideModal = () => this._isMounted && this.setState({ isModalVisible: false });
-
- public componentDidMount() {
- this._isMounted = true;
- }
-
- public componentWillUnmount() {
- this._isMounted = false;
- }
-
- private _renderLayoutControls = () => {
- const { bringToFront, bringForward, sendBackward, sendToBack } = this.props;
- return (
-
-
-
- {shortcutHelp.BRING_TO_FRONT}
-
-
- }
- >
-
-
-
-
-
- {shortcutHelp.BRING_FORWARD}
-
-
- }
- >
-
-
-
-
-
- {shortcutHelp.SEND_BACKWARD}
-
-
- }
- >
-
-
-
-
-
- {shortcutHelp.SEND_TO_BACK}
-
-
- }
- >
-
-
-
-
- );
- };
-
- private _getLayerMenuItems = (): MenuTuple => {
- const { bringToFront, bringForward, sendBackward, sendToBack } = this.props;
-
- return {
- menuItem: {
- name: strings.getOrderMenuItemLabel(),
- className: CONTEXT_MENU_TOP_BORDER_CLASSNAME,
- panel: 1,
- },
- panel: {
- id: 1,
- title: strings.getOrderMenuItemLabel(),
- items: [
- {
- name: shortcutHelp.BRING_TO_FRONT, // TODO: check against current element position and disable if already top layer
- icon: 'sortUp',
- onClick: bringToFront,
- },
- {
- name: shortcutHelp.BRING_TO_FRONT, // TODO: same as above
- icon: 'arrowUp',
- onClick: bringForward,
- },
- {
- name: shortcutHelp.SEND_BACKWARD, // TODO: check against current element position and disable if already bottom layer
- icon: 'arrowDown',
- onClick: sendBackward,
- },
- {
- name: shortcutHelp.SEND_TO_BACK, // TODO: same as above
- icon: 'sortDown',
- onClick: sendToBack,
- },
- ],
- },
- };
- };
-
- private _getAlignmentMenuItems = (close: (fn: () => void) => () => void): MenuTuple => {
- const { alignLeft, alignCenter, alignRight, alignTop, alignMiddle, alignBottom } = this.props;
-
- return {
- menuItem: {
- name: strings.getAlignmentMenuItemLabel(),
- className: 'canvasContextMenu',
- panel: 2,
- },
- panel: {
- id: 2,
- title: strings.getAlignmentMenuItemLabel(),
- items: [
- {
- name: strings.getLeftAlignMenuItemLabel(),
- icon: 'editorItemAlignLeft',
- onClick: close(alignLeft),
- },
- {
- name: strings.getCenterAlignMenuItemLabel(),
- icon: 'editorItemAlignCenter',
- onClick: close(alignCenter),
- },
- {
- name: strings.getRightAlignMenuItemLabel(),
- icon: 'editorItemAlignRight',
- onClick: close(alignRight),
- },
- {
- name: strings.getTopAlignMenuItemLabel(),
- icon: 'editorItemAlignTop',
- onClick: close(alignTop),
- },
- {
- name: strings.getMiddleAlignMenuItemLabel(),
- icon: 'editorItemAlignMiddle',
- onClick: close(alignMiddle),
- },
- {
- name: strings.getBottomAlignMenuItemLabel(),
- icon: 'editorItemAlignBottom',
- onClick: close(alignBottom),
- },
- ],
- },
- };
- };
-
- private _getDistributionMenuItems = (close: (fn: () => void) => () => void): MenuTuple => {
- const { distributeHorizontally, distributeVertically } = this.props;
-
- return {
- menuItem: {
- name: strings.getDistributionMenuItemLabel(),
- className: 'canvasContextMenu',
- panel: 3,
- },
- panel: {
- id: 3,
- title: strings.getDistributionMenuItemLabel(),
- items: [
- {
- name: strings.getHorizontalDistributionMenuItemLabel(),
- icon: 'editorDistributeHorizontal',
- onClick: close(distributeHorizontally),
- },
- {
- name: strings.getVerticalDistributionMenuItemLabel(),
- icon: 'editorDistributeVertical',
- onClick: close(distributeVertically),
- },
- ],
- },
- };
- };
-
- private _getGroupMenuItems = (
- close: (fn: () => void) => () => void
- ): EuiContextMenuPanelItemDescriptor[] => {
- const { groupIsSelected, ungroupNodes, groupNodes, selectedNodes } = this.props;
- return groupIsSelected
- ? [
- {
- name: strings.getUngroupMenuItemLabel(),
- className: CONTEXT_MENU_TOP_BORDER_CLASSNAME,
- onClick: close(ungroupNodes),
- },
- ]
- : selectedNodes.length > 1
- ? [
- {
- name: strings.getGroupMenuItemLabel(),
- className: CONTEXT_MENU_TOP_BORDER_CLASSNAME,
- onClick: close(groupNodes),
- },
- ]
- : [];
- };
-
- private _getPanels = (closePopover: () => void): EuiContextMenuPanelDescriptor[] => {
- const {
- showLayerControls,
- cutNodes,
- copyNodes,
- pasteNodes,
- deleteNodes,
- cloneNodes,
- } = this.props;
-
- // closes popover after invoking fn
- const close = (fn: () => void) => () => {
- fn();
- closePopover();
- };
-
- const items: EuiContextMenuPanelItemDescriptor[] = [
- {
- name: shortcutHelp.CUT,
- icon: 'cut',
- onClick: close(cutNodes),
- },
- {
- name: shortcutHelp.COPY,
- icon: 'copy',
- onClick: copyNodes,
- },
- {
- name: shortcutHelp.PASTE, // TODO: can this be disabled if clipboard is empty?
- icon: 'copyClipboard',
- onClick: close(pasteNodes),
- },
- {
- name: shortcutHelp.DELETE,
- icon: 'trash',
- onClick: close(deleteNodes),
- },
- {
- name: shortcutHelp.CLONE,
- onClick: close(cloneNodes),
- },
- ...this._getGroupMenuItems(close),
- ];
-
- const panels: EuiContextMenuPanelDescriptor[] = [
- {
- id: 0,
- title: strings.getContextMenuTitle(),
- items,
- },
- ];
-
- const fillMenu = ({ menuItem, panel }: MenuTuple) => {
- items.push(menuItem); // add Order menu item to first panel
- panels.push(panel); // add nested panel for layers controls
- };
-
- if (showLayerControls) {
- fillMenu(this._getLayerMenuItems());
- }
-
- if (this.props.selectedNodes.length > 1) {
- fillMenu(this._getAlignmentMenuItems(close));
- }
-
- if (this.props.selectedNodes.length > 2) {
- fillMenu(this._getDistributionMenuItems(close));
- }
-
- items.push({
- name: strings.getSaveElementMenuItemLabel(),
- icon: 'indexOpen',
- className: CONTEXT_MENU_TOP_BORDER_CLASSNAME,
- onClick: this._showModal,
- });
-
- return panels;
- };
-
- private _renderContextMenu = () => (
-
- );
-
- private _handleSave = (name: string, description: string, image: string) => {
- const { createCustomElement } = this.props;
- createCustomElement(name, description, image);
- this._hideModal();
- };
-
- render() {
- const { title, showLayerControls } = this.props;
- const { isModalVisible } = this.state;
-
- return (
-
-
+export const SidebarHeader: FunctionComponent = ({
+ title,
+ showLayerControls,
+ bringToFront,
+ bringForward,
+ sendBackward,
+ sendToBack,
+}) => (
+
+
+
+ {title}
+
+
+ {showLayerControls ? (
+
+
-
- {title}
-
+
+ {shortcutHelp.BRING_TO_FRONT}
+
+
+ }
+ >
+
+
-
- {showLayerControls ? this._renderLayoutControls() : null}
-
-
-
-
-
- {this._renderContextMenu()}
-
+
+ {shortcutHelp.BRING_FORWARD}
+
+
+ }
+ >
+
+
+
+
+
+ {shortcutHelp.SEND_BACKWARD}
+
+
+ }
+ >
+
+
+
+
+
+ {shortcutHelp.SEND_TO_BACK}
+
+
+ }
+ >
+
+
- {isModalVisible ? (
-
-
-
- ) : null}
-
- );
- }
-}
+
+ ) : null}
+
+);
+
+SidebarHeader.propTypes = {
+ title: PropTypes.string.isRequired,
+ showLayerControls: PropTypes.bool, // TODO: remove when we support relayering multiple elements
+ bringToFront: PropTypes.func.isRequired,
+ bringForward: PropTypes.func.isRequired,
+ sendBackward: PropTypes.func.isRequired,
+ sendToBack: PropTypes.func.isRequired,
+};
+
+SidebarHeader.defaultProps = {
+ showLayerControls: false,
+};
diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/__snapshots__/edit_menu.examples.storyshot b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/__snapshots__/edit_menu.examples.storyshot
new file mode 100644
index 0000000000000..a26a13c1b6d9d
--- /dev/null
+++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/__snapshots__/edit_menu.examples.storyshot
@@ -0,0 +1,199 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Storyshots components/WorkpadHeader/EditMenu 2 elements selected 1`] = `
+
+`;
+
+exports[`Storyshots components/WorkpadHeader/EditMenu 3+ elements selected 1`] = `
+
+`;
+
+exports[`Storyshots components/WorkpadHeader/EditMenu clipboard data exists 1`] = `
+
+`;
+
+exports[`Storyshots components/WorkpadHeader/EditMenu default 1`] = `
+
+`;
+
+exports[`Storyshots components/WorkpadHeader/EditMenu single element selected 1`] = `
+
+`;
+
+exports[`Storyshots components/WorkpadHeader/EditMenu single grouped element selected 1`] = `
+
+`;
diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/edit_menu.examples.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/edit_menu.examples.tsx
new file mode 100644
index 0000000000000..a0ab8d53485f5
--- /dev/null
+++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/edit_menu.examples.tsx
@@ -0,0 +1,69 @@
+/*
+ * 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 { action } from '@storybook/addon-actions';
+import React from 'react';
+import { EditMenu } from '../edit_menu';
+
+const handlers = {
+ cutNodes: action('cutNodes'),
+ copyNodes: action('copyNodes'),
+ pasteNodes: action('pasteNodes'),
+ deleteNodes: action('deleteNodes'),
+ cloneNodes: action('cloneNodes'),
+ bringToFront: action('bringToFront'),
+ bringForward: action('bringForward'),
+ sendBackward: action('sendBackward'),
+ sendToBack: action('sendToBack'),
+ alignLeft: action('alignLeft'),
+ alignCenter: action('alignCenter'),
+ alignRight: action('alignRight'),
+ alignTop: action('alignTop'),
+ alignMiddle: action('alignMiddle'),
+ alignBottom: action('alignBottom'),
+ distributeHorizontally: action('distributeHorizontally'),
+ distributeVertically: action('distributeVertically'),
+ createCustomElement: action('createCustomElement'),
+ groupNodes: action('groupNodes'),
+ ungroupNodes: action('ungroupNodes'),
+ undoHistory: action('undoHistory'),
+ redoHistory: action('redoHistory'),
+};
+
+storiesOf('components/WorkpadHeader/EditMenu', module)
+ .add('default', () => (
+
+ ))
+ .add('clipboard data exists', () => (
+
+ ))
+ .add('single element selected', () => (
+
+ ))
+ .add('single grouped element selected', () => (
+
+ ))
+ .add('2 elements selected', () => (
+
+ ))
+ .add('3+ elements selected', () => (
+
+ ));
diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/edit_menu.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/edit_menu.tsx
new file mode 100644
index 0000000000000..5074cb7d42ec3
--- /dev/null
+++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/edit_menu.tsx
@@ -0,0 +1,442 @@
+/*
+ * 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, FunctionComponent, useState } from 'react';
+import PropTypes from 'prop-types';
+import { EuiButtonEmpty, EuiContextMenu, EuiIcon, EuiOverlayMask } from '@elastic/eui';
+import { ComponentStrings } from '../../../../i18n/components';
+import { ShortcutStrings } from '../../../../i18n/shortcuts';
+import { flattenPanelTree } from '../../../lib/flatten_panel_tree';
+import { Popover, ClosePopoverFn } from '../../popover';
+import { CustomElementModal } from '../../custom_element_modal';
+import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib/constants';
+
+const { WorkpadHeaderEditMenu: strings } = ComponentStrings;
+const shortcutHelp = ShortcutStrings.getShortcutHelp();
+
+export interface Props {
+ /**
+ * cuts selected elements
+ */
+ cutNodes: () => void;
+ /**
+ * copies selected elements to clipboard
+ */
+ copyNodes: () => void;
+ /**
+ * pastes elements stored in clipboard to page
+ */
+ pasteNodes: () => void;
+ /**
+ * clones selected elements
+ */
+ cloneNodes: () => void;
+ /**
+ * deletes selected elements
+ */
+ deleteNodes: () => void;
+ /**
+ * moves selected element to top layer
+ */
+ bringToFront: () => void;
+ /**
+ * moves selected element up one layer
+ */
+ bringForward: () => void;
+ /**
+ * moves selected element down one layer
+ */
+ sendBackward: () => void;
+ /**
+ * moves selected element to bottom layer
+ */
+ sendToBack: () => void;
+ /**
+ * saves the selected elements as an custom-element saved object
+ */
+ createCustomElement: (name: string, description: string, image: string) => void;
+ /**
+ * indicated whether the selected element is a group or not
+ */
+ groupIsSelected: boolean;
+ /**
+ * only more than one selected element can be grouped
+ */
+ selectedNodes: string[];
+ /**
+ * groups selected elements
+ */
+ groupNodes: () => void;
+ /**
+ * ungroups selected group
+ */
+ ungroupNodes: () => void;
+ /**
+ * left align selected elements
+ */
+ alignLeft: () => void;
+ /**
+ * center align selected elements
+ */
+ alignCenter: () => void;
+ /**
+ * right align selected elements
+ */
+ alignRight: () => void;
+ /**
+ * top align selected elements
+ */
+ alignTop: () => void;
+ /**
+ * middle align selected elements
+ */
+ alignMiddle: () => void;
+ /**
+ * bottom align selected elements
+ */
+ alignBottom: () => void;
+ /**
+ * horizontally distribute selected elements
+ */
+ distributeHorizontally: () => void;
+ /**
+ * vertically distribute selected elements
+ */
+ distributeVertically: () => void;
+ /**
+ * Reverts last change to the workpad
+ */
+ undoHistory: () => void;
+ /**
+ * Reapplies last reverted change to the workpad
+ */
+ redoHistory: () => void;
+ /**
+ * Is there element clipboard data to paste?
+ */
+ hasPasteData: boolean;
+}
+
+export const EditMenu: FunctionComponent = ({
+ cutNodes,
+ copyNodes,
+ pasteNodes,
+ deleteNodes,
+ cloneNodes,
+ bringToFront,
+ bringForward,
+ sendBackward,
+ sendToBack,
+ alignLeft,
+ alignCenter,
+ alignRight,
+ alignTop,
+ alignMiddle,
+ alignBottom,
+ distributeHorizontally,
+ distributeVertically,
+ createCustomElement,
+ selectedNodes,
+ groupIsSelected,
+ groupNodes,
+ ungroupNodes,
+ undoHistory,
+ redoHistory,
+ hasPasteData,
+}) => {
+ const [isModalVisible, setModalVisible] = useState(false);
+ const showModal = () => setModalVisible(true);
+ const hideModal = () => setModalVisible(false);
+
+ const handleSave = (name: string, description: string, image: string) => {
+ createCustomElement(name, description, image);
+ hideModal();
+ };
+
+ const editControl = (togglePopover: React.MouseEventHandler) => (
+
+ {strings.getEditMenuButtonLabel()}
+
+ );
+
+ const getPanelTree = (closePopover: ClosePopoverFn) => {
+ const groupMenuItem = groupIsSelected
+ ? {
+ name: strings.getUngroupMenuItemLabel(),
+ className: CONTEXT_MENU_TOP_BORDER_CLASSNAME,
+ icon: ,
+ onClick: () => {
+ ungroupNodes();
+ closePopover();
+ },
+ }
+ : {
+ name: strings.getGroupMenuItemLabel(),
+ className: CONTEXT_MENU_TOP_BORDER_CLASSNAME,
+ icon: ,
+ disabled: selectedNodes.length < 2,
+ onClick: () => {
+ groupNodes();
+ closePopover();
+ },
+ };
+
+ const orderMenuItem = {
+ name: strings.getOrderMenuItemLabel(),
+ disabled: selectedNodes.length !== 1, // TODO: change to === 0 when we support relayering multiple elements
+ icon: ,
+ panel: {
+ id: 1,
+ title: strings.getOrderMenuItemLabel(),
+ items: [
+ {
+ name: shortcutHelp.BRING_TO_FRONT, // TODO: check against current element position and disable if already top layer
+ icon: 'sortUp',
+ onClick: bringToFront,
+ },
+ {
+ name: shortcutHelp.BRING_FORWARD, // TODO: same as above
+ icon: 'arrowUp',
+ onClick: bringForward,
+ },
+ {
+ name: shortcutHelp.SEND_BACKWARD, // TODO: check against current element position and disable if already bottom layer
+ icon: 'arrowDown',
+ onClick: sendBackward,
+ },
+ {
+ name: shortcutHelp.SEND_TO_BACK, // TODO: same as above
+ icon: 'sortDown',
+ onClick: sendToBack,
+ },
+ ],
+ },
+ };
+
+ const alignmentMenuItem = {
+ name: strings.getAlignmentMenuItemLabel(),
+ className: 'canvasContextMenu',
+ disabled: selectedNodes.length < 2,
+ icon: ,
+ panel: {
+ id: 2,
+ title: strings.getAlignmentMenuItemLabel(),
+ items: [
+ {
+ name: strings.getLeftAlignMenuItemLabel(),
+ icon: 'editorItemAlignLeft',
+ onClick: () => {
+ alignLeft();
+ closePopover();
+ },
+ },
+ {
+ name: strings.getCenterAlignMenuItemLabel(),
+ icon: 'editorItemAlignCenter',
+ onClick: () => {
+ alignCenter();
+ closePopover();
+ },
+ },
+ {
+ name: strings.getRightAlignMenuItemLabel(),
+ icon: 'editorItemAlignRight',
+ onClick: () => {
+ alignRight();
+ closePopover();
+ },
+ },
+ {
+ name: strings.getTopAlignMenuItemLabel(),
+ icon: 'editorItemAlignTop',
+ onClick: () => {
+ alignTop();
+ closePopover();
+ },
+ },
+ {
+ name: strings.getMiddleAlignMenuItemLabel(),
+ icon: 'editorItemAlignMiddle',
+ onClick: () => {
+ alignMiddle();
+ closePopover();
+ },
+ },
+ {
+ name: strings.getBottomAlignMenuItemLabel(),
+ icon: 'editorItemAlignBottom',
+ onClick: () => {
+ alignBottom();
+ closePopover();
+ },
+ },
+ ],
+ },
+ };
+
+ const distributionMenuItem = {
+ name: strings.getDistributionMenuItemLabel(),
+ className: 'canvasContextMenu',
+ disabled: selectedNodes.length < 3,
+ icon: ,
+ panel: {
+ id: 3,
+ title: strings.getAlignmentMenuItemLabel(),
+ items: [
+ {
+ name: strings.getHorizontalDistributionMenuItemLabel(),
+ icon: 'editorDistributeHorizontal',
+ onClick: () => {
+ distributeHorizontally();
+ closePopover();
+ },
+ },
+ {
+ name: strings.getVerticalDistributionMenuItemLabel(),
+ icon: 'editorDistributeVertical',
+ onClick: () => {
+ distributeVertically();
+ closePopover();
+ },
+ },
+ ],
+ },
+ };
+
+ const savedElementMenuItem = {
+ name: strings.getSaveElementMenuItemLabel(),
+ icon: ,
+ disabled: selectedNodes.length < 1,
+ className: CONTEXT_MENU_TOP_BORDER_CLASSNAME,
+ onClick: () => {
+ showModal();
+ closePopover();
+ },
+ };
+
+ const items = [
+ {
+ // TODO: check history and disable when there are no more changes to revert
+ name: strings.getUndoMenuItemLabel(),
+ icon: ,
+ onClick: () => {
+ undoHistory();
+ },
+ },
+ {
+ // TODO: check history and disable when there are no more changes to reapply
+ name: strings.getRedoMenuItemLabel(),
+ icon: ,
+ onClick: () => {
+ redoHistory();
+ },
+ },
+ {
+ name: shortcutHelp.CUT,
+ icon: ,
+ className: CONTEXT_MENU_TOP_BORDER_CLASSNAME,
+ disabled: selectedNodes.length < 1,
+ onClick: () => {
+ cutNodes();
+ closePopover();
+ },
+ },
+ {
+ name: shortcutHelp.COPY,
+ disabled: selectedNodes.length < 1,
+ icon: ,
+ onClick: () => {
+ copyNodes();
+ },
+ },
+ {
+ name: shortcutHelp.PASTE, // TODO: can this be disabled if clipboard is empty?
+ icon: ,
+ disabled: !hasPasteData,
+ onClick: () => {
+ pasteNodes();
+ closePopover();
+ },
+ },
+ {
+ name: shortcutHelp.DELETE,
+ icon: ,
+ disabled: selectedNodes.length < 1,
+ onClick: () => {
+ deleteNodes();
+ closePopover();
+ },
+ },
+ {
+ name: shortcutHelp.CLONE,
+ icon: ,
+ disabled: selectedNodes.length < 1,
+ onClick: () => {
+ cloneNodes();
+ closePopover();
+ },
+ },
+ groupMenuItem,
+ orderMenuItem,
+ alignmentMenuItem,
+ distributionMenuItem,
+ savedElementMenuItem,
+ ];
+
+ return {
+ id: 0,
+ // title: strings.getEditMenuLabel(),
+ items,
+ };
+ };
+
+ return (
+
+
+ {({ closePopover }: { closePopover: ClosePopoverFn }) => (
+
+ )}
+
+ {isModalVisible ? (
+
+
+
+ ) : null}
+
+ );
+};
+
+EditMenu.propTypes = {
+ cutNodes: PropTypes.func.isRequired,
+ copyNodes: PropTypes.func.isRequired,
+ pasteNodes: PropTypes.func.isRequired,
+ deleteNodes: PropTypes.func.isRequired,
+ cloneNodes: PropTypes.func.isRequired,
+ bringToFront: PropTypes.func.isRequired,
+ bringForward: PropTypes.func.isRequired,
+ sendBackward: PropTypes.func.isRequired,
+ sendToBack: PropTypes.func.isRequired,
+ alignLeft: PropTypes.func.isRequired,
+ alignCenter: PropTypes.func.isRequired,
+ alignRight: PropTypes.func.isRequired,
+ alignTop: PropTypes.func.isRequired,
+ alignMiddle: PropTypes.func.isRequired,
+ alignBottom: PropTypes.func.isRequired,
+ distributeHorizontally: PropTypes.func.isRequired,
+ distributeVertically: PropTypes.func.isRequired,
+ createCustomElement: PropTypes.func.isRequired,
+ selectedNodes: PropTypes.arrayOf(PropTypes.string).isRequired,
+ groupIsSelected: PropTypes.bool.isRequired,
+ groupNodes: PropTypes.func.isRequired,
+ ungroupNodes: PropTypes.func.isRequired,
+};
diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/index.ts
new file mode 100644
index 0000000000000..a8bb7177dbd24
--- /dev/null
+++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/index.ts
@@ -0,0 +1,123 @@
+/*
+ * 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 { connect } from 'react-redux';
+import { compose, withHandlers, withProps } from 'recompose';
+import { Dispatch } from 'redux';
+import { State, PositionedElement } from '../../../../types';
+import { getClipboardData } from '../../../lib/clipboard';
+// @ts-ignore Untyped local
+import { flatten } from '../../../lib/aeroelastic/functional';
+// @ts-ignore Untyped local
+import { globalStateUpdater } from '../../workpad_page/integration_utils';
+// @ts-ignore Untyped local
+import { crawlTree } from '../../workpad_page/integration_utils';
+// @ts-ignore Untyped local
+import { insertNodes, elementLayer, removeElements } from '../../../state/actions/elements';
+// @ts-ignore Untyped local
+import { undoHistory, redoHistory } from '../../../state/actions/history';
+// @ts-ignore Untyped local
+import { selectToplevelNodes } from '../../../state/actions/transient';
+import {
+ getSelectedPage,
+ getNodes,
+ getSelectedToplevelNodes,
+} from '../../../state/selectors/workpad';
+import {
+ layerHandlerCreators,
+ clipboardHandlerCreators,
+ basicHandlerCreators,
+ groupHandlerCreators,
+ alignmentDistributionHandlerCreators,
+} from '../../../lib/element_handler_creators';
+import { EditMenu as Component, Props as ComponentProps } from './edit_menu';
+
+type LayoutState = any;
+
+type CommitFn = (type: string, payload: any) => LayoutState;
+
+interface OwnProps {
+ commit: CommitFn;
+}
+
+const withGlobalState = (
+ commit: CommitFn,
+ updateGlobalState: (layoutState: LayoutState) => void
+) => (type: string, payload: any) => {
+ const newLayoutState = commit(type, payload);
+ if (newLayoutState.currentScene.gestureEnd) {
+ updateGlobalState(newLayoutState);
+ }
+};
+
+/*
+ * TODO: this is all copied from interactive_workpad_page and workpad_shortcuts
+ */
+const mapStateToProps = (state: State) => {
+ const pageId = getSelectedPage(state);
+ const nodes = getNodes(state, pageId) as PositionedElement[];
+ const selectedToplevelNodes = getSelectedToplevelNodes(state);
+ const selectedPrimaryShapeObjects = selectedToplevelNodes
+ .map((id: string) => nodes.find((s: PositionedElement) => s.id === id))
+ .filter((shape?: PositionedElement) => shape) as PositionedElement[];
+ const selectedPersistentPrimaryNodes = flatten(
+ selectedPrimaryShapeObjects.map((shape: PositionedElement) =>
+ nodes.find((n: PositionedElement) => n.id === shape.id) // is it a leaf or a persisted group?
+ ? [shape.id]
+ : nodes.filter((s: PositionedElement) => s.position.parent === shape.id).map(s => s.id)
+ )
+ );
+ const selectedNodeIds = flatten(selectedPersistentPrimaryNodes.map(crawlTree(nodes)));
+
+ return {
+ pageId,
+ selectedToplevelNodes,
+ selectedNodes: selectedNodeIds.map((id: string) => nodes.find(s => s.id === id)),
+ state,
+ };
+};
+
+const mapDispatchToProps = (dispatch: Dispatch) => ({
+ insertNodes: (selectedNodes: PositionedElement[], pageId: string) =>
+ dispatch(insertNodes(selectedNodes, pageId)),
+ removeNodes: (nodeIds: string[], pageId: string) => dispatch(removeElements(nodeIds, pageId)),
+ selectToplevelNodes: (nodes: PositionedElement[]) =>
+ dispatch(
+ selectToplevelNodes(nodes.filter((e: PositionedElement) => !e.position.parent).map(e => e.id))
+ ),
+ elementLayer: (pageId: string, elementId: string, movement: number) => {
+ dispatch(elementLayer({ pageId, elementId, movement }));
+ },
+ undoHistory: () => dispatch(undoHistory()),
+ redoHistory: () => dispatch(redoHistory()),
+ dispatch,
+});
+
+const mergeProps = (
+ { state, selectedToplevelNodes, ...restStateProps }: ReturnType,
+ { dispatch, ...restDispatchProps }: ReturnType,
+ { commit }: OwnProps
+) => {
+ const updateGlobalState = globalStateUpdater(dispatch, state);
+
+ return {
+ ...restDispatchProps,
+ ...restStateProps,
+ commit: withGlobalState(commit, updateGlobalState),
+ groupIsSelected:
+ selectedToplevelNodes.length === 1 && selectedToplevelNodes[0].includes('group'),
+ };
+};
+
+export const EditMenu = compose(
+ connect(mapStateToProps, mapDispatchToProps, mergeProps),
+ withProps(() => ({ hasPasteData: Boolean(getClipboardData()) })),
+ withHandlers(basicHandlerCreators),
+ withHandlers(clipboardHandlerCreators),
+ withHandlers(layerHandlerCreators),
+ withHandlers(groupHandlerCreators),
+ withHandlers(alignmentDistributionHandlerCreators)
+)(Component);
diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx
index 621077c29c368..2ac0591a1bdd4 100644
--- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx
+++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx
@@ -62,7 +62,6 @@ export const ShareMenu: FunctionComponent = ({ onCopy, onExport, getExpor
const getPanelTree = (closePopover: ClosePopoverFn) => ({
id: 0,
- title: strings.getShareWorkpadMessage(),
items: [
{
name: strings.getShareDownloadJSONTitle(),
diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx
index 253e6c68cfc9e..5ff3030af8ad0 100644
--- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx
+++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx
@@ -16,6 +16,7 @@ import { ControlSettings } from './control_settings';
import { RefreshControl } from './refresh_control';
// @ts-ignore untyped local
import { FullscreenControl } from './fullscreen_control';
+import { EditMenu } from './edit_menu';
import { ElementMenu } from './element_menu';
import { ShareMenu } from './share_menu';
import { ViewMenu } from './view_menu';
@@ -26,12 +27,14 @@ export interface Props {
isWriteable: boolean;
toggleWriteable: () => void;
canUserWrite: boolean;
+ commit: (type: string, payload: any) => any;
}
export const WorkpadHeader: FunctionComponent = ({
isWriteable,
canUserWrite,
toggleWriteable,
+ commit,
}) => {
const keyHandler = (action: string) => {
if (action === 'EDITING') {
@@ -100,6 +103,9 @@ export const WorkpadHeader: FunctionComponent = ({
+
+
+
diff --git a/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts b/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts
index 1623035bd25ba..80a7c34e8bef5 100644
--- a/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts
+++ b/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts
@@ -363,7 +363,11 @@ export function getNodesForPage(page: CanvasPage, withAst: boolean): CanvasEleme
}
// todo unify or DRY up with `getElements`
-export function getNodes(state: State, pageId: string, withAst = true): CanvasElement[] {
+export function getNodes(
+ state: State,
+ pageId: string,
+ withAst = true
+): CanvasElement[] | PositionedElement[] {
const id = pageId || getSelectedPage(state);
if (!id) {
return [];
diff --git a/x-pack/legacy/plugins/canvas/types/elements.ts b/x-pack/legacy/plugins/canvas/types/elements.ts
index 86356f5bd32a9..5de6b4968545f 100644
--- a/x-pack/legacy/plugins/canvas/types/elements.ts
+++ b/x-pack/legacy/plugins/canvas/types/elements.ts
@@ -75,4 +75,8 @@ export interface ElementPosition {
parent: string | null;
}
-export type PositionedElement = CanvasElement & { ast: ExpressionAstExpression };
+export type PositionedElement = CanvasElement & {
+ ast: ExpressionAstExpression;
+} & {
+ position: ElementPosition;
+};