UNPKG

slate

Version:

A completely customizable framework for building rich text editors.

1,587 lines (1,562 loc) 198 kB
import { isPlainObject } from 'is-plain-object'; import { createDraft, finishDraft, isDraft, 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 applyToDraft = (editor, selection, op) => { switch (op.type) { case 'insert_node': { var { path, node } = op; var parent = Node.parent(editor, path); var index = path[path.length - 1]; if (index > parent.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.")); } parent.children.splice(index, 0, node); if (selection) { for (var [point, key] of Range.points(selection)) { selection[key] = Point.transform(point, op); } } break; } case 'insert_text': { var { path: _path, offset, text } = op; if (text.length === 0) break; var _node = Node.leaf(editor, _path); var before = _node.text.slice(0, offset); var after = _node.text.slice(offset); _node.text = before + text + after; if (selection) { for (var [_point, _key] of Range.points(selection)) { selection[_key] = Point.transform(_point, op); } } break; } case 'merge_node': { var { path: _path2 } = op; var _node2 = Node.get(editor, _path2); var prevPath = Path.previous(_path2); var prev = Node.get(editor, prevPath); var _parent = Node.parent(editor, _path2); var _index = _path2[_path2.length - 1]; if (Text.isText(_node2) && Text.isText(prev)) { prev.text += _node2.text; } else if (!Text.isText(_node2) && !Text.isText(prev)) { prev.children.push(..._node2.children); } else { throw new Error("Cannot apply a \"merge_node\" operation at path [".concat(_path2, "] to nodes of different interfaces: ").concat(Scrubber.stringify(_node2), " ").concat(Scrubber.stringify(prev))); } _parent.children.splice(_index, 1); if (selection) { for (var [_point2, _key2] of Range.points(selection)) { selection[_key2] = Point.transform(_point2, op); } } break; } case 'move_node': { var { path: _path3, newPath } = op; 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 _node3 = Node.get(editor, _path3); var _parent2 = Node.parent(editor, _path3); var _index2 = _path3[_path3.length - 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. _parent2.children.splice(_index2, 1); var truePath = Path.transform(_path3, op); var newParent = Node.get(editor, Path.parent(truePath)); var newIndex = truePath[truePath.length - 1]; newParent.children.splice(newIndex, 0, _node3); if (selection) { for (var [_point3, _key3] of Range.points(selection)) { selection[_key3] = Point.transform(_point3, op); } } break; } case 'remove_node': { var { path: _path4 } = op; var _index3 = _path4[_path4.length - 1]; var _parent3 = Node.parent(editor, _path4); _parent3.children.splice(_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 (selection) { for (var [_point4, _key4] of Range.points(selection)) { var result = Point.transform(_point4, op); if (selection != null && result != null) { selection[_key4] = 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) { _point4.path = _prev[1]; _point4.offset = _prev[0].text.length; } else if (next) { _point4.path = next[1]; _point4.offset = 0; } else { selection = null; } } } } break; } case 'remove_text': { var { path: _path5, offset: _offset, text: _text } = op; if (_text.length === 0) break; var _node4 = Node.leaf(editor, _path5); var _before = _node4.text.slice(0, _offset); var _after = _node4.text.slice(_offset + _text.length); _node4.text = _before + _after; if (selection) { for (var [_point5, _key5] of Range.points(selection)) { selection[_key5] = Point.transform(_point5, op); } } break; } case 'set_node': { var { path: _path6, properties, newProperties } = op; if (_path6.length === 0) { throw new Error("Cannot set properties on the root node!"); } var _node5 = Node.get(editor, _path6); for (var _key6 in newProperties) { if (_key6 === 'children' || _key6 === 'text') { throw new Error("Cannot set the \"".concat(_key6, "\" property of nodes!")); } var value = newProperties[_key6]; if (value == null) { delete _node5[_key6]; } else { _node5[_key6] = value; } } // properties that were previously defined, but are now missing, must be deleted for (var _key7 in properties) { if (!newProperties.hasOwnProperty(_key7)) { delete _node5[_key7]; } } break; } case 'set_selection': { var { newProperties: _newProperties } = op; if (_newProperties == null) { selection = _newProperties; } else { if (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.")); } selection = _objectSpread$e({}, _newProperties); } for (var _key8 in _newProperties) { var _value = _newProperties[_key8]; if (_value == null) { if (_key8 === 'anchor' || _key8 === 'focus') { throw new Error("Cannot remove the \"".concat(_key8, "\" selection property")); } delete selection[_key8]; } else { selection[_key8] = _value; } } } break; } case 'split_node': { var { path: _path7, position, properties: _properties } = op; if (_path7.length === 0) { throw new Error("Cannot apply a \"split_node\" operation at path [".concat(_path7, "] because the root node cannot be split.")); } var _node6 = Node.get(editor, _path7); var _parent4 = Node.parent(editor, _path7); var _index4 = _path7[_path7.length - 1]; var newNode; if (Text.isText(_node6)) { var _before2 = _node6.text.slice(0, position); var _after2 = _node6.text.slice(position); _node6.text = _before2; newNode = _objectSpread$e(_objectSpread$e({}, _properties), {}, { text: _after2 }); } else { var _before3 = _node6.children.slice(0, position); var _after3 = _node6.children.slice(position); _node6.children = _before3; newNode = _objectSpread$e(_objectSpread$e({}, _properties), {}, { children: _after3 }); } _parent4.children.splice(_index4 + 1, 0, newNode); if (selection) { for (var [_point6, _key9] of Range.points(selection)) { selection[_key9] = Point.transform(_point6, op); } } break; } } return selection; }; // eslint-disable-next-line no-redeclare var GeneralTransforms = { transform(editor, op) { editor.children = createDraft(editor.children); var selection = editor.selection && createDraft(editor.selection); try { selection = applyToDraft(editor, selection, op); } finally { editor.children = finishDraft(editor.children); if (selection) { editor.selection = isDraft(selection) ? finishDraft(selection) : selection; } else { editor.selection = null; } } } }; // 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); } }; /* 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 (isPlainObject(a) && isPlainObject(b)) { if (!isDeepEqual(a, b)) return false; } else 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 (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); }, 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 isPlainObject(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] : {}; return produce(range, r => { if (r === 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(r); if (Range.isForward(r)) { affinityAnchor = 'forward'; affinityFocus = isCollapsed ? affinityAnchor : 'backward'; } else { affinityAnchor = 'backward'; affinityFocus = isCollapsed ? affinityAnchor : 'forward'; } } else if (affinity === 'outward') { if (Range.isForward(r)) { affinityAnchor = 'backward'; affinityFocus = 'forward'; } else { affinityAnchor = 'forward'; affinityFocus = 'backward'; } } else { affinityAnchor = affinity; affinityFocus = affinity; } var anchor = Point.transform(r.anchor, op, { affinity: affinityAnchor }); var focus = Point.transform(r.focus, op, { affinity: affinityFocus }); if (!anchor || !focus) { return null; } r.anchor = anchor; r.focus = focus; }); } }; /** * Shared the function with isElementType utility */ var isElement = value => { return isPlainObject(value) && Node.isNodeList(value.children) && !Editor.isEditor(value); }; // eslint-disable-next-line no-redeclare var Element = { isAncestor(value) { return isPlainObject(value) && Node.isNodeList(value.children); }, isElement, isElementList(value) { return Array.isArray(value) && value.every(val => Element.isElement(val)); }, 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"]; var IS_NODE_LIST_CACHE = new WeakMap(); // 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 = root; for (var i = 0; i < path.length; i++) { var p = path[i]; if (Text.isText(node) || !node.children[p]) { throw new Error("Cannot find a descendant at path [".concat(path, "] in node: ").concat(Scrubber.stringify(root))); } 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) { return Text.isText(value) || Element.isElement(value) || Editor.isEditor(value); }, isNodeList(value) { if (!Array.isArray(value)) { return false; } var cachedResult = IS_NODE_LIST_CACHE.get(value); if (cachedResult !== undefined) { return cachedResult; } var isNodeList = value.every(val => Node.isNode(val)); IS_NODE_LIST_CACHE.set(value, isNodeList); return isNodeList; }, 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 (!isPlainObject(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) && isPlainObject(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) && isPlainObject(value.properties) && isPlainObject(value.newProperties); case 'set_selection': return value.properties === null && Range.isRange(value.newProperties) || value.newProperties === null && Range.isRange(value.properties) || isPlainObject(value.properties) && isPlainObject(value.newProperties); case 'split_node': return Path.isPath(value.path) && typeof value.position === 'number' && isPlainObject(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 IS_EDITOR_CACHE = new WeakMap(); var isEditor = value => { var cachedIsEditor = IS_EDITOR_CACHE.get(value); if (cachedIsEditor !== undefined) { return cachedIsEditor; } if (!isPlainObject(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 || isPlainObject(value.marks)) && (value.selection === null || Range.isRange(value.selection)) && Node.isNodeList(value.children) && Operation.isOperationList(value.operations); IS_EDITOR_CACHE.set(value, isEditor); 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.before(at, options); }, deleteBackward(editor) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var { unit = 'character' } = options; editor.deleteBackward(unit); }, deleteForward(editor) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var { unit = 'character' } = options; editor.deleteForward(unit); }, deleteFragment(editor, options) { editor.deleteFragment(options); }, edges(editor, at) { return editor.edges(at); }, elementReadOnly(editor) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return editor.elementReadOnly(options); }, end(editor, at) { return editor.end(at); }, first(editor, at) { return editor.first(at); }, fragment(editor, at) { return editor.fragment(at); }, hasBlocks(editor, element) { return editor.hasBlocks(element); }, hasInlines(editor, element) { return editor.hasInlines(element); }, hasPath(editor, path) { return editor.hasPath(path); }, hasTexts(editor, element) { return editor.hasTexts(element); }, insertBreak(editor) { editor.insertBreak(); }, insertFragment(editor, fragment, options) { editor.insertFragment(fragment, options); }, insertNode(editor, node) { editor.insertNode(node); }, insertSoftBreak(editor) { editor.insertSoftBreak(); }, insertText(editor, text) { editor.insertText(text); }, isBlock(editor, value) { return editor.isBlock(value); }, isEdge(editor, point, at) { return editor.isEdge(point, at); }, isEditor(value) { return isEditor(value); }, isElementReadOnly(editor, element) { return editor.isElementReadOnly(element); }, isEmpty(editor, element) { return editor.isEmpty(element); }, isEnd(editor, point, at) { return editor.isEnd(point, at); }, isInline(editor, value) { return editor.isInline(value); }, isNormalizing(editor) { return editor.isNormalizing(); }, isSelectable(editor, value) { return editor.isSelectable(value); }, isStart(editor, point, at) { return editor.isStart(point, at); }, isVoid(editor, value) { return editor.isVoid(value); }, last(editor, at) { return editor.last(at); }, leaf(editor, at, options) { return editor.leaf(at, options); }, levels(editor, options) { return editor.levels(options); }, marks(editor) { return editor.getMarks(); }, next(editor, options) { return editor.next(options); }, node(editor, at, options) { return editor.node(at, options); }, nodes(editor, options) { return editor.nodes(options); }, normalize(editor, options) { editor.normalize(options); }, parent(editor, at, options) { return editor.parent(at, options); }, path(editor, at, options) { return editor.path(at, options); }, pathRef(editor, path, options) { return editor.pathRef(path, options); }, pathRefs(editor) { return editor.pathRefs(); }, point(editor, at, options) { return editor.point(at, options); }, pointRef(editor, point, options) { return editor.pointRef(point, options); }, pointRefs(editor) { return editor.pointRefs(); }, positions(editor, options) { return editor.positions(options); }, previous(editor, options) { return editor.previous(options); }, range(editor, at, to) { return editor.range(at, to); }, rangeRef(editor, range, options) { return editor.rangeRef(range, options); }, rangeRefs(editor) { return editor.rangeRefs(); }, removeMark(editor, key) { editor.removeMark(key); },