@atlaskit/editor-plugin-text-formatting
Version:
Text-formatting plugin for @atlaskit/editor-core
108 lines (105 loc) • 4.09 kB
JavaScript
import { entireSelectionContainsMark } from '@atlaskit/editor-common/mark';
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
import { fg } from '@atlaskit/platform-feature-flags';
/**
* A custom version of the ProseMirror toggleMark, where we only toggle marks
* on text nodes in the selection rather than all inline nodes.
* @param markType
* @param attrs
*/
export var nextToggleMark = function nextToggleMark(markType, api, attrs) {
return function (_ref) {
var tr = _ref.tr;
var mark = markType.create(attrs);
// For cursor selections we can use the default behaviour.
if (tr.selection instanceof TextSelection && tr.selection.$cursor) {
if (mark.isInSet(tr.storedMarks || tr.selection.$cursor.marks())) {
tr.removeStoredMark(mark);
} else {
tr.addStoredMark(mark);
}
return tr;
}
return nextToggleMarkInRange(mark, api)({
tr: tr
});
};
};
var nextToggleMarkInRange = function nextToggleMarkInRange(mark, api) {
return function (_ref2) {
var tr = _ref2.tr;
if (tr.selection instanceof CellSelection) {
var removeMark = true;
var cells = [];
tr.selection.forEachCell(function (cell, cellPos) {
cells.push({
node: cell,
pos: cellPos
});
var from = cellPos;
var to = cellPos + cell.nodeSize;
removeMark && (removeMark = entireSelectionContainsMark(mark, tr.doc, from, to));
});
for (var i = cells.length - 1; i >= 0; i--) {
var cell = cells[i];
var from = cell.pos;
var to = from + cell.node.nodeSize;
nextApplyMarkOnRange(from, to, removeMark, mark, tr, api);
}
} else {
var _tr$selection = tr.selection,
$from = _tr$selection.$from,
$to = _tr$selection.$to;
// We decide to remove the mark only if the entire selection contains the mark
// Examples with *bold* text
// Scenario 1: Selection contains both bold and non-bold text -> bold entire selection
// Scenario 2: Selection contains only bold text -> un-bold entire selection
// Scenario 3: Selection contains no bold text -> bold entire selection
var _removeMark = entireSelectionContainsMark(mark, tr.doc, $from.pos, $to.pos);
nextApplyMarkOnRange($from.pos, $to.pos, _removeMark, mark, tr, api);
}
if (tr.docChanged) {
return tr;
}
return null;
};
};
export var nextApplyMarkOnRange = function nextApplyMarkOnRange(from, to, removeMark, mark, tr, api) {
var schema = tr.doc.type.schema;
var code = schema.marks.code;
if (mark.type === code) {
var _api$base;
api === null || api === void 0 || (_api$base = api.base) === null || _api$base === void 0 || (_api$base = _api$base.actions) === null || _api$base === void 0 || _api$base.resolveMarks(from, to, tr);
}
/**
* We should refactor this so text formatting doesn't reference plugins it doesn't know about.
*/
tr.doc.nodesBetween(tr.mapping.map(from), tr.mapping.map(to), function (node, pos) {
if (fg('editor_inline_comments_on_inline_nodes')) {
if (!node.isText) {
var isAllowedInlineNode = ['emoji', 'status', 'date', 'mention', 'inlineCard'].includes(node.type.name);
if (!isAllowedInlineNode) {
return true;
}
}
} else {
if (!node.isText) {
return true;
}
}
// This is an issue when the user selects some text.
// We need to check if the current node position is less than the range selection from.
// If it’s true, that means we should apply the mark using the range selection,
// not the current node position.
var nodeBetweenFrom = Math.max(pos, tr.mapping.map(from));
var nodeBetweenTo = Math.min(pos + node.nodeSize, tr.mapping.map(to));
if (removeMark) {
tr.removeMark(nodeBetweenFrom, nodeBetweenTo, mark);
} else {
tr.addMark(nodeBetweenFrom, nodeBetweenTo, mark);
}
return true;
});
return tr;
};