UNPKG

@atlaskit/editor-plugin-layout

Version:

Layout plugin for @atlaskit/editor-core

298 lines (296 loc) 12.6 kB
import React from 'react'; import { INPUT_METHOD } from '@atlaskit/editor-common/analytics'; import commonMessages, { layoutMessages, layoutMessages as toolbarMessages } from '@atlaskit/editor-common/messages'; import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-check'; import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils'; import { akEditorSelectedNodeClassName } from '@atlaskit/editor-shared-styles'; import CopyIcon from '@atlaskit/icon/core/copy'; import DeleteIcon from '@atlaskit/icon/core/delete'; import LayoutOneColumnIcon from '@atlaskit/icon/core/layout-one-column'; import LayoutThreeColumnsIcon from '@atlaskit/icon/core/layout-three-columns'; import LayoutThreeColumnsSidebarsIcon from '@atlaskit/icon/core/layout-three-columns-sidebars'; import LayoutTwoColumnsIcon from '@atlaskit/icon/core/layout-two-columns'; import LayoutTwoColumnsSidebarLeftIcon from '@atlaskit/icon/core/layout-two-columns-sidebar-left'; import LayoutTwoColumnsSidebarRightIcon from '@atlaskit/icon/core/layout-two-columns-sidebar-right'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { deleteActiveLayoutNode, getPresetLayout, setPresetLayout } from '../pm-plugins/actions'; import { EditorLayoutFiveColumnsIcon, EditorLayoutFourColumnsIcon } from './icons/LayoutColumnsIcon'; import { LayoutThreeWithLeftSidebarsIcon } from './icons/LayoutThreeWithLeftSidebars'; import { LayoutThreeWithRightSidebarsIcon } from './icons/LayoutThreeWithRightSidebars'; const LAYOUT_TYPES = [{ id: 'editor.layout.twoEquals', type: 'two_equal', title: toolbarMessages.twoColumns, icon: LayoutTwoColumnsIcon }, { id: 'editor.layout.threeEquals', type: 'three_equal', title: toolbarMessages.threeColumns, icon: LayoutThreeColumnsIcon }]; const LAYOUT_TYPES_WITH_SINGLE_COL = [{ id: 'editor.layout.singeLayout', type: 'single', title: toolbarMessages.singleColumn, icon: LayoutOneColumnIcon }, ...LAYOUT_TYPES]; const SIDEBAR_LAYOUT_TYPES = [{ id: 'editor.layout.twoRightSidebar', type: 'two_right_sidebar', title: toolbarMessages.rightSidebar, icon: LayoutTwoColumnsSidebarRightIcon }, { id: 'editor.layout.twoLeftSidebar', type: 'two_left_sidebar', title: toolbarMessages.leftSidebar, icon: LayoutTwoColumnsSidebarLeftIcon }, { id: 'editor.layout.threeWithSidebars', type: 'three_with_sidebars', title: toolbarMessages.threeColumnsWithSidebars, icon: LayoutThreeColumnsSidebarsIcon }]; // These are used for advanced layout options const LAYOUT_WITH_TWO_COL_DISTRIBUTION = [{ id: 'editor.layout.twoEquals', type: 'two_equal', title: toolbarMessages.twoColumns, icon: LayoutTwoColumnsIcon }, { id: 'editor.layout.twoRightSidebar', type: 'two_right_sidebar', title: toolbarMessages.rightSidebar, icon: LayoutTwoColumnsSidebarRightIcon }, { id: 'editor.layout.twoLeftSidebar', type: 'two_left_sidebar', title: toolbarMessages.leftSidebar, icon: LayoutTwoColumnsSidebarLeftIcon }]; const LAYOUT_WITH_THREE_COL_DISTRIBUTION = [{ id: 'editor.layout.threeEquals', type: 'three_equal', title: toolbarMessages.threeColumns, icon: LayoutThreeColumnsIcon }, { id: 'editor.layout.threeWithSidebars', type: 'three_with_sidebars', title: toolbarMessages.threeColumnsWithSidebars, icon: LayoutThreeColumnsSidebarsIcon }, { id: 'editor.layout.threeRightSidebars', type: 'three_right_sidebars', title: toolbarMessages.threeColumnsWithRightSidebars, icon: LayoutThreeWithRightSidebarsIcon, iconFallback: LayoutThreeWithRightSidebarsIcon }, { id: 'editor.layout.threeLeftSidebars', type: 'three_left_sidebars', title: toolbarMessages.threeColumnsWithLeftSidebars, icon: LayoutThreeWithLeftSidebarsIcon, iconFallback: LayoutThreeWithLeftSidebarsIcon }]; const buildLayoutButton = (intl, item, currentLayout, editorAnalyticsAPI) => ({ id: item.id, type: 'button', icon: item.icon, iconFallback: item.iconFallback, testId: item.title.id ? `${item.title.id}` : undefined, title: intl.formatMessage(item.title), onClick: setPresetLayout(editorAnalyticsAPI)(item.type), selected: !!currentLayout && currentLayout === item.type, tabIndex: null }); export const layoutToolbarTitle = 'Layout floating controls'; const iconPlaceholder = LayoutTwoColumnsIcon; // TODO: ED-25466 - Replace with proper icon const getLayoutColumnsIcons = colCount => { if (!editorExperiment('single_column_layouts', true) && !editorExperiment('platform_editor_controls', 'variant1')) { return undefined; } switch (colCount) { case 1: return /*#__PURE__*/React.createElement(LayoutOneColumnIcon, { label: "" }); case 2: return /*#__PURE__*/React.createElement(LayoutTwoColumnsIcon, { label: "" }); case 3: return /*#__PURE__*/React.createElement(LayoutThreeColumnsIcon, { label: "" }); case 4: return /*#__PURE__*/React.createElement(EditorLayoutFourColumnsIcon, null); case 5: return /*#__PURE__*/React.createElement(EditorLayoutFiveColumnsIcon, null); default: return undefined; } }; const getAdvancedLayoutItems = ({ addSidebarLayouts, intl, editorAnalyticsAPI, state, node, nodeType, separator, deleteButton, currentLayout, allowAdvancedSingleColumnLayout }) => { const numberOfColumns = node.content.childCount || 2; const distributionOptions = numberOfColumns === 2 ? LAYOUT_WITH_TWO_COL_DISTRIBUTION : numberOfColumns === 3 ? LAYOUT_WITH_THREE_COL_DISTRIBUTION : []; const columnOptions = [{ title: intl.formatMessage(layoutMessages.columnOption, { count: 2 }), //'2-columns', icon: getLayoutColumnsIcons(2) || iconPlaceholder, onClick: setPresetLayout(editorAnalyticsAPI)('two_equal'), selected: numberOfColumns === 2 }, { title: intl.formatMessage(layoutMessages.columnOption, { count: 3 }), //'3-columns' icon: getLayoutColumnsIcons(3) || iconPlaceholder, onClick: setPresetLayout(editorAnalyticsAPI)('three_equal'), selected: numberOfColumns === 3 }, { title: intl.formatMessage(layoutMessages.columnOption, { count: 4 }), //'4-columns' icon: getLayoutColumnsIcons(4) || iconPlaceholder, onClick: setPresetLayout(editorAnalyticsAPI)('four_equal'), selected: numberOfColumns === 4 }, { title: intl.formatMessage(layoutMessages.columnOption, { count: 5 }), //'5-columns' icon: getLayoutColumnsIcons(5) || iconPlaceholder, onClick: setPresetLayout(editorAnalyticsAPI)('five_equal'), selected: numberOfColumns === 5 }]; const singleColumnOption = allowAdvancedSingleColumnLayout ? { title: intl.formatMessage(layoutMessages.columnOption, { count: 1 }), //'1-columns', icon: getLayoutColumnsIcons(1) || iconPlaceholder, onClick: setPresetLayout(editorAnalyticsAPI)('single'), selected: numberOfColumns === 1 } : []; return [{ type: 'dropdown', title: intl.formatMessage(layoutMessages.columnOption, { count: numberOfColumns }), //`${numberOfColumns}-columns`, options: [singleColumnOption, columnOptions].flat(), showSelected: true, testId: 'column-options-button' }, ...(distributionOptions.length > 0 ? [separator] : []), ...(addSidebarLayouts ? distributionOptions.map(i => buildLayoutButton(intl, i, currentLayout, editorAnalyticsAPI)) : [])]; }; const fullHeightSeparator = { type: 'separator', fullHeight: true }; export const buildToolbar = (state, intl, pos, _allowBreakout, addSidebarLayouts, allowSingleColumnLayout, allowAdvancedSingleColumnLayout, api) => { var _api$decorations$acti, _api$decorations, _api$analytics; const { hoverDecoration } = (_api$decorations$acti = api === null || api === void 0 ? void 0 : (_api$decorations = api.decorations) === null || _api$decorations === void 0 ? void 0 : _api$decorations.actions) !== null && _api$decorations$acti !== void 0 ? _api$decorations$acti : {}; const editorAnalyticsAPI = api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions; const node = state.doc.nodeAt(pos); const toolbarFlagsEnabled = areToolbarFlagsEnabled(Boolean(api === null || api === void 0 ? void 0 : api.toolbar)); if (node) { const currentLayout = getPresetLayout(node); const separator = { type: 'separator' }; const nodeType = state.schema.nodes.layoutSection; const deleteButton = { id: 'editor.layout.delete', type: 'button', appearance: 'danger', focusEditoronEnter: true, icon: DeleteIcon, testId: commonMessages.remove.id, title: intl.formatMessage(commonMessages.remove), onClick: deleteActiveLayoutNode(editorAnalyticsAPI, INPUT_METHOD.FLOATING_TB), onMouseEnter: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, true), onMouseLeave: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, false), onFocus: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, true), onBlur: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, false), tabIndex: null }; const copyButton = { type: 'copy-button', items: [{ state, formatMessage: intl.formatMessage, nodeType }] }; const layoutTypes = allowSingleColumnLayout ? LAYOUT_TYPES_WITH_SINGLE_COL : LAYOUT_TYPES; const hoverDecorationProps = (nodeType, className) => ({ onMouseEnter: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, true, className), onMouseLeave: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, false, className), onFocus: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, true, className), onBlur: hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, false, className) }); // testId is required to show focus on trigger button on ESC key press // see hideOnEsc in platform/packages/editor/editor-plugin-floating-toolbar/src/ui/Dropdown.tsx const testId = 'layout-overflow-dropdown-trigger'; const overflowMenu = { type: 'overflow-dropdown', testId, options: [{ title: intl.formatMessage(commonMessages.copyToClipboard), onClick: () => { var _api$core, _api$floatingToolbar; api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute( // @ts-ignore api === null || api === void 0 ? void 0 : (_api$floatingToolbar = api.floatingToolbar) === null || _api$floatingToolbar === void 0 ? void 0 : _api$floatingToolbar.commands.copyNode(nodeType, INPUT_METHOD.FLOATING_TB)); return true; }, icon: /*#__PURE__*/React.createElement(CopyIcon, { label: "" }), ...hoverDecorationProps(nodeType, akEditorSelectedNodeClassName) }, { title: intl.formatMessage(commonMessages.delete), onClick: deleteActiveLayoutNode(editorAnalyticsAPI, INPUT_METHOD.FLOATING_TB), icon: /*#__PURE__*/React.createElement(DeleteIcon, { label: "" }), ...hoverDecorationProps(nodeType) }] }; return { title: layoutToolbarTitle, // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting getDomRef: view => findDomRefAtPos(pos, view.domAtPos.bind(view)), nodeType, groupLabel: intl.formatMessage(toolbarMessages.floatingToolbarRadioGroupAriaLabel), items: [...(editorExperiment('advanced_layouts', true) ? getAdvancedLayoutItems({ addSidebarLayouts, intl, editorAnalyticsAPI, state, nodeType, node, separator, deleteButton, currentLayout, allowAdvancedSingleColumnLayout }) : [...layoutTypes.map(i => buildLayoutButton(intl, i, currentLayout, editorAnalyticsAPI)), ...(addSidebarLayouts ? SIDEBAR_LAYOUT_TYPES.map(i => buildLayoutButton(intl, i, currentLayout, editorAnalyticsAPI)) : [])]), ...(toolbarFlagsEnabled ? [fullHeightSeparator, overflowMenu] : [separator, copyButton, separator, deleteButton])], scrollable: true }; } return; };