@portabletext/editor 8.1.1 → 8.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,14 +13,14 @@
13
13
 
14
14
  This library provides you with the building blocks to create a completely custom editor experience built on top of Portable Text. We recommend [checking out the official documentation](https://www.portabletext.org/). The following guide includes the basics to get your started.
15
15
 
16
- In order to set up an editor you'll need to:
16
+ To set up an editor you need to:
17
17
 
18
18
  - Create a schema that defines the rich text and block content elements.
19
19
  - Create a toolbar to toggle and insert these elements.
20
20
  - Set up rendering for each element type in the editor, including text blocks and inline formatting.
21
21
  - Render the editor.
22
22
 
23
- Check out the [Portable Text Playground](../../apps/playground/) for a comprehensive example of the editor in action.
23
+ The [Portable Text Playground](../../apps/playground/) is a complete editor built with this library. Use it as a reference.
24
24
 
25
25
  ### Add the library to your project
26
26
 
@@ -361,7 +361,7 @@ Extend the editor with [official plugins](../../#editor-plugins).
361
361
 
362
362
  ## End-user experience
363
363
 
364
- In order to provide a robust and consistent end-user experience, the editor is backed by an elaborate E2E test suite generated from a [human-readable Gherkin spec](./gherkin-spec/).
364
+ A large E2E test suite, generated from a [human-readable Gherkin spec](./gherkin-spec/), guards the end-user experience: how selection, editing, and formatting behave under real keyboard and pointer input.
365
365
 
366
366
  ## Development
367
367
 
package/lib/index.js CHANGED
@@ -465,17 +465,19 @@ const IS_IOS = typeof navigator < "u" && typeof window < "u" && /iPad|iPhone|iPo
465
465
  },
466
466
  toEditorSelection: (editor, domRange, options) => {
467
467
  let { exactMatch, suppressThrow } = options, el = isDOMSelection(domRange) ? domRange.anchorNode : domRange.startContainer, anchorNode = null, anchorOffset = 0, focusNode = null, focusOffset = 0, isCollapsed = !1;
468
- if (el) if (isDOMSelection(domRange)) {
469
- if (IS_FIREFOX && domRange.rangeCount > 1) {
470
- focusNode = domRange.focusNode;
471
- let firstRange = domRange.getRangeAt(0), lastRange = domRange.getRangeAt(domRange.rangeCount - 1);
472
- if (focusNode instanceof HTMLTableRowElement && firstRange.startContainer instanceof HTMLTableRowElement && lastRange.startContainer instanceof HTMLTableRowElement) {
473
- let getLastChildren = (element) => element.childElementCount > 0 ? getLastChildren(element.children[0]) : element, firstNodeRow = firstRange.startContainer, lastNodeRow = lastRange.startContainer, firstNode = getLastChildren(firstNodeRow.children[firstRange.startOffset]), lastNode = getLastChildren(lastNodeRow.children[lastRange.startOffset]);
474
- focusOffset = 0, anchorNode = lastNode.childNodes.length > 0 ? lastNode.childNodes[0] ?? null : lastNode, focusNode = firstNode.childNodes.length > 0 ? firstNode.childNodes[0] ?? null : firstNode, anchorOffset = lastNode instanceof HTMLElement ? lastNode.innerHTML.length : 0;
475
- } else firstRange.startContainer === focusNode ? (anchorNode = lastRange.endContainer, anchorOffset = lastRange.endOffset, focusOffset = firstRange.startOffset) : (anchorNode = firstRange.startContainer, anchorOffset = firstRange.endOffset, focusOffset = lastRange.startOffset);
476
- } else anchorNode = domRange.anchorNode, anchorOffset = domRange.anchorOffset, focusNode = domRange.focusNode, focusOffset = domRange.focusOffset;
477
- isCollapsed = IS_CHROME && hasShadowRoot(anchorNode) || IS_FIREFOX ? domRange.anchorNode === domRange.focusNode && domRange.anchorOffset === domRange.focusOffset : domRange.isCollapsed;
478
- } else anchorNode = domRange.startContainer, anchorOffset = domRange.startOffset, focusNode = domRange.endContainer, focusOffset = domRange.endOffset, isCollapsed = domRange.collapsed;
468
+ if (el) {
469
+ if (isDOMSelection(domRange)) {
470
+ if (IS_FIREFOX && domRange.rangeCount > 1) {
471
+ focusNode = domRange.focusNode;
472
+ let firstRange = domRange.getRangeAt(0), lastRange = domRange.getRangeAt(domRange.rangeCount - 1);
473
+ if (focusNode instanceof HTMLTableRowElement && firstRange.startContainer instanceof HTMLTableRowElement && lastRange.startContainer instanceof HTMLTableRowElement) {
474
+ let getLastChildren = (element) => element.childElementCount > 0 ? getLastChildren(element.children[0]) : element, firstNodeRow = firstRange.startContainer, lastNodeRow = lastRange.startContainer, firstNode = getLastChildren(firstNodeRow.children[firstRange.startOffset]), lastNode = getLastChildren(lastNodeRow.children[lastRange.startOffset]);
475
+ focusOffset = 0, anchorNode = lastNode.childNodes.length > 0 ? lastNode.childNodes[0] ?? null : lastNode, focusNode = firstNode.childNodes.length > 0 ? firstNode.childNodes[0] ?? null : firstNode, anchorOffset = lastNode instanceof HTMLElement ? lastNode.innerHTML.length : 0;
476
+ } else firstRange.startContainer === focusNode ? (anchorNode = lastRange.endContainer, anchorOffset = lastRange.endOffset, focusOffset = firstRange.startOffset) : (anchorNode = firstRange.startContainer, anchorOffset = firstRange.endOffset, focusOffset = lastRange.startOffset);
477
+ } else anchorNode = domRange.anchorNode, anchorOffset = domRange.anchorOffset, focusNode = domRange.focusNode, focusOffset = domRange.focusOffset;
478
+ isCollapsed = IS_CHROME && hasShadowRoot(anchorNode) || IS_FIREFOX ? domRange.anchorNode === domRange.focusNode && domRange.anchorOffset === domRange.focusOffset : domRange.isCollapsed;
479
+ } else anchorNode = domRange.startContainer, anchorOffset = domRange.startOffset, focusNode = domRange.endContainer, focusOffset = domRange.endOffset, isCollapsed = domRange.collapsed;
480
+ }
479
481
  if (anchorNode == null || focusNode == null || anchorOffset == null || focusOffset == null) {
480
482
  if (suppressThrow) return null;
481
483
  throw Error(`Cannot map DOM range to an editor selection: ${domRange}`);
@@ -2863,12 +2865,14 @@ const RestoreDOM = IS_ANDROID ? RestoreDOMComponent : ({ children }) => /* @__PU
2863
2865
  exactMatch: !1,
2864
2866
  suppressThrow: !0
2865
2867
  });
2866
- if (range) if (!editor.composing && !androidInputManager?.hasPendingChanges() && !androidInputManager?.isFlushing()) {
2867
- if (editor.select(range), domSelection.isCollapsed && anchorNode && anchorNode.nodeType === Node.ELEMENT_NODE) try {
2868
- let canonicalRange = DOMEditor.toDOMRange(editor, range);
2869
- domSelection.setBaseAndExtent(canonicalRange.startContainer, canonicalRange.startOffset, canonicalRange.startContainer, canonicalRange.startOffset);
2870
- } catch {}
2871
- } else androidInputManager?.handleUserSelect(range);
2868
+ if (range) {
2869
+ if (!editor.composing && !androidInputManager?.hasPendingChanges() && !androidInputManager?.isFlushing()) {
2870
+ if (editor.select(range), domSelection.isCollapsed && anchorNode && anchorNode.nodeType === Node.ELEMENT_NODE) try {
2871
+ let canonicalRange = DOMEditor.toDOMRange(editor, range);
2872
+ domSelection.setBaseAndExtent(canonicalRange.startContainer, canonicalRange.startOffset, canonicalRange.startContainer, canonicalRange.startOffset);
2873
+ } catch {}
2874
+ } else androidInputManager?.handleUserSelect(range);
2875
+ }
2872
2876
  }
2873
2877
  readOnly && (!anchorNodeSelectable || !focusNodeInEditor) && deselect(editor);
2874
2878
  }
@@ -5644,11 +5648,7 @@ const PortableTextEditable = forwardRef(function PortableTextEditable(props, for
5644
5648
  }, $[105] = editorActor, $[106] = editorEngine, $[107] = onDragStart, $[108] = readOnly, $[109] = t28) : t28 = $[109];
5645
5649
  let handleDragStart = t28, t29;
5646
5650
  $[110] !== editorActor || $[111] !== editorEngine || $[112] !== onDrag ? (t29 = (event_9) => {
5647
- if (onDrag?.(event_9), !(event_9.isDefaultPrevented() || event_9.isPropagationStopped()) && getEventPosition({
5648
- editorActor,
5649
- editorEngine,
5650
- event: event_9.nativeEvent
5651
- })) return editorActor.send({
5651
+ if (onDrag?.(event_9), !(event_9.isDefaultPrevented() || event_9.isPropagationStopped()) && !(editorActor.getSnapshot().matches({ setup: "setting up" }) || !DOMEditor.hasTarget(editorEngine, event_9.target))) return editorActor.send({
5652
5652
  type: "behavior event",
5653
5653
  behaviorEvent: {
5654
5654
  type: "drag.drag",
@@ -5671,17 +5671,17 @@ const PortableTextEditable = forwardRef(function PortableTextEditable(props, for
5671
5671
  let handleDragEnd = t30, t31;
5672
5672
  $[118] !== editorActor || $[119] !== editorEngine || $[120] !== onDragEnter ? (t31 = (event_11) => {
5673
5673
  if (onDragEnter?.(event_11), event_11.isDefaultPrevented() || event_11.isPropagationStopped()) return;
5674
- let position_6 = getEventPosition({
5674
+ let position_5 = getEventPosition({
5675
5675
  editorActor,
5676
5676
  editorEngine,
5677
5677
  event: event_11.nativeEvent
5678
5678
  });
5679
- if (position_6) return editorActor.send({
5679
+ if (position_5) return editorActor.send({
5680
5680
  type: "behavior event",
5681
5681
  behaviorEvent: {
5682
5682
  type: "drag.dragenter",
5683
5683
  originEvent: { dataTransfer: event_11.dataTransfer },
5684
- position: position_6
5684
+ position: position_5
5685
5685
  },
5686
5686
  editor: editorEngine
5687
5687
  }), !0;
@@ -5689,18 +5689,18 @@ const PortableTextEditable = forwardRef(function PortableTextEditable(props, for
5689
5689
  let handleDragEnter = t31, t32;
5690
5690
  $[122] !== editorActor || $[123] !== editorEngine || $[124] !== onDragOver ? (t32 = (event_12) => {
5691
5691
  if (onDragOver?.(event_12), event_12.isDefaultPrevented() || event_12.isPropagationStopped()) return;
5692
- let position_7 = getEventPosition({
5692
+ let position_6 = getEventPosition({
5693
5693
  editorActor,
5694
5694
  editorEngine,
5695
5695
  event: event_12.nativeEvent
5696
5696
  });
5697
- if (position_7) return editorActor.send({
5697
+ if (position_6) return editorActor.send({
5698
5698
  type: "behavior event",
5699
5699
  behaviorEvent: {
5700
5700
  type: "drag.dragover",
5701
5701
  originEvent: { dataTransfer: event_12.dataTransfer },
5702
5702
  dragOrigin: editorActor.getSnapshot().context.internalDrag?.origin,
5703
- position: position_7
5703
+ position: position_6
5704
5704
  },
5705
5705
  editor: editorEngine,
5706
5706
  nativeEvent: event_12
@@ -5709,12 +5709,12 @@ const PortableTextEditable = forwardRef(function PortableTextEditable(props, for
5709
5709
  let handleDragOver = t32, t33;
5710
5710
  $[126] !== editorActor || $[127] !== editorEngine || $[128] !== onDrop ? (t33 = (event_13) => {
5711
5711
  if (onDrop?.(event_13), event_13.isDefaultPrevented() || event_13.isPropagationStopped()) return;
5712
- let position_8 = getEventPosition({
5712
+ let position_7 = getEventPosition({
5713
5713
  editorActor,
5714
5714
  editorEngine,
5715
5715
  event: event_13.nativeEvent
5716
5716
  });
5717
- if (!position_8) {
5717
+ if (!position_7) {
5718
5718
  console.warn("Could not find position for drop event");
5719
5719
  return;
5720
5720
  }
@@ -5724,7 +5724,7 @@ const PortableTextEditable = forwardRef(function PortableTextEditable(props, for
5724
5724
  type: "drag.drop",
5725
5725
  originEvent: { dataTransfer: event_13.dataTransfer },
5726
5726
  dragOrigin: editorActor.getSnapshot().context.internalDrag?.origin,
5727
- position: position_8
5727
+ position: position_7
5728
5728
  },
5729
5729
  editor: editorEngine,
5730
5730
  nativeEvent: event_13
@@ -5732,11 +5732,7 @@ const PortableTextEditable = forwardRef(function PortableTextEditable(props, for
5732
5732
  }, $[126] = editorActor, $[127] = editorEngine, $[128] = onDrop, $[129] = t33) : t33 = $[129];
5733
5733
  let handleDrop = t33, t34;
5734
5734
  $[130] !== editorActor || $[131] !== editorEngine || $[132] !== onDragLeave ? (t34 = (event_14) => {
5735
- if (onDragLeave?.(event_14), !(event_14.isDefaultPrevented() || event_14.isPropagationStopped()) && getEventPosition({
5736
- editorActor,
5737
- editorEngine,
5738
- event: event_14.nativeEvent
5739
- })) return editorActor.send({
5735
+ if (onDragLeave?.(event_14), !(event_14.isDefaultPrevented() || event_14.isPropagationStopped()) && !(editorActor.getSnapshot().matches({ setup: "setting up" }) || !DOMEditor.hasTarget(editorEngine, event_14.target))) return editorActor.send({
5740
5736
  type: "behavior event",
5741
5737
  behaviorEvent: {
5742
5738
  type: "drag.dragleave",
@@ -6945,14 +6941,15 @@ function applyOperation(editor, op) {
6945
6941
  typeof value == "object" && value && !Array.isArray(value) && modifyDescendant(editor, setNodePath, () => value), transformSelection = !0;
6946
6942
  break;
6947
6943
  }
6948
- if (getNode(editor.snapshot, setNodePath)) if (setPropertyPath.length === 1) {
6949
- let propertyName = setPropertyPath[0];
6950
- modifyDescendant(editor, setNodePath, (node) => ({
6951
- ...node,
6952
- [propertyName]: value
6953
- }));
6954
- } else modifyDescendant(editor, setNodePath, (node) => deepSet(node, setPropertyPath, value));
6955
- else {
6944
+ if (getNode(editor.snapshot, setNodePath)) {
6945
+ if (setPropertyPath.length === 1) {
6946
+ let propertyName = setPropertyPath[0];
6947
+ modifyDescendant(editor, setNodePath, (node) => ({
6948
+ ...node,
6949
+ [propertyName]: value
6950
+ }));
6951
+ } else modifyDescendant(editor, setNodePath, (node) => deepSet(node, setPropertyPath, value));
6952
+ } else {
6956
6953
  let blockSegment = findBlockSegment(path);
6957
6954
  if (!blockSegment) break;
6958
6955
  let blockIndex = resolveBlockIndex(editor, blockSegment);
@@ -7042,14 +7039,15 @@ function applyOperation(editor, op) {
7042
7039
  value: previousValue
7043
7040
  };
7044
7041
  }
7045
- if (getNode(editor.snapshot, unsetNodePath)) if (unsetPropertyPath.length === 1) {
7046
- let propertyName = unsetPropertyPath[0];
7047
- modifyDescendant(editor, unsetNodePath, (node) => {
7048
- let newNode = { ...node };
7049
- return delete newNode[propertyName], newNode;
7050
- });
7051
- } else modifyDescendant(editor, unsetNodePath, (node) => deepUnset(node, unsetPropertyPath));
7052
- else {
7042
+ if (getNode(editor.snapshot, unsetNodePath)) {
7043
+ if (unsetPropertyPath.length === 1) {
7044
+ let propertyName = unsetPropertyPath[0];
7045
+ modifyDescendant(editor, unsetNodePath, (node) => {
7046
+ let newNode = { ...node };
7047
+ return delete newNode[propertyName], newNode;
7048
+ });
7049
+ } else modifyDescendant(editor, unsetNodePath, (node) => deepUnset(node, unsetPropertyPath));
7050
+ } else {
7053
7051
  let blockSegment = findBlockSegment(path);
7054
7052
  if (!blockSegment) break;
7055
7053
  let blockIndex = resolveBlockIndex(editor, blockSegment);
@@ -7253,23 +7251,25 @@ const apply$1 = (editor, op) => {
7253
7251
  for (let ref of editor.pathRefs) PathRef.transform(ref, op);
7254
7252
  for (let ref of editor.pointRefs) PointRef.transform(ref, op);
7255
7253
  for (let ref of editor.rangeRefs) transformRangeRef(ref, op, editor.snapshot.context);
7256
- if (applyOperation(editor, op), updateDirtyPaths(editor, getDirtyPaths(editor.snapshot.context, op)), editor.operations.push(op), normalize(editor, { operation: op }), op.type === "set.selection") if (op.properties && op.newProperties && op.properties.anchor && op.properties.focus && op.newProperties.anchor && op.newProperties.focus) {
7257
- let previousSelectionIsCollapsed = isCollapsedRange({
7258
- anchor: op.properties.anchor,
7259
- focus: op.properties.focus
7260
- }), newSelectionIsCollapsed = isCollapsedRange({
7261
- anchor: op.newProperties.anchor,
7262
- focus: op.newProperties.focus
7263
- });
7264
- if (previousSelectionIsCollapsed && newSelectionIsCollapsed) {
7265
- let focusSpan = getSpan(editor.snapshot, op.properties.focus.path)?.node, newFocusSpan = getSpan(editor.snapshot, op.newProperties.focus.path)?.node, sameParent = pathEquals(parentPath(op.properties.focus.path), parentPath(op.newProperties.focus.path)), movedToNextSpan = !1, movedToPreviousSpan = !1;
7266
- if (sameParent && focusSpan && newFocusSpan) {
7267
- let nextSibling = getSibling(editor.snapshot, op.properties.focus.path, { direction: "next" }), previousSibling = getSibling(editor.snapshot, op.properties.focus.path, { direction: "previous" });
7268
- movedToNextSpan = nextSibling !== void 0 && pathEquals(nextSibling.path, op.newProperties.focus.path) && focusSpan.text.length === op.properties.focus.offset && op.newProperties.focus.offset === 0, movedToPreviousSpan = previousSibling !== void 0 && pathEquals(previousSibling.path, op.newProperties.focus.path) && op.properties.focus.offset === 0 && newFocusSpan.text.length === op.newProperties.focus.offset;
7254
+ if (applyOperation(editor, op), updateDirtyPaths(editor, getDirtyPaths(editor.snapshot.context, op)), editor.operations.push(op), normalize(editor, { operation: op }), op.type === "set.selection") {
7255
+ if (op.properties && op.newProperties && op.properties.anchor && op.properties.focus && op.newProperties.anchor && op.newProperties.focus) {
7256
+ let previousSelectionIsCollapsed = isCollapsedRange({
7257
+ anchor: op.properties.anchor,
7258
+ focus: op.properties.focus
7259
+ }), newSelectionIsCollapsed = isCollapsedRange({
7260
+ anchor: op.newProperties.anchor,
7261
+ focus: op.newProperties.focus
7262
+ });
7263
+ if (previousSelectionIsCollapsed && newSelectionIsCollapsed) {
7264
+ let focusSpan = getSpan(editor.snapshot, op.properties.focus.path)?.node, newFocusSpan = getSpan(editor.snapshot, op.newProperties.focus.path)?.node, sameParent = pathEquals(parentPath(op.properties.focus.path), parentPath(op.newProperties.focus.path)), movedToNextSpan = !1, movedToPreviousSpan = !1;
7265
+ if (sameParent && focusSpan && newFocusSpan) {
7266
+ let nextSibling = getSibling(editor.snapshot, op.properties.focus.path, { direction: "next" }), previousSibling = getSibling(editor.snapshot, op.properties.focus.path, { direction: "previous" });
7267
+ movedToNextSpan = nextSibling !== void 0 && pathEquals(nextSibling.path, op.newProperties.focus.path) && focusSpan.text.length === op.properties.focus.offset && op.newProperties.focus.offset === 0, movedToPreviousSpan = previousSibling !== void 0 && pathEquals(previousSibling.path, op.newProperties.focus.path) && op.properties.focus.offset === 0 && newFocusSpan.text.length === op.newProperties.focus.offset;
7268
+ }
7269
+ !movedToNextSpan && !movedToPreviousSpan && (editor.snapshot.decoratorState = {});
7269
7270
  }
7270
- !movedToNextSpan && !movedToPreviousSpan && (editor.snapshot.decoratorState = {});
7271
- }
7272
- } else editor.snapshot.decoratorState = {};
7271
+ } else editor.snapshot.decoratorState = {};
7272
+ }
7273
7273
  editor.flushing || (editor.flushing = !0, Promise.resolve().then(() => {
7274
7274
  editor.flushing = !1, editor.onChange({ operation: op }), editor.operations = [];
7275
7275
  })), operationEvent && emitOperationEvent(editor.operationListeners.after, operationEvent);
@@ -7677,30 +7677,32 @@ function applyNodeProperties(editor, props, path) {
7677
7677
  let nodeRecord = result.entry.node, propsRecord = props, keys = Object.keys(propsRecord), keyIndex = keys.indexOf("_key");
7678
7678
  keyIndex !== -1 && (keys.splice(keyIndex, 1), keys.unshift("_key"));
7679
7679
  let currentPath = path;
7680
- for (let key of keys) if (propsRecord[key] !== nodeRecord[key]) if (propsRecord[key] != null) {
7681
- let hadProperty = nodeRecord.hasOwnProperty(key), lastSegment = currentPath[currentPath.length - 1], renamedPath = key === "_key" && typeof propsRecord[key] == "string" && isKeyedSegment(lastSegment) ? [...currentPath.slice(0, -1), { _key: propsRecord[key] }] : void 0, inversePath = renamedPath ? [...renamedPath, key] : [...currentPath, key];
7682
- editor.apply({
7683
- type: "set",
7680
+ for (let key of keys) if (propsRecord[key] !== nodeRecord[key]) {
7681
+ if (propsRecord[key] != null) {
7682
+ let hadProperty = nodeRecord.hasOwnProperty(key), lastSegment = currentPath[currentPath.length - 1], renamedPath = key === "_key" && typeof propsRecord[key] == "string" && isKeyedSegment(lastSegment) ? [...currentPath.slice(0, -1), { _key: propsRecord[key] }] : void 0, inversePath = renamedPath ? [...renamedPath, key] : [...currentPath, key];
7683
+ editor.apply({
7684
+ type: "set",
7685
+ path: [...currentPath, key],
7686
+ value: propsRecord[key],
7687
+ inverse: hadProperty ? {
7688
+ type: "set",
7689
+ path: inversePath,
7690
+ value: nodeRecord[key]
7691
+ } : {
7692
+ type: "unset",
7693
+ path: inversePath
7694
+ }
7695
+ }), renamedPath && (currentPath = renamedPath);
7696
+ } else nodeRecord.hasOwnProperty(key) && editor.apply({
7697
+ type: "unset",
7684
7698
  path: [...currentPath, key],
7685
- value: propsRecord[key],
7686
- inverse: hadProperty ? {
7699
+ inverse: {
7687
7700
  type: "set",
7688
- path: inversePath,
7701
+ path: [...currentPath, key],
7689
7702
  value: nodeRecord[key]
7690
- } : {
7691
- type: "unset",
7692
- path: inversePath
7693
7703
  }
7694
- }), renamedPath && (currentPath = renamedPath);
7695
- } else nodeRecord.hasOwnProperty(key) && editor.apply({
7696
- type: "unset",
7697
- path: [...currentPath, key],
7698
- inverse: {
7699
- type: "set",
7700
- path: [...currentPath, key],
7701
- value: nodeRecord[key]
7702
- }
7703
- });
7704
+ });
7705
+ }
7704
7706
  }
7705
7707
  /**
7706
7708
  * Create a placeholder text block -- the empty initial block used when the
@@ -7819,7 +7821,7 @@ function richDescentToParent(input, path) {
7819
7821
  * 3. **Global fallback with position-validity.** Falls back to the
7820
7822
  * top-level rich `containers` map keyed by `_type`. Activates only
7821
7823
  * when schema-at-position permits the registered `arrayField`
7822
- * (for containers) or simply declares `_type` (for leaves).
7824
+ * (for containers) or declares `_type` (for leaves).
7823
7825
  * Registration is type-keyed; activation is position-gated.
7824
7826
  */
7825
7827
  function resolveContainerByPath(input, path, node) {
@@ -8759,8 +8761,10 @@ function bitap(text, pattern, loc, opts = {}) {
8759
8761
  let charMatch = s[text.charAt(j - 1)];
8760
8762
  if (d === 0 ? rd[j] = (rd[j + 1] << 1 | 1) & charMatch : rd[j] = (rd[j + 1] << 1 | 1) & charMatch | (lastRd[j + 1] | lastRd[j]) << 1 | 1 | lastRd[j + 1], rd[j] & matchmask) {
8761
8763
  let score = getBitapScore(d, j - 1);
8762
- if (score <= scoreThreshold) if (scoreThreshold = score, bestLoc = j - 1, bestLoc > loc) start = Math.max(1, 2 * loc - bestLoc);
8763
- else break;
8764
+ if (score <= scoreThreshold) {
8765
+ if (scoreThreshold = score, bestLoc = j - 1, bestLoc > loc) start = Math.max(1, 2 * loc - bestLoc);
8766
+ else break;
8767
+ }
8764
8768
  }
8765
8769
  }
8766
8770
  if (getBitapScore(d + 1, loc) > scoreThreshold) break;
@@ -10387,9 +10391,9 @@ function isBlockLike(value) {
10387
10391
  * drag origin in a way that would make the drop a no-op.
10388
10392
  *
10389
10393
  * Two expanded selections that only touch at a single endpoint are not treated
10390
- * as a self-drop the drop position covers an adjacent block (typical when
10394
+ * as a self-drop: the drop position covers an adjacent block (typical when
10391
10395
  * dragging a block-object onto the next block via the expanded fallback in
10392
- * `getEventPosition`), and the user genuinely wants the drop to happen.
10396
+ * `getEventPosition`), so the drop should go through.
10393
10397
  *
10394
10398
  * Chrome drags emit a collapsed selection pointing AT the dragged container.
10395
10399
  * A drop position whose path descends into that container is dropping the
@@ -11222,59 +11226,65 @@ function isBeforePath(path, another, root) {
11222
11226
  }
11223
11227
  const removeAnnotationOperationImplementation = ({ snapshot, operation }) => {
11224
11228
  let editor = operation.editor, at = operation.at ? resolveSelection(operation.editor, operation.at) : null, effectiveSelection = at ?? editor.snapshot.context.selection;
11225
- if (effectiveSelection) if (isCollapsedRange(effectiveSelection)) {
11226
- let blockEntry = getParent(snapshot, effectiveSelection.focus.path, { match: (node) => isTextBlock({ schema: editor.snapshot.context.schema }, node) });
11227
- if (!blockEntry) return;
11228
- let { node: block, path: blockPath } = blockEntry, potentialAnnotations = (block.markDefs ?? []).filter((markDef) => markDef._type === operation.annotation.name), selectedChildEntry = getNode(editor.snapshot, effectiveSelection.focus.path);
11229
- if (!selectedChildEntry) return;
11230
- let { node: selectedChild, path: selectedChildPath } = selectedChildEntry;
11231
- if (!isSpan({ schema: editor.snapshot.context.schema }, selectedChild)) return;
11232
- let annotationToRemove = selectedChild.marks?.find((mark) => potentialAnnotations.some((markDef) => markDef._key === mark));
11233
- if (!annotationToRemove) return;
11234
- let previousSpansWithSameAnnotation = [], reversedChildren = getChildren(editor.snapshot, blockPath);
11235
- for (let index = reversedChildren.length - 1; index >= 0; index--) {
11236
- let entry = reversedChildren[index];
11237
- if (!entry) continue;
11238
- let { node: child, path: childPath } = entry;
11239
- if (isSpan({ schema: editor.snapshot.context.schema }, child) && isBeforePath(childPath, selectedChildPath, editor.snapshot.context)) if (child.marks?.includes(annotationToRemove)) previousSpansWithSameAnnotation.push([child, childPath]);
11240
- else break;
11241
- }
11242
- let nextSpansWithSameAnnotation = [];
11243
- for (let { node: child, path: childPath } of getChildren(editor.snapshot, blockPath)) if (isSpan({ schema: editor.snapshot.context.schema }, child) && isAfterPath(childPath, selectedChildPath, editor.snapshot.context)) if (child.marks?.includes(annotationToRemove)) nextSpansWithSameAnnotation.push([child, childPath]);
11244
- else break;
11245
- for (let [child, childPath] of [
11246
- ...previousSpansWithSameAnnotation,
11247
- [selectedChild, selectedChildPath],
11248
- ...nextSpansWithSameAnnotation
11249
- ]) applyNodeProperties(editor, { marks: child.marks?.filter((mark) => mark !== annotationToRemove) }, childPath);
11250
- } else {
11251
- let ref = at ? rangeRef(editor, at, { affinity: "inward" }) : null;
11252
- withoutNormalizing(editor, () => {
11253
- let splitRange = at ?? editor.snapshot.context.selection;
11254
- if (splitRange && isRange(splitRange)) {
11255
- let splitLeaf = getNode(editor.snapshot, splitRange.anchor.path)?.node;
11256
- if (!(splitLeaf && isCollapsedRange(splitRange) && isSpan({ schema: editor.snapshot.context.schema }, splitLeaf) && splitLeaf.text.length > 0)) {
11257
- let splitRangeRef = rangeRef(editor, splitRange, { affinity: "inward" }), [splitStart, splitEnd] = rangeEdges(splitRange, editor.snapshot.context);
11258
- (!isEnd(editor, splitEnd, splitEnd.path) || !isEdge(editor, splitEnd, splitEnd.path)) && applySplitNode(editor, splitEnd.path, splitEnd.offset), (!isStart(editor, splitStart, splitStart.path) || !isEdge(editor, splitStart, splitStart.path)) && applySplitNode(editor, splitStart.path, splitStart.offset);
11259
- let updatedSplitRange = splitRangeRef.unref();
11260
- !at && updatedSplitRange && applySelect(editor, updatedSplitRange);
11229
+ if (effectiveSelection) {
11230
+ if (isCollapsedRange(effectiveSelection)) {
11231
+ let blockEntry = getParent(snapshot, effectiveSelection.focus.path, { match: (node) => isTextBlock({ schema: editor.snapshot.context.schema }, node) });
11232
+ if (!blockEntry) return;
11233
+ let { node: block, path: blockPath } = blockEntry, potentialAnnotations = (block.markDefs ?? []).filter((markDef) => markDef._type === operation.annotation.name), selectedChildEntry = getNode(editor.snapshot, effectiveSelection.focus.path);
11234
+ if (!selectedChildEntry) return;
11235
+ let { node: selectedChild, path: selectedChildPath } = selectedChildEntry;
11236
+ if (!isSpan({ schema: editor.snapshot.context.schema }, selectedChild)) return;
11237
+ let annotationToRemove = selectedChild.marks?.find((mark) => potentialAnnotations.some((markDef) => markDef._key === mark));
11238
+ if (!annotationToRemove) return;
11239
+ let previousSpansWithSameAnnotation = [], reversedChildren = getChildren(editor.snapshot, blockPath);
11240
+ for (let index = reversedChildren.length - 1; index >= 0; index--) {
11241
+ let entry = reversedChildren[index];
11242
+ if (!entry) continue;
11243
+ let { node: child, path: childPath } = entry;
11244
+ if (isSpan({ schema: editor.snapshot.context.schema }, child) && isBeforePath(childPath, selectedChildPath, editor.snapshot.context)) {
11245
+ if (child.marks?.includes(annotationToRemove)) previousSpansWithSameAnnotation.push([child, childPath]);
11246
+ else break;
11261
11247
  }
11262
11248
  }
11263
- let blocks = Array.from(getNodes(editor.snapshot, {
11264
- from: rangeStart(effectiveSelection, editor.snapshot.context).path,
11265
- to: rangeEnd(effectiveSelection, editor.snapshot.context).path,
11266
- match: (node) => isTextBlock({ schema: editor.snapshot.context.schema }, node)
11267
- })), selectionRange = ref?.current ?? editor.snapshot.context.selection;
11268
- for (let { node: block, path: blockPath } of blocks) {
11269
- if (!isTextBlock({ schema: editor.snapshot.context.schema }, block)) continue;
11270
- let children = getChildren(editor.snapshot, blockPath);
11271
- for (let { node: child, path: childPath } of children) {
11272
- if (!isSpan({ schema: editor.snapshot.context.schema }, child) || !selectionRange || !rangeIntersects(editor.snapshot, selectionRange, childPath)) continue;
11273
- let markDefs = block.markDefs ?? [], marks = child.marks ?? [], marksWithoutAnnotation = marks.filter((mark) => markDefs.find((markDef) => markDef._key === mark)?._type !== operation.annotation.name);
11274
- marksWithoutAnnotation.length !== marks.length && applyNodeProperties(editor, { marks: marksWithoutAnnotation }, childPath);
11275
- }
11249
+ let nextSpansWithSameAnnotation = [];
11250
+ for (let { node: child, path: childPath } of getChildren(editor.snapshot, blockPath)) if (isSpan({ schema: editor.snapshot.context.schema }, child) && isAfterPath(childPath, selectedChildPath, editor.snapshot.context)) {
11251
+ if (child.marks?.includes(annotationToRemove)) nextSpansWithSameAnnotation.push([child, childPath]);
11252
+ else break;
11276
11253
  }
11277
- }), ref?.unref();
11254
+ for (let [child, childPath] of [
11255
+ ...previousSpansWithSameAnnotation,
11256
+ [selectedChild, selectedChildPath],
11257
+ ...nextSpansWithSameAnnotation
11258
+ ]) applyNodeProperties(editor, { marks: child.marks?.filter((mark) => mark !== annotationToRemove) }, childPath);
11259
+ } else {
11260
+ let ref = at ? rangeRef(editor, at, { affinity: "inward" }) : null;
11261
+ withoutNormalizing(editor, () => {
11262
+ let splitRange = at ?? editor.snapshot.context.selection;
11263
+ if (splitRange && isRange(splitRange)) {
11264
+ let splitLeaf = getNode(editor.snapshot, splitRange.anchor.path)?.node;
11265
+ if (!(splitLeaf && isCollapsedRange(splitRange) && isSpan({ schema: editor.snapshot.context.schema }, splitLeaf) && splitLeaf.text.length > 0)) {
11266
+ let splitRangeRef = rangeRef(editor, splitRange, { affinity: "inward" }), [splitStart, splitEnd] = rangeEdges(splitRange, editor.snapshot.context);
11267
+ (!isEnd(editor, splitEnd, splitEnd.path) || !isEdge(editor, splitEnd, splitEnd.path)) && applySplitNode(editor, splitEnd.path, splitEnd.offset), (!isStart(editor, splitStart, splitStart.path) || !isEdge(editor, splitStart, splitStart.path)) && applySplitNode(editor, splitStart.path, splitStart.offset);
11268
+ let updatedSplitRange = splitRangeRef.unref();
11269
+ !at && updatedSplitRange && applySelect(editor, updatedSplitRange);
11270
+ }
11271
+ }
11272
+ let blocks = Array.from(getNodes(editor.snapshot, {
11273
+ from: rangeStart(effectiveSelection, editor.snapshot.context).path,
11274
+ to: rangeEnd(effectiveSelection, editor.snapshot.context).path,
11275
+ match: (node) => isTextBlock({ schema: editor.snapshot.context.schema }, node)
11276
+ })), selectionRange = ref?.current ?? editor.snapshot.context.selection;
11277
+ for (let { node: block, path: blockPath } of blocks) {
11278
+ if (!isTextBlock({ schema: editor.snapshot.context.schema }, block)) continue;
11279
+ let children = getChildren(editor.snapshot, blockPath);
11280
+ for (let { node: child, path: childPath } of children) {
11281
+ if (!isSpan({ schema: editor.snapshot.context.schema }, child) || !selectionRange || !rangeIntersects(editor.snapshot, selectionRange, childPath)) continue;
11282
+ let markDefs = block.markDefs ?? [], marks = child.marks ?? [], marksWithoutAnnotation = marks.filter((mark) => markDefs.find((markDef) => markDef._key === mark)?._type !== operation.annotation.name);
11283
+ marksWithoutAnnotation.length !== marks.length && applyNodeProperties(editor, { marks: marksWithoutAnnotation }, childPath);
11284
+ }
11285
+ }
11286
+ }), ref?.unref();
11287
+ }
11278
11288
  }
11279
11289
  };
11280
11290
  /**
@@ -13014,12 +13024,13 @@ const insertChildOperationImplementation = ({ snapshot, operation }) => {
13014
13024
  let node = nodeEntry.node;
13015
13025
  if (isLeafObject(editor.snapshot, node, nodeEntry.path)) {
13016
13026
  let nextSibling = getSibling(editor.snapshot, nodeEntry.path, { direction: "next" });
13017
- if (nextSibling) if (getSpan(editor.snapshot, nextSibling.path)) path = nextSibling.path, offset = 0, applySelect(editor, {
13018
- path,
13019
- offset
13020
- });
13021
- else return;
13022
- else return;
13027
+ if (nextSibling) {
13028
+ if (getSpan(editor.snapshot, nextSibling.path)) path = nextSibling.path, offset = 0, applySelect(editor, {
13029
+ path,
13030
+ offset
13031
+ });
13032
+ else return;
13033
+ } else return;
13023
13034
  }
13024
13035
  operation.text.length > 0 && editor.apply({
13025
13036
  type: "insert.text",
@@ -16142,49 +16153,51 @@ async function updateValue({ context, sendBack, editorEngine, streamBlocks, valu
16142
16153
  if ((!value || value.length === 0) && (clearEditor({
16143
16154
  editorEngine,
16144
16155
  doneSyncing
16145
- }), isChanged = !0), value && value.length > 0) if (streamBlocks) await new Promise((resolve) => {
16146
- if (doneSyncing) {
16147
- resolve();
16148
- return;
16149
- }
16150
- isChanged = removeExtraBlocks({
16151
- editorEngine,
16152
- value
16156
+ }), isChanged = !0), value && value.length > 0) {
16157
+ if (streamBlocks) await new Promise((resolve) => {
16158
+ if (doneSyncing) {
16159
+ resolve();
16160
+ return;
16161
+ }
16162
+ isChanged = removeExtraBlocks({
16163
+ editorEngine,
16164
+ value
16165
+ });
16166
+ let processBlocks = async () => {
16167
+ for await (let [currentBlock, currentBlockIndex] of getStreamedBlocks({ value })) {
16168
+ let { blockChanged, blockValid } = syncBlock({
16169
+ context,
16170
+ sendBack,
16171
+ block: currentBlock,
16172
+ index: currentBlockIndex,
16173
+ editorEngine,
16174
+ value
16175
+ });
16176
+ if (isChanged = blockChanged || isChanged, isValid &&= blockValid, !isValid) break;
16177
+ }
16178
+ resolve();
16179
+ };
16180
+ processBlocks();
16153
16181
  });
16154
- let processBlocks = async () => {
16155
- for await (let [currentBlock, currentBlockIndex] of getStreamedBlocks({ value })) {
16182
+ else {
16183
+ if (doneSyncing) return;
16184
+ isChanged = removeExtraBlocks({
16185
+ editorEngine,
16186
+ value
16187
+ });
16188
+ let index = 0;
16189
+ for (let block of value) {
16156
16190
  let { blockChanged, blockValid } = syncBlock({
16157
16191
  context,
16158
16192
  sendBack,
16159
- block: currentBlock,
16160
- index: currentBlockIndex,
16193
+ block,
16194
+ index,
16161
16195
  editorEngine,
16162
16196
  value
16163
16197
  });
16164
- if (isChanged = blockChanged || isChanged, isValid &&= blockValid, !isValid) break;
16198
+ if (isChanged = blockChanged || isChanged, isValid &&= blockValid, !blockValid) break;
16199
+ index++;
16165
16200
  }
16166
- resolve();
16167
- };
16168
- processBlocks();
16169
- });
16170
- else {
16171
- if (doneSyncing) return;
16172
- isChanged = removeExtraBlocks({
16173
- editorEngine,
16174
- value
16175
- });
16176
- let index = 0;
16177
- for (let block of value) {
16178
- let { blockChanged, blockValid } = syncBlock({
16179
- context,
16180
- sendBack,
16181
- block,
16182
- index,
16183
- editorEngine,
16184
- value
16185
- });
16186
- if (isChanged = blockChanged || isChanged, isValid &&= blockValid, !blockValid) break;
16187
- index++;
16188
16201
  }
16189
16202
  }
16190
16203
  if (!isValid) {