UNPKG

@atlaskit/editor-plugin-table

Version:

Table plugin for the @atlaskit/editor

105 lines (100 loc) 3.68 kB
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin'; import { PluginKey } from '@atlaskit/editor-prosemirror/state'; import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view'; import { CellSelection } from '@atlaskit/editor-tables/cell-selection'; import { findCellClosestToPos } from '@atlaskit/editor-tables/utils'; import { TableCssClassName as ClassName } from '../../types'; const EMPTY_STATE = { cellPos: -1, decorationSet: DecorationSet.empty }; export const activeCellHighlightPluginKey = new PluginKey('tableActiveCellHighlight'); /** * Returns the position of the cell containing the cursor, or -1 if the cursor * is not in a table cell or the selection is a CellSelection. */ const getActiveCellPos = state => { if (state.selection instanceof CellSelection) { return -1; } try { const cell = findCellClosestToPos(state.selection.$from); return cell ? cell.pos : -1; } catch { return -1; } }; /** Builds the decoration state highlighting the cell at `cellPos`. */ const buildState = (cellPos, state) => { if (cellPos === -1) { return EMPTY_STATE; } try { const cell = state.doc.nodeAt(cellPos); if (!cell) { return EMPTY_STATE; } const decoration = Decoration.node(cellPos, cellPos + cell.nodeSize, { class: ClassName.ACTIVE_CURSOR_CELL }); return { cellPos, decorationSet: DecorationSet.create(state.doc, [decoration]) }; } catch { return EMPTY_STATE; } }; const hasHadInteraction = api => { var _api$interaction$shar; // The interaction plugin is optional - when absent, default to treating the editor as // interacted. if (!(api !== null && api !== void 0 && api.interaction)) { return true; } return ((_api$interaction$shar = api.interaction.sharedState.currentState()) === null || _api$interaction$shar === void 0 ? void 0 : _api$interaction$shar.interactionState) !== 'hasNotHadInteraction'; }; /** Stays hidden until the first interaction. */ const createStateField = api => ({ init: (_config, state) => { const interacted = hasHadInteraction(api); return { ...(interacted ? buildState(getActiveCellPos(state), state) : EMPTY_STATE), hasHadInteraction: interacted }; }, apply: (tr, prev, _oldState, newState) => { const interacted = hasHadInteraction(api); // Keep the highlight hidden until the first interaction has happened. if (!interacted) { return prev; } // `true` only on the single transaction that crosses from "no interaction" // to "interacted" — the meta-only transaction that reveals the highlight. const isFirstInteraction = !prev.hasHadInteraction; if (!tr.docChanged && !tr.selectionSet && !isFirstInteraction) { // Doc, selection and interaction are all unchanged — nothing to do. return prev; } const nextCellPos = getActiveCellPos(newState); if (nextCellPos === prev.cellPos && !isFirstInteraction) { return prev; } return { ...buildState(nextCellPos, newState), hasHadInteraction: true }; } }); export const createPlugin = api => { return new SafePlugin({ key: activeCellHighlightPluginKey, state: createStateField(api), props: { decorations: state => { var _activeCellHighlightP, _activeCellHighlightP2; return (_activeCellHighlightP = (_activeCellHighlightP2 = activeCellHighlightPluginKey.getState(state)) === null || _activeCellHighlightP2 === void 0 ? void 0 : _activeCellHighlightP2.decorationSet) !== null && _activeCellHighlightP !== void 0 ? _activeCellHighlightP : DecorationSet.empty; } } }); };