@atlaskit/editor-plugin-decorations
Version:
Decorations plugin for @atlaskit/editor-core
106 lines (103 loc) • 3.95 kB
JavaScript
import { getSourceNodesFromSelectionRange } from '@atlaskit/editor-common/selection';
import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
import { CellSelection, TableMap } from '@atlaskit/editor-tables';
import { findTableClosestToPos } from '@atlaskit/editor-tables/utils';
import { fg } from '@atlaskit/platform-feature-flags';
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
import { ACTIONS, decorationStateKey } from './main';
export const hoverDecorationCommand = ({
add,
className = 'danger selected',
selection: providedSelection
}) => ({
tr
}) => {
// [FEATURE FLAG: platform_editor_block_menu_jira_patch_1]
// Use the provided selection (e.g., preservedSelection) or fall back to tr.selection.
// To clean up: always use providedSelection || tr.selection, remove the feature flag check.
const selection = (fg('platform_editor_block_menu_jira_patch_1') ? providedSelection : undefined) || tr.selection;
const decorations = [];
const handleTableSelection = (pos = selection.$from) => {
const table = findTableClosestToPos(pos);
if (!table) {
return tr;
}
const tableNode = table.node;
const tablePos = table.pos;
decorations.push(Decoration.node(tablePos, tablePos + tableNode.nodeSize, {
class: className
}, {
key: `decorationNode-table-${tablePos}`
}));
const map = TableMap.get(tableNode);
const cellPositions = new Set(map.map);
cellPositions.forEach(cellPos => {
const docPos = tablePos + cellPos + 1;
const cell = tableNode.nodeAt(cellPos);
if (cell) {
decorations.push(Decoration.node(docPos, docPos + cell.nodeSize, {
class: className
}, {
key: `decorationNode-cell-${docPos}`
}));
}
});
};
const handleNodeSelection = (node, pos) => {
const from = pos;
const to = from + node.nodeSize;
decorations.push(Decoration.node(from, to, {
class: className
}, {
key: 'decorationNode'
}));
if (node.type.name === 'layoutSection' || node.type.name === 'layoutColumn') {
const startOfInsideOfContainer = tr.doc.resolve(from + 1);
const endOfInsideOfContainer = tr.doc.resolve(to - 1);
handleNodesSelection(startOfInsideOfContainer, endOfInsideOfContainer);
}
};
const handleNodesSelection = ($from, $to) => {
let currentPos = $from.pos;
const sourceNodes = getSourceNodesFromSelectionRange(tr, TextSelection.create(tr.doc, currentPos, $to.pos));
sourceNodes.forEach(sourceNode => {
if (sourceNode.type.name === 'table') {
const startPosOfTable = tr.doc.resolve(currentPos + 3);
handleTableSelection(startPosOfTable);
} else {
handleNodeSelection(sourceNode, currentPos);
}
currentPos += sourceNode.nodeSize;
});
};
// Selecting a table directly: CellSelection
if (selection instanceof CellSelection) {
handleTableSelection();
}
// multiselect: TextSelection
if (selection instanceof TextSelection) {
handleNodesSelection(selection.$from, selection.$to);
}
if (selection instanceof NodeSelection) {
handleNodeSelection(selection.node, selection.from);
}
// If no decorations were created, return early
if (decorations.length === 0) {
return tr;
}
const hasDangerDecorations = className.split(' ').includes('danger');
tr.setMeta(decorationStateKey, {
action: add ? ACTIONS.DECORATION_ADD : ACTIONS.DECORATION_REMOVE,
data: DecorationSet.create(tr.doc, decorations),
hasDangerDecorations: editorExperiment('platform_editor_block_menu', true) && hasDangerDecorations
}).setMeta('addToHistory', false);
return tr;
};
export const removeDecorationCommand = () => ({
tr
}) => {
return tr.setMeta(decorationStateKey, {
action: ACTIONS.DECORATION_REMOVE
});
};