@atlaskit/editor-plugin-highlight
Version:
Highlight plugin for @atlaskit/editor-core
111 lines (107 loc) • 3.85 kB
JavaScript
import { entireSelectionContainsMark } from '@atlaskit/editor-common/mark';
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
/**
* Value returned by `getActiveColor` when the selection spans more than one
* distinct highlight (background) color. Callers can compare against this to
* detect a multi-color selection.
*/
export const MULTIPLE_HIGHLIGHT_COLORS_SELECTED = 'multiple';
const getAllUniqueBackgroundColorMarksInRange = (from, to, tr) => {
const {
doc
} = tr;
const {
backgroundColor
} = doc.type.schema.marks;
const colorMarks = [];
const colorSet = new Set();
doc.nodesBetween(from, to, node => {
if (node.isLeaf) {
const mark = backgroundColor.isInSet(node.marks);
if (mark && !colorSet.has(mark.attrs.color)) {
colorMarks.push(mark);
colorSet.add(mark.attrs.color);
}
}
});
return colorMarks;
};
const getAllUniqueBackgroundColorMarksInCellSelection = (selection, tr) => {
const colorMarks = [];
const colorSet = new Set();
selection.forEachCell((cell, cellPos) => {
const from = cellPos;
const to = cellPos + cell.nodeSize;
const marks = getAllUniqueBackgroundColorMarksInRange(from, to, tr);
marks.forEach(mark => {
if (!colorSet.has(mark.attrs.color)) {
colorMarks.push(mark);
colorSet.add(mark.attrs.color);
}
});
});
return colorMarks;
};
// For Cell Selections - find first instance of a backgroundColor mark
// if all cells entirely contain this mark, set the color.
// If the selection contains multiple colors, return null
const getColorFromCellSelection = (selection, tr) => {
const marks = getAllUniqueBackgroundColorMarksInCellSelection(selection, tr);
if (marks.length > 1) {
return MULTIPLE_HIGHLIGHT_COLORS_SELECTED;
}
const firstColorMark = marks.at(0);
if (!firstColorMark) {
return null;
}
let foundUncolouredNode = false;
selection.forEachCell((cell, cellPos) => {
if (foundUncolouredNode) {
return;
}
const from = cellPos;
const to = cellPos + cell.nodeSize;
if (!entireSelectionContainsMark(firstColorMark, tr.doc, from, to)) {
foundUncolouredNode = true;
}
});
return foundUncolouredNode ? null : firstColorMark.attrs.color;
};
// All other selections - find the first instance of a backgroundColor mark
// if selection entirely contains this mark, set the color.
// If the selection contains multiple colors, return null
const getColorFromRange = (from, to, tr) => {
const marks = getAllUniqueBackgroundColorMarksInRange(from, to, tr);
if (marks.length > 1) {
return MULTIPLE_HIGHLIGHT_COLORS_SELECTED;
}
const firstColorMark = marks.at(0);
if (firstColorMark && entireSelectionContainsMark(firstColorMark, tr.doc, from, to)) {
return firstColorMark.attrs.color;
}
return null;
};
// For Cursor selections - set color if it is found in the storedMarks or $cursor.marks
const getColorFromCursor = (selection, tr) => {
var _mark$attrs$color;
if (!selection.$cursor) {
return null;
}
const mark = tr.doc.type.schema.marks.backgroundColor.isInSet([...(tr.storedMarks ? tr.storedMarks : []), ...selection.$cursor.marks()]);
return (_mark$attrs$color = mark === null || mark === void 0 ? void 0 : mark.attrs.color) !== null && _mark$attrs$color !== void 0 ? _mark$attrs$color : null;
};
export const getActiveColor = tr => {
const {
selection
} = tr;
let color = null;
if (selection instanceof CellSelection) {
color = getColorFromCellSelection(selection, tr);
} else if (selection instanceof TextSelection && selection.$cursor) {
color = getColorFromCursor(selection, tr);
} else {
color = getColorFromRange(selection.from, selection.to, tr);
}
return color;
};