UNPKG

slate

Version:

A completely customizable framework for building rich text editors.

1,522 lines (1,497 loc) 205 kB
import { produce } from 'immer'; // eslint-disable-next-line no-redeclare var PathRef = { transform(ref, op) { var { current, affinity } = ref; if (current == null) { return; } var path = Path.transform(current, op, { affinity }); ref.current = path; if (path == null) { ref.unref(); } } }; // eslint-disable-next-line no-redeclare var PointRef = { transform(ref, op) { var { current, affinity } = ref; if (current == null) { return; } var point = Point.transform(current, op, { affinity }); ref.current = point; if (point == null) { ref.unref(); } } }; // eslint-disable-next-line no-redeclare var RangeRef = { transform(ref, op) { var { current, affinity } = ref; if (current == null) { return; } var path = Range.transform(current, op, { affinity }); ref.current = path; if (path == null) { ref.unref(); } } }; var DIRTY_PATHS = new WeakMap(); var DIRTY_PATH_KEYS = new WeakMap(); var FLUSHING = new WeakMap(); var NORMALIZING = new WeakMap(); var PATH_REFS = new WeakMap(); var POINT_REFS = new WeakMap(); var RANGE_REFS = new WeakMap(); // eslint-disable-next-line no-redeclare var Path = { ancestors(path) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var { reverse = false } = options; var paths = Path.levels(path, options); if (reverse) { paths = paths.slice(1); } else { paths = paths.slice(0, -1); } return paths; }, common(path, another) { var common = []; for (var i = 0; i < path.length && i < another.length; i++) { var av = path[i]; var bv = another[i]; if (av !== bv) { break; } common.push(av); } return common; }, compare(path, another) { var min = Math.min(path.length, another.length); for (var i = 0; i < min; i++) { if (path[i] < another[i]) return -1; if (path[i] > another[i]) return 1; } return 0; }, endsAfter(path, another) { var i = path.length - 1; var as = path.slice(0, i); var bs = another.slice(0, i); var av = path[i]; var bv = another[i]; return Path.equals(as, bs) && av > bv; }, endsAt(path, another) { var i = path.length; var as = path.slice(0, i); var bs = another.slice(0, i); return Path.equals(as, bs); }, endsBefore(path, another) { var i = path.length - 1; var as = path.slice(0, i); var bs = another.slice(0, i); var av = path[i]; var bv = another[i]; return Path.equals(as, bs) && av < bv; }, equals(path, another) { return path.length === another.length && path.every((n, i) => n === another[i]); }, hasPrevious(path) { return path[path.length - 1] > 0; }, isAfter(path, another) { return Path.compare(path, another) === 1; }, isAncestor(path, another) { return path.length < another.length && Path.compare(path, another) === 0; }, isBefore(path, another) { return Path.compare(path, another) === -1; }, isChild(path, another) { return path.length === another.length + 1 && Path.compare(path, another) === 0; }, isCommon(path, another) { return path.length <= another.length && Path.compare(path, another) === 0; }, isDescendant(path, another) { return path.length > another.length && Path.compare(path, another) === 0; }, isParent(path, another) { return path.length + 1 === another.length && Path.compare(path, another) === 0; }, isPath(value) { return Array.isArray(value) && (value.length === 0 || typeof value[0] === 'number'); }, isSibling(path, another) { if (path.length !== another.length) { return false; } var as = path.slice(0, -1); var bs = another.slice(0, -1); var al = path[path.length - 1]; var bl = another[another.length - 1]; return al !== bl && Path.equals(as, bs); }, levels(path) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var { reverse = false } = options; var list = []; for (var i = 0; i <= path.length; i++) { list.push(path.slice(0, i)); } if (reverse) { list.reverse(); } return list; }, next(path) { if (path.length === 0) { throw new Error("Cannot get the next path of a root path [".concat(path, "], because it has no next index.")); } var last = path[path.length - 1]; return path.slice(0, -1).concat(last + 1); }, operationCanTransformPath(operation) { switch (operation.type) { case 'insert_node': case 'remove_node': case 'merge_node': case 'split_node': case 'move_node': return true; default: return false; } }, parent(path) { if (path.length === 0) { throw new Error("Cannot get the parent path of the root path [".concat(path, "].")); } return path.slice(0, -1); }, previous(path) { if (path.length === 0) { throw new Error("Cannot get the previous path of a root path [".concat(path, "], because it has no previous index.")); } var last = path[path.length - 1]; if (last <= 0) { throw new Error("Cannot get the previous path of a first child path [".concat(path, "] because it would result in a negative index.")); } return path.slice(0, -1).concat(last - 1); }, relative(path, ancestor) { if (!Path.isAncestor(ancestor, path) && !Path.equals(path, ancestor)) { throw new Error("Cannot get the relative path of [".concat(path, "] inside ancestor [").concat(ancestor, "], because it is not above or equal to the path.")); } return path.slice(ancestor.length); }, transform(path, operation) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; if (!path) return null; // PERF: use destructing instead of immer var p = [...path]; var { affinity = 'forward' } = options; // PERF: Exit early if the operation is guaranteed not to have an effect. if (path.length === 0) { return p; } switch (operation.type) { case 'insert_node': { var { path: op } = operation; if (Path.equals(op, p) || Path.endsBefore(op, p) || Path.isAncestor(op, p)) { p[op.length - 1] += 1; } break; } case 'remove_node': { var { path: _op } = operation; if (Path.equals(_op, p) || Path.isAncestor(_op, p)) { return null; } else if (Path.endsBefore(_op, p)) { p[_op.length - 1] -= 1; } break; } case 'merge_node': { var { path: _op2, position } = operation; if (Path.equals(_op2, p) || Path.endsBefore(_op2, p)) { p[_op2.length - 1] -= 1; } else if (Path.isAncestor(_op2, p)) { p[_op2.length - 1] -= 1; p[_op2.length] += position; } break; } case 'split_node': { var { path: _op3, position: _position } = operation; if (Path.equals(_op3, p)) { if (affinity === 'forward') { p[p.length - 1] += 1; } else if (affinity === 'backward') ; else { return null; } } else if (Path.endsBefore(_op3, p)) { p[_op3.length - 1] += 1; } else if (Path.isAncestor(_op3, p) && path[_op3.length] >= _position) { p[_op3.length - 1] += 1; p[_op3.length] -= _position; } break; } case 'move_node': { var { path: _op4, newPath: onp } = operation; // If the old and new path are the same, it's a no-op. if (Path.equals(_op4, onp)) { return p; } if (Path.isAncestor(_op4, p) || Path.equals(_op4, p)) { var copy = onp.slice(); if (Path.endsBefore(_op4, onp) && _op4.length < onp.length) { copy[_op4.length - 1] -= 1; } return copy.concat(p.slice(_op4.length)); } else if (Path.isSibling(_op4, onp) && (Path.isAncestor(onp, p) || Path.equals(onp, p))) { if (Path.endsBefore(_op4, p)) { p[_op4.length - 1] -= 1; } else { p[_op4.length - 1] += 1; } } else if (Path.endsBefore(onp, p) || Path.equals(onp, p) || Path.isAncestor(onp, p)) { if (Path.endsBefore(_op4, p)) { p[_op4.length - 1] -= 1; } p[onp.length - 1] += 1; } else if (Path.endsBefore(_op4, p)) { if (Path.equals(onp, p)) { p[onp.length - 1] += 1; } p[_op4.length - 1] -= 1; } break; } } return p; } }; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function ownKeys$e(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$e(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$e(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$e(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } var insertChildren = function insertChildren(xs, index) { for (var _len = arguments.length, newValues = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { newValues[_key - 2] = arguments[_key]; } return [...xs.slice(0, index), ...newValues, ...xs.slice(index)]; }; var replaceChildren = function replaceChildren(xs, index, removeCount) { for (var _len2 = arguments.length, newValues = new Array(_len2 > 3 ? _len2 - 3 : 0), _key2 = 3; _key2 < _len2; _key2++) { newValues[_key2 - 3] = arguments[_key2]; } return [...xs.slice(0, index), ...newValues, ...xs.slice(index + removeCount)]; }; var removeChildren = replaceChildren; /** * Replace a descendant with a new node, replacing all ancestors */ var modifyDescendant = (editor, path, f) => { if (path.length === 0) { throw new Error('Cannot modify the editor'); } var node = Node.get(editor, path); var slicedPath = path.slice(); var modifiedNode = f(node); while (slicedPath.length > 1) { var _index = slicedPath.pop(); var ancestorNode = Node.get(editor, slicedPath); modifiedNode = _objectSpread$e(_objectSpread$e({}, ancestorNode), {}, { children: replaceChildren(ancestorNode.children, _index, 1, modifiedNode) }); } var index = slicedPath.pop(); editor.children = replaceChildren(editor.children, index, 1, modifiedNode); }; /** * Replace the children of a node, replacing all ancestors */ var modifyChildren = (editor, path, f) => { if (path.length === 0) { editor.children = f(editor.children); } else { modifyDescendant(editor, path, node => { if (Text.isText(node)) { throw new Error("Cannot get the element at path [".concat(path, "] because it refers to a leaf node: ").concat(Scrubber.stringify(node))); } return _objectSpread$e(_objectSpread$e({}, node), {}, { children: f(node.children) }); }); } }; /** * Replace a leaf, replacing all ancestors */ var modifyLeaf = (editor, path, f) => modifyDescendant(editor, path, node => { if (!Text.isText(node)) { throw new Error("Cannot get the leaf node at path [".concat(path, "] because it refers to a non-leaf node: ").concat(Scrubber.stringify(node))); } return f(node); }); // eslint-disable-next-line no-redeclare var GeneralTransforms = { transform(editor, op) { var transformSelection = false; switch (op.type) { case 'insert_node': { var { path, node } = op; modifyChildren(editor, Path.parent(path), children => { var index = path[path.length - 1]; if (index > children.length) { throw new Error("Cannot apply an \"insert_node\" operation at path [".concat(path, "] because the destination is past the end of the node.")); } return insertChildren(children, index, node); }); transformSelection = true; break; } case 'insert_text': { var { path: _path, offset, text } = op; if (text.length === 0) break; modifyLeaf(editor, _path, node => { var before = node.text.slice(0, offset); var after = node.text.slice(offset); return _objectSpread$e(_objectSpread$e({}, node), {}, { text: before + text + after }); }); transformSelection = true; break; } case 'merge_node': { var { path: _path2 } = op; var index = _path2[_path2.length - 1]; var prevPath = Path.previous(_path2); var prevIndex = prevPath[prevPath.length - 1]; modifyChildren(editor, Path.parent(_path2), children => { var node = children[index]; var prev = children[prevIndex]; var newNode; if (Text.isText(node) && Text.isText(prev)) { newNode = _objectSpread$e(_objectSpread$e({}, prev), {}, { text: prev.text + node.text }); } else if (!Text.isText(node) && !Text.isText(prev)) { newNode = _objectSpread$e(_objectSpread$e({}, prev), {}, { children: prev.children.concat(node.children) }); } else { throw new Error("Cannot apply a \"merge_node\" operation at path [".concat(_path2, "] to nodes of different interfaces: ").concat(Scrubber.stringify(node), " ").concat(Scrubber.stringify(prev))); } return replaceChildren(children, prevIndex, 2, newNode); }); transformSelection = true; break; } case 'move_node': { var { path: _path3, newPath } = op; var _index2 = _path3[_path3.length - 1]; if (Path.isAncestor(_path3, newPath)) { throw new Error("Cannot move a path [".concat(_path3, "] to new path [").concat(newPath, "] because the destination is inside itself.")); } var _node = Node.get(editor, _path3); modifyChildren(editor, Path.parent(_path3), children => removeChildren(children, _index2, 1)); // This is tricky, but since the `path` and `newPath` both refer to // the same snapshot in time, there's a mismatch. After either // removing the original position, the second step's path can be out // of date. So instead of using the `op.newPath` directly, we // transform `op.path` to ascertain what the `newPath` would be after // the operation was applied. var truePath = Path.transform(_path3, op); var newIndex = truePath[truePath.length - 1]; modifyChildren(editor, Path.parent(truePath), children => insertChildren(children, newIndex, _node)); transformSelection = true; break; } case 'remove_node': { var { path: _path4 } = op; var _index3 = _path4[_path4.length - 1]; modifyChildren(editor, Path.parent(_path4), children => removeChildren(children, _index3, 1)); // Transform all the points in the value, but if the point was in the // node that was removed we need to update the range or remove it. if (editor.selection) { var selection = _objectSpread$e({}, editor.selection); for (var [point, key] of Range.points(selection)) { var result = Point.transform(point, op); if (selection != null && result != null) { selection[key] = result; } else { var prev = void 0; var next = void 0; for (var [n, p] of Node.texts(editor)) { if (Path.compare(p, _path4) === -1) { prev = [n, p]; } else { next = [n, p]; break; } } var preferNext = false; if (prev && next) { if (Path.equals(next[1], _path4)) { preferNext = !Path.hasPrevious(next[1]); } else { preferNext = Path.common(prev[1], _path4).length < Path.common(next[1], _path4).length; } } if (prev && !preferNext) { selection[key] = { path: prev[1], offset: prev[0].text.length }; } else if (next) { selection[key] = { path: next[1], offset: 0 }; } else { selection = null; } } } if (!selection || !Range.equals(selection, editor.selection)) { editor.selection = selection; } } break; } case 'remove_text': { var { path: _path5, offset: _offset, text: _text } = op; if (_text.length === 0) break; modifyLeaf(editor, _path5, node => { var before = node.text.slice(0, _offset); var after = node.text.slice(_offset + _text.length); return _objectSpread$e(_objectSpread$e({}, node), {}, { text: before + after }); }); transformSelection = true; break; } case 'set_node': { var { path: _path6, properties, newProperties } = op; if (_path6.length === 0) { throw new Error("Cannot set properties on the root node!"); } modifyDescendant(editor, _path6, node => { var newNode = _objectSpread$e({}, node); for (var _key3 in newProperties) { if (_key3 === 'children' || _key3 === 'text') { throw new Error("Cannot set the \"".concat(_key3, "\" property of nodes!")); } var value = newProperties[_key3]; if (value == null) { delete newNode[_key3]; } else { newNode[_key3] = value; } } // properties that were previously defined, but are now missing, must be deleted for (var _key4 in properties) { if (!newProperties.hasOwnProperty(_key4)) { delete newNode[_key4]; } } return newNode; }); break; } case 'set_selection': { var { newProperties: _newProperties } = op; if (_newProperties == null) { editor.selection = null; break; } if (editor.selection == null) { if (!Range.isRange(_newProperties)) { throw new Error("Cannot apply an incomplete \"set_selection\" operation properties ".concat(Scrubber.stringify(_newProperties), " when there is no current selection.")); } editor.selection = _objectSpread$e({}, _newProperties); break; } var _selection = _objectSpread$e({}, editor.selection); for (var _key5 in _newProperties) { var value = _newProperties[_key5]; if (value == null) { if (_key5 === 'anchor' || _key5 === 'focus') { throw new Error("Cannot remove the \"".concat(_key5, "\" selection property")); } delete _selection[_key5]; } else { _selection[_key5] = value; } } editor.selection = _selection; break; } case 'split_node': { var { path: _path7, position, properties: _properties } = op; var _index4 = _path7[_path7.length - 1]; if (_path7.length === 0) { throw new Error("Cannot apply a \"split_node\" operation at path [".concat(_path7, "] because the root node cannot be split.")); } modifyChildren(editor, Path.parent(_path7), children => { var node = children[_index4]; var newNode; var nextNode; if (Text.isText(node)) { var before = node.text.slice(0, position); var after = node.text.slice(position); newNode = _objectSpread$e(_objectSpread$e({}, node), {}, { text: before }); nextNode = _objectSpread$e(_objectSpread$e({}, _properties), {}, { text: after }); } else { var _before = node.children.slice(0, position); var _after = node.children.slice(position); newNode = _objectSpread$e(_objectSpread$e({}, node), {}, { children: _before }); nextNode = _objectSpread$e(_objectSpread$e({}, _properties), {}, { children: _after }); } return replaceChildren(children, _index4, 1, newNode, nextNode); }); transformSelection = true; break; } } if (transformSelection && editor.selection) { var _selection2 = _objectSpread$e({}, editor.selection); for (var [_point, _key6] of Range.points(_selection2)) { _selection2[_key6] = Point.transform(_point, op); } if (!Range.equals(_selection2, editor.selection)) { editor.selection = _selection2; } } } }; // eslint-disable-next-line no-redeclare var NodeTransforms = { insertNodes(editor, nodes, options) { editor.insertNodes(nodes, options); }, liftNodes(editor, options) { editor.liftNodes(options); }, mergeNodes(editor, options) { editor.mergeNodes(options); }, moveNodes(editor, options) { editor.moveNodes(options); }, removeNodes(editor, options) { editor.removeNodes(options); }, setNodes(editor, props, options) { editor.setNodes(props, options); }, splitNodes(editor, options) { editor.splitNodes(options); }, unsetNodes(editor, props, options) { editor.unsetNodes(props, options); }, unwrapNodes(editor, options) { editor.unwrapNodes(options); }, wrapNodes(editor, element, options) { editor.wrapNodes(element, options); } }; // eslint-disable-next-line no-redeclare var SelectionTransforms = { collapse(editor, options) { editor.collapse(options); }, deselect(editor) { editor.deselect(); }, move(editor, options) { editor.move(options); }, select(editor, target) { editor.select(target); }, setPoint(editor, props, options) { editor.setPoint(props, options); }, setSelection(editor, props) { editor.setSelection(props); } }; var isObject = value => typeof value === 'object' && value !== null; /* Custom deep equal comparison for Slate nodes. We don't need general purpose deep equality; Slate only supports plain values, Arrays, and nested objects. Complex values nested inside Arrays are not supported. Slate objects are designed to be serialised, so missing keys are deliberately normalised to undefined. */ var isDeepEqual = (node, another) => { for (var key in node) { var a = node[key]; var b = another[key]; if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) return false; for (var i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false; } } else if (isObject(a) && isObject(b)) { if (!isDeepEqual(a, b)) return false; } else if (a !== b) { return false; } } /* Deep object equality is only necessary in one direction; in the reverse direction we are only looking for keys that are missing. As above, undefined keys are normalised to missing. */ for (var _key in another) { if (node[_key] === undefined && another[_key] !== undefined) { return false; } } return true; }; function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } var _excluded$4 = ["anchor", "focus"]; function ownKeys$d(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$d(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$d(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$d(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // eslint-disable-next-line no-redeclare var Range = { edges(range) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var { reverse = false } = options; var { anchor, focus } = range; return Range.isBackward(range) === reverse ? [anchor, focus] : [focus, anchor]; }, end(range) { var [, end] = Range.edges(range); return end; }, equals(range, another) { return Point.equals(range.anchor, another.anchor) && Point.equals(range.focus, another.focus); }, surrounds(range, target) { var intersectionRange = Range.intersection(range, target); if (!intersectionRange) { return false; } return Range.equals(intersectionRange, target); }, includes(range, target) { if (Range.isRange(target)) { if (Range.includes(range, target.anchor) || Range.includes(range, target.focus)) { return true; } var [rs, re] = Range.edges(range); var [ts, te] = Range.edges(target); return Point.isBefore(rs, ts) && Point.isAfter(re, te); } var [start, end] = Range.edges(range); var isAfterStart = false; var isBeforeEnd = false; if (Point.isPoint(target)) { isAfterStart = Point.compare(target, start) >= 0; isBeforeEnd = Point.compare(target, end) <= 0; } else { isAfterStart = Path.compare(target, start.path) >= 0; isBeforeEnd = Path.compare(target, end.path) <= 0; } return isAfterStart && isBeforeEnd; }, intersection(range, another) { var rest = _objectWithoutProperties(range, _excluded$4); var [s1, e1] = Range.edges(range); var [s2, e2] = Range.edges(another); var start = Point.isBefore(s1, s2) ? s2 : s1; var end = Point.isBefore(e1, e2) ? e1 : e2; if (Point.isBefore(end, start)) { return null; } else { return _objectSpread$d({ anchor: start, focus: end }, rest); } }, isBackward(range) { var { anchor, focus } = range; return Point.isAfter(anchor, focus); }, isCollapsed(range) { var { anchor, focus } = range; return Point.equals(anchor, focus); }, isExpanded(range) { return !Range.isCollapsed(range); }, isForward(range) { return !Range.isBackward(range); }, isRange(value) { return isObject(value) && Point.isPoint(value.anchor) && Point.isPoint(value.focus); }, *points(range) { yield [range.anchor, 'anchor']; yield [range.focus, 'focus']; }, start(range) { var [start] = Range.edges(range); return start; }, transform(range, op) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; if (range === null) { return null; } var { affinity = 'inward' } = options; var affinityAnchor; var affinityFocus; if (affinity === 'inward') { // If the range is collapsed, make sure to use the same affinity to // avoid the two points passing each other and expanding in the opposite // direction var isCollapsed = Range.isCollapsed(range); if (Range.isForward(range)) { affinityAnchor = 'forward'; affinityFocus = isCollapsed ? affinityAnchor : 'backward'; } else { affinityAnchor = 'backward'; affinityFocus = isCollapsed ? affinityAnchor : 'forward'; } } else if (affinity === 'outward') { if (Range.isForward(range)) { affinityAnchor = 'backward'; affinityFocus = 'forward'; } else { affinityAnchor = 'forward'; affinityFocus = 'backward'; } } else { affinityAnchor = affinity; affinityFocus = affinity; } var anchor = Point.transform(range.anchor, op, { affinity: affinityAnchor }); var focus = Point.transform(range.focus, op, { affinity: affinityFocus }); if (!anchor || !focus) { return null; } return { anchor, focus }; } }; /** * Shared the function with isElementType utility */ var isElement = function isElement(value) { var { deep = false } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!isObject(value)) return false; // PERF: No need to use the full Editor.isEditor here var isEditor = typeof value.apply === 'function'; if (isEditor) return false; var isChildrenValid = deep ? Node.isNodeList(value.children) : Array.isArray(value.children); return isChildrenValid; }; // eslint-disable-next-line no-redeclare var Element = { isAncestor(value) { var { deep = false } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return isObject(value) && Node.isNodeList(value.children, { deep }); }, isElement, isElementList(value) { var { deep = false } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return Array.isArray(value) && value.every(val => Element.isElement(val, { deep })); }, isElementProps(props) { return props.children !== undefined; }, isElementType: function isElementType(value, elementVal) { var elementKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'type'; return isElement(value) && value[elementKey] === elementVal; }, matches(element, props) { for (var key in props) { if (key === 'children') { continue; } if (element[key] !== props[key]) { return false; } } return true; } }; var _excluded$3 = ["children"], _excluded2$3 = ["text"]; // eslint-disable-next-line no-redeclare var Node = { ancestor(root, path) { var node = Node.get(root, path); if (Text.isText(node)) { throw new Error("Cannot get the ancestor node at path [".concat(path, "] because it refers to a text node instead: ").concat(Scrubber.stringify(node))); } return node; }, ancestors(root, path) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; return function* () { for (var p of Path.ancestors(path, options)) { var n = Node.ancestor(root, p); var entry = [n, p]; yield entry; } }(); }, child(root, index) { if (Text.isText(root)) { throw new Error("Cannot get the child of a text node: ".concat(Scrubber.stringify(root))); } var c = root.children[index]; if (c == null) { throw new Error("Cannot get child at index `".concat(index, "` in node: ").concat(Scrubber.stringify(root))); } return c; }, children(root, path) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; return function* () { var { reverse = false } = options; var ancestor = Node.ancestor(root, path); var { children } = ancestor; var index = reverse ? children.length - 1 : 0; while (reverse ? index >= 0 : index < children.length) { var child = Node.child(ancestor, index); var childPath = path.concat(index); yield [child, childPath]; index = reverse ? index - 1 : index + 1; } }(); }, common(root, path, another) { var p = Path.common(path, another); var n = Node.get(root, p); return [n, p]; }, descendant(root, path) { var node = Node.get(root, path); if (Editor.isEditor(node)) { throw new Error("Cannot get the descendant node at path [".concat(path, "] because it refers to the root editor node instead: ").concat(Scrubber.stringify(node))); } return node; }, descendants(root) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return function* () { for (var [node, path] of Node.nodes(root, options)) { if (path.length !== 0) { // NOTE: we have to coerce here because checking the path's length does // guarantee that `node` is not a `Editor`, but TypeScript doesn't know. yield [node, path]; } } }(); }, elements(root) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return function* () { for (var [node, path] of Node.nodes(root, options)) { if (Element.isElement(node)) { yield [node, path]; } } }(); }, extractProps(node) { if (Element.isAncestor(node)) { var properties = _objectWithoutProperties(node, _excluded$3); return properties; } else { var properties = _objectWithoutProperties(node, _excluded2$3); return properties; } }, first(root, path) { var p = path.slice(); var n = Node.get(root, p); while (n) { if (Text.isText(n) || n.children.length === 0) { break; } else { n = n.children[0]; p.push(0); } } return [n, p]; }, fragment(root, range) { if (Text.isText(root)) { throw new Error("Cannot get a fragment starting from a root text node: ".concat(Scrubber.stringify(root))); } var newRoot = produce({ children: root.children }, r => { var [start, end] = Range.edges(range); var nodeEntries = Node.nodes(r, { reverse: true, pass: _ref => { var [, path] = _ref; return !Range.includes(range, path); } }); for (var [, path] of nodeEntries) { if (!Range.includes(range, path)) { var parent = Node.parent(r, path); var index = path[path.length - 1]; parent.children.splice(index, 1); } if (Path.equals(path, end.path)) { var leaf = Node.leaf(r, path); leaf.text = leaf.text.slice(0, end.offset); } if (Path.equals(path, start.path)) { var _leaf = Node.leaf(r, path); _leaf.text = _leaf.text.slice(start.offset); } } if (Editor.isEditor(r)) { r.selection = null; } }); return newRoot.children; }, get(root, path) { var node = Node.getIf(root, path); if (node === undefined) { throw new Error("Cannot find a descendant at path [".concat(path, "] in node: ").concat(Scrubber.stringify(root))); } return node; }, getIf(root, path) { var node = root; for (var i = 0; i < path.length; i++) { var p = path[i]; if (Text.isText(node) || !node.children[p]) { return; } node = node.children[p]; } return node; }, has(root, path) { var node = root; for (var i = 0; i < path.length; i++) { var p = path[i]; if (Text.isText(node) || !node.children[p]) { return false; } node = node.children[p]; } return true; }, isNode(value) { var { deep = false } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return Text.isText(value) || Element.isElement(value, { deep }) || Editor.isEditor(value, { deep }); }, isNodeList(value) { var { deep = false } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return Array.isArray(value) && value.every(val => Node.isNode(val, { deep })); }, last(root, path) { var p = path.slice(); var n = Node.get(root, p); while (n) { if (Text.isText(n) || n.children.length === 0) { break; } else { var i = n.children.length - 1; n = n.children[i]; p.push(i); } } return [n, p]; }, leaf(root, path) { var node = Node.get(root, path); if (!Text.isText(node)) { throw new Error("Cannot get the leaf node at path [".concat(path, "] because it refers to a non-leaf node: ").concat(Scrubber.stringify(node))); } return node; }, levels(root, path) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; return function* () { for (var p of Path.levels(path, options)) { var n = Node.get(root, p); yield [n, p]; } }(); }, matches(node, props) { return Element.isElement(node) && Element.isElementProps(props) && Element.matches(node, props) || Text.isText(node) && Text.isTextProps(props) && Text.matches(node, props); }, nodes(root) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return function* () { var { pass, reverse = false } = options; var { from = [], to } = options; var visited = new Set(); var p = []; var n = root; while (true) { if (to && (reverse ? Path.isBefore(p, to) : Path.isAfter(p, to))) { break; } if (!visited.has(n)) { yield [n, p]; } // If we're allowed to go downward and we haven't descended yet, do. if (!visited.has(n) && !Text.isText(n) && n.children.length !== 0 && (pass == null || pass([n, p]) === false)) { visited.add(n); var nextIndex = reverse ? n.children.length - 1 : 0; if (Path.isAncestor(p, from)) { nextIndex = from[p.length]; } p = p.concat(nextIndex); n = Node.get(root, p); continue; } // If we're at the root and we can't go down, we're done. if (p.length === 0) { break; } // If we're going forward... if (!reverse) { var newPath = Path.next(p); if (Node.has(root, newPath)) { p = newPath; n = Node.get(root, p); continue; } } // If we're going backward... if (reverse && p[p.length - 1] !== 0) { var _newPath = Path.previous(p); p = _newPath; n = Node.get(root, p); continue; } // Otherwise we're going upward... p = Path.parent(p); n = Node.get(root, p); visited.add(n); } }(); }, parent(root, path) { var parentPath = Path.parent(path); var p = Node.get(root, parentPath); if (Text.isText(p)) { throw new Error("Cannot get the parent of path [".concat(path, "] because it does not exist in the root.")); } return p; }, string(node) { if (Text.isText(node)) { return node.text; } else { return node.children.map(Node.string).join(''); } }, texts(root) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return function* () { for (var [node, path] of Node.nodes(root, options)) { if (Text.isText(node)) { yield [node, path]; } } }(); } }; function ownKeys$c(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$c(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$c(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$c(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // eslint-disable-next-line no-redeclare var Operation = { isNodeOperation(value) { return Operation.isOperation(value) && value.type.endsWith('_node'); }, isOperation(value) { if (!isObject(value)) { return false; } switch (value.type) { case 'insert_node': return Path.isPath(value.path) && Node.isNode(value.node); case 'insert_text': return typeof value.offset === 'number' && typeof value.text === 'string' && Path.isPath(value.path); case 'merge_node': return typeof value.position === 'number' && Path.isPath(value.path) && isObject(value.properties); case 'move_node': return Path.isPath(value.path) && Path.isPath(value.newPath); case 'remove_node': return Path.isPath(value.path) && Node.isNode(value.node); case 'remove_text': return typeof value.offset === 'number' && typeof value.text === 'string' && Path.isPath(value.path); case 'set_node': return Path.isPath(value.path) && isObject(value.properties) && isObject(value.newProperties); case 'set_selection': return value.properties === null && Range.isRange(value.newProperties) || value.newProperties === null && Range.isRange(value.properties) || isObject(value.properties) && isObject(value.newProperties); case 'split_node': return Path.isPath(value.path) && typeof value.position === 'number' && isObject(value.properties); default: return false; } }, isOperationList(value) { return Array.isArray(value) && value.every(val => Operation.isOperation(val)); }, isSelectionOperation(value) { return Operation.isOperation(value) && value.type.endsWith('_selection'); }, isTextOperation(value) { return Operation.isOperation(value) && value.type.endsWith('_text'); }, inverse(op) { switch (op.type) { case 'insert_node': { return _objectSpread$c(_objectSpread$c({}, op), {}, { type: 'remove_node' }); } case 'insert_text': { return _objectSpread$c(_objectSpread$c({}, op), {}, { type: 'remove_text' }); } case 'merge_node': { return _objectSpread$c(_objectSpread$c({}, op), {}, { type: 'split_node', path: Path.previous(op.path) }); } case 'move_node': { var { newPath, path } = op; // PERF: in this case the move operation is a no-op anyways. if (Path.equals(newPath, path)) { return op; } // If the move happens completely within a single parent the path and // newPath are stable with respect to each other. if (Path.isSibling(path, newPath)) { return _objectSpread$c(_objectSpread$c({}, op), {}, { path: newPath, newPath: path }); } // If the move does not happen within a single parent it is possible // for the move to impact the true path to the location where the node // was removed from and where it was inserted. We have to adjust for this // and find the original path. We can accomplish this (only in non-sibling) // moves by looking at the impact of the move operation on the node // after the original move path. var inversePath = Path.transform(path, op); var inverseNewPath = Path.transform(Path.next(path), op); return _objectSpread$c(_objectSpread$c({}, op), {}, { path: inversePath, newPath: inverseNewPath }); } case 'remove_node': { return _objectSpread$c(_objectSpread$c({}, op), {}, { type: 'insert_node' }); } case 'remove_text': { return _objectSpread$c(_objectSpread$c({}, op), {}, { type: 'insert_text' }); } case 'set_node': { var { properties, newProperties } = op; return _objectSpread$c(_objectSpread$c({}, op), {}, { properties: newProperties, newProperties: properties }); } case 'set_selection': { var { properties: _properties, newProperties: _newProperties } = op; if (_properties == null) { return _objectSpread$c(_objectSpread$c({}, op), {}, { properties: _newProperties, newProperties: null }); } else if (_newProperties == null) { return _objectSpread$c(_objectSpread$c({}, op), {}, { properties: null, newProperties: _properties }); } else { return _objectSpread$c(_objectSpread$c({}, op), {}, { properties: _newProperties, newProperties: _properties }); } } case 'split_node': { return _objectSpread$c(_objectSpread$c({}, op), {}, { type: 'merge_node', path: Path.next(op.path) }); } } } }; var isEditor = function isEditor(value) { var { deep = false } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!isObject(value)) { return false; } var isEditor = typeof value.addMark === 'function' && typeof value.apply === 'function' && typeof value.deleteFragment === 'function' && typeof value.insertBreak === 'function' && typeof value.insertSoftBreak === 'function' && typeof value.insertFragment === 'function' && typeof value.insertNode === 'function' && typeof value.insertText === 'function' && typeof value.isElementReadOnly === 'function' && typeof value.isInline === 'function' && typeof value.isSelectable === 'function' && typeof value.isVoid === 'function' && typeof value.normalizeNode === 'function' && typeof value.onChange === 'function' && typeof value.removeMark === 'function' && typeof value.getDirtyPaths === 'function' && (value.marks === null || isObject(value.marks)) && (value.selection === null || Range.isRange(value.selection)) && (!deep || Node.isNodeList(value.children)) && Operation.isOperationList(value.operations); return isEditor; }; // eslint-disable-next-line no-redeclare var Editor = { above(editor, options) { return editor.above(options); }, addMark(editor, key, value) { editor.addMark(key, value); }, after(editor, at, options) { return editor.after(at, options); }, before(editor, at, options) { return editor