UNPKG

@atlaskit/editor-plugin-block-controls

Version:

Block controls plugin for @atlaskit/editor-core

159 lines (155 loc) 6.2 kB
import { createElement } from 'react'; import { bind } from 'bind-event-listener'; import ReactDOM from 'react-dom'; // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead import uuid from 'uuid'; import { Decoration } from '@atlaskit/editor-prosemirror/view'; import { fg } from '@atlaskit/platform-feature-flags'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { DragHandle, DragHandleWithVisibility } from '../ui/drag-handle'; import { TYPE_HANDLE_DEC, TYPE_NODE_DEC, unmountDecorations } from './decorations-common'; import { getActiveBlockMarks, getMatchingBlockMarks } from './utils/marks'; export const emptyParagraphNodeDecorations = () => { const anchorName = `--node-anchor-paragraph-0`; const style = `anchor-name: ${anchorName}; margin-top: 0px;`; return Decoration.node(0, 2, { style, ['data-drag-handler-anchor-name']: anchorName }, { type: TYPE_NODE_DEC }); }; export const findHandleDec = (decorations, from, to) => { return decorations.find(from, to, spec => spec.type === TYPE_HANDLE_DEC); }; export const dragHandleDecoration = ({ api, formatMessage, pos, anchorName, nodeType, nodeViewPortalProviderAPI, handleOptions, anchorRectCache, editorState }) => { unmountDecorations(nodeViewPortalProviderAPI, 'data-blocks-drag-handle-container', 'data-blocks-drag-handle-key'); let unbind; // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead const key = uuid(); const widgetSpec = editorExperiment('platform_editor_breakout_resizing', true) ? { side: -1, type: TYPE_HANDLE_DEC, // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead testid: `${TYPE_HANDLE_DEC}-${uuid()}`, /** * sigh - `marks` influences the position that the widget is drawn (as described on the `side` property). * Exclude 'breakout' on purpose, so the widgets render at the top of the document to avoid z-index issues * Other block marks must be added, otherwise PM will split the DOM elements causing mutations and re-draws */ marks: expValEquals('platform_editor_small_font_size', 'isEnabled', true) ? getMatchingBlockMarks(editorState, pos, [editorState.schema.marks.alignment, editorState.schema.marks.fontSize]) : expValEquals('platform_editor_clean_up_widget_mark_logic', 'isEnabled', true) ? [] : getActiveBlockMarks(editorState, pos), destroy: node => { unbind && unbind(); } } : { side: -1, type: TYPE_HANDLE_DEC, // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead testid: `${TYPE_HANDLE_DEC}-${uuid()}`, marks: expValEquals('platform_editor_small_font_size', 'isEnabled', true) ? getMatchingBlockMarks(editorState, pos, [editorState.schema.marks.alignment, editorState.schema.marks.fontSize]) : expValEquals('platform_editor_clean_up_widget_mark_logic', 'isEnabled', true) ? [] : expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? getActiveBlockMarks(editorState, pos) : undefined, destroy: node => { unbind && unbind(); } }; return Decoration.widget(pos, (view, getPosUnsafe) => { const element = document.createElement('span'); // inline decoration causes focus issues when refocusing Editor into first line element.style.display = 'block'; if (fg('confluence_remix_button_right_side_block_fg')) { element.setAttribute('data-blocks-decorator-widget', 'true'); } element.setAttribute('data-testid', 'block-ctrl-decorator-widget'); element.setAttribute('data-blocks-drag-handle-container', 'true'); element.setAttribute('data-blocks-drag-handle-key', key); let isTopLevelNode = true; const getPos = () => { try { return getPosUnsafe(); } catch { // Ignore errors from getPosUnsafe return undefined; } }; const newPos = getPos(); if (typeof newPos === 'number') { const $pos = view.state.doc.resolve(newPos); isTopLevelNode = ($pos === null || $pos === void 0 ? void 0 : $pos.parent.type.name) === 'doc'; } /* * We disable mouseover event to fix flickering issue on hover * However, the tooltip for nested drag handle is no long working. */ if (newPos === undefined || !isTopLevelNode) { if (fg('platform_editor_fix_widget_destroy')) { element.onmouseover = e => { e.stopPropagation(); }; } else { unbind = bind(element, { type: 'mouseover', listener: e => { e.stopPropagation(); } }); } } // There are times when global clear: "both" styles are applied to this decoration causing jumpiness // due to margins applied to other nodes eg. Headings element.style.clear = 'unset'; // temporarily re-instating ReactDOM.render to fix drag handle focus issue, fix to // follow via ED-26546 // previous under platform_editor_react18_plugin_portalprovider // nodeViewPortalProviderAPI.render( // () => // createElement(DragHandle, { // view, // api, // formatMessage, // getPos, // anchorName, // nodeType, // handleOptions, // isTopLevelNode, // }), // element, // key, // ); if (editorExperiment('platform_editor_controls', 'variant1')) { ReactDOM.render( /*#__PURE__*/createElement(DragHandleWithVisibility, { view, api, formatMessage, getPos, anchorName, nodeType, handleOptions, isTopLevelNode, anchorRectCache }), element); } else { ReactDOM.render( /*#__PURE__*/createElement(DragHandle, { view, api, formatMessage, getPos, anchorName, nodeType, handleOptions, isTopLevelNode, anchorRectCache }), element); } return element; }, widgetSpec); };