UNPKG

@atlaskit/editor-plugin-selection

Version:

Selection plugin for @atlaskit/editor-core

108 lines (106 loc) 5.2 kB
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin'; import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state'; import { DecorationSet } from '@atlaskit/editor-prosemirror/view'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { selectionPluginKey } from '../types'; import { SelectionActionTypes } from './actions'; import { onCreateSelectionBetween } from './events/create-selection-between'; import { createOnKeydown } from './events/keydown'; import { onMouseOut } from './events/mouseout'; import { createPluginState, getPluginState } from './plugin-factory'; import { getDecorations, shouldRecalcDecorations } from './utils'; export const getInitialState = state => ({ decorationSet: getDecorations(state.tr), selection: state.selection }); export const createPlugin = (api, dispatch, dispatchAnalyticsEvent, options = {}) => { let cursorHidden = false; let blockSelection; return new SafePlugin({ key: selectionPluginKey, state: createPluginState(dispatch, getInitialState), appendTransaction(transactions, oldEditorState, newEditorState) { const { tr } = newEditorState; let manualSelection; let hideCursorChanged = false; let blockSelectionChanged = false; const needsManualSelection = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true); const needsBlockSelection = editorExperiment('platform_editor_block_menu', true, { exposure: true }); for (let i = transactions.length - 1; i >= 0; i--) { const meta = transactions[i].getMeta(selectionPluginKey); if ((meta === null || meta === void 0 ? void 0 : meta.hideCursor) !== undefined) { const newHideCursorValue = meta.hideCursor; if (cursorHidden !== newHideCursorValue) { cursorHidden = newHideCursorValue; hideCursorChanged = true; } } if (needsManualSelection && meta !== null && meta !== void 0 && meta.manualSelection && !manualSelection) { manualSelection = meta.manualSelection; } if (needsBlockSelection) { if (meta !== null && meta !== void 0 && meta.setBlockSelection) { blockSelection = meta.setBlockSelection; blockSelectionChanged = true; } if (meta !== null && meta !== void 0 && meta.clearBlockSelection) { blockSelection = undefined; blockSelectionChanged = true; } } } if (!shouldRecalcDecorations({ oldEditorState, newEditorState }) && !manualSelection && !hideCursorChanged && !blockSelectionChanged) { return; } tr.setMeta(selectionPluginKey, { type: SelectionActionTypes.SET_DECORATIONS, selection: tr.selection, decorationSet: getDecorations(tr, manualSelection, cursorHidden, blockSelection) }); return tr; }, filterTransaction(tr, state) { // Prevent single click selecting atom nodes on mobile (we want to select with long press gesture instead) if (options.useLongPressSelection && tr.selectionSet && tr.selection instanceof NodeSelection && !tr.getMeta(selectionPluginKey)) { return false; } // Prevent prosemirror's mutation observer overriding a node selection with a text selection // for exact same range - this was cause of being unable to change dates in collab: // https://product-fabric.atlassian.net/browse/ED-10645 if (state.selection instanceof NodeSelection && tr.selection instanceof TextSelection && state.selection.from === tr.selection.from && state.selection.to === tr.selection.to) { return false; } return true; }, props: { createSelectionBetween: onCreateSelectionBetween, decorations(state) { var _api$interaction, _api$interaction$shar; const interactionState = api === null || api === void 0 ? void 0 : (_api$interaction = api.interaction) === null || _api$interaction === void 0 ? void 0 : (_api$interaction$shar = _api$interaction.sharedState.currentState()) === null || _api$interaction$shar === void 0 ? void 0 : _api$interaction$shar.interactionState; // Do not show selection decorations for live pages where the user has not // interacted with the page. We do not show cursor until interaction and we do not // want to show selections either. if ((options.__livePage || expValEquals('platform_editor_no_cursor_on_edit_page_init', 'isEnabled', true)) && interactionState === 'hasNotHadInteraction') { return DecorationSet.empty; } return getPluginState(state).decorationSet; }, handleDOMEvents: { keydown: createOnKeydown({ __livePage: options.__livePage }), // Without this event, it is not possible to click and drag to select the first node in the // document if the node is a block with content (e.g. a panel with a paragraph inside). mouseout: onMouseOut } } }); };