lexical 0.11.0 → 0.11.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/Lexical.dev.js CHANGED
@@ -428,6 +428,102 @@ function initMutationObserver(editor) {
428
428
  });
429
429
  }
430
430
 
431
+ /**
432
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
433
+ *
434
+ * This source code is licensed under the MIT license found in the
435
+ * LICENSE file in the root directory of this source tree.
436
+ *
437
+ */
438
+
439
+ function $canSimpleTextNodesBeMerged(node1, node2) {
440
+ const node1Mode = node1.__mode;
441
+ const node1Format = node1.__format;
442
+ const node1Style = node1.__style;
443
+ const node2Mode = node2.__mode;
444
+ const node2Format = node2.__format;
445
+ const node2Style = node2.__style;
446
+ return (node1Mode === null || node1Mode === node2Mode) && (node1Format === null || node1Format === node2Format) && (node1Style === null || node1Style === node2Style);
447
+ }
448
+
449
+ function $mergeTextNodes(node1, node2) {
450
+ const writableNode1 = node1.mergeWithSibling(node2);
451
+
452
+ const normalizedNodes = getActiveEditor()._normalizedNodes;
453
+
454
+ normalizedNodes.add(node1.__key);
455
+ normalizedNodes.add(node2.__key);
456
+ return writableNode1;
457
+ }
458
+
459
+ function $normalizeTextNode(textNode) {
460
+ let node = textNode;
461
+
462
+ if (node.__text === '' && node.isSimpleText() && !node.isUnmergeable()) {
463
+ node.remove();
464
+ return;
465
+ } // Backward
466
+
467
+
468
+ let previousNode;
469
+
470
+ while ((previousNode = node.getPreviousSibling()) !== null && $isTextNode(previousNode) && previousNode.isSimpleText() && !previousNode.isUnmergeable()) {
471
+ if (previousNode.__text === '') {
472
+ previousNode.remove();
473
+ } else if ($canSimpleTextNodesBeMerged(previousNode, node)) {
474
+ node = $mergeTextNodes(previousNode, node);
475
+ break;
476
+ } else {
477
+ break;
478
+ }
479
+ } // Forward
480
+
481
+
482
+ let nextNode;
483
+
484
+ while ((nextNode = node.getNextSibling()) !== null && $isTextNode(nextNode) && nextNode.isSimpleText() && !nextNode.isUnmergeable()) {
485
+ if (nextNode.__text === '') {
486
+ nextNode.remove();
487
+ } else if ($canSimpleTextNodesBeMerged(node, nextNode)) {
488
+ node = $mergeTextNodes(node, nextNode);
489
+ break;
490
+ } else {
491
+ break;
492
+ }
493
+ }
494
+ }
495
+ function $normalizeSelection(selection) {
496
+ $normalizePoint(selection.anchor);
497
+ $normalizePoint(selection.focus);
498
+ return selection;
499
+ }
500
+
501
+ function $normalizePoint(point) {
502
+ while (point.type === 'element') {
503
+ const node = point.getNode();
504
+ const offset = point.offset;
505
+ let nextNode;
506
+ let nextOffsetAtEnd;
507
+
508
+ if (offset === node.getChildrenSize()) {
509
+ nextNode = node.getChildAtIndex(offset - 1);
510
+ nextOffsetAtEnd = true;
511
+ } else {
512
+ nextNode = node.getChildAtIndex(offset);
513
+ nextOffsetAtEnd = false;
514
+ }
515
+
516
+ if ($isTextNode(nextNode)) {
517
+ point.set(nextNode.__key, nextOffsetAtEnd ? nextNode.getTextContentSize() : 0, 'text');
518
+ break;
519
+ } else if (!$isElementNode(nextNode)) {
520
+ break;
521
+ }
522
+
523
+ point.set(nextNode.__key, nextOffsetAtEnd ? nextNode.getChildrenSize() : 0, 'element');
524
+ }
525
+ }
526
+
431
527
  /**
432
528
  * Copyright (c) Meta Platforms, Inc. and affiliates.
433
529
  *
@@ -948,13 +1044,13 @@ function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focu
948
1044
 
949
1045
  const parent = node.getParent();
950
1046
  const prevSelection = $getPreviousSelection();
1047
+ const prevTextContentSize = node.getTextContentSize();
951
1048
  const compositionKey = $getCompositionKey();
952
1049
  const nodeKey = node.getKey();
953
1050
 
954
- if (node.isToken() || compositionKey !== null && nodeKey === compositionKey && !isComposing || // Check if character was added at the start, and we need
955
- // to clear this input from occurring as that action wasn't
956
- // permitted.
957
- parent !== null && $isRangeSelection(prevSelection) && !parent.canInsertTextBefore() && prevSelection.anchor.offset === 0) {
1051
+ if (node.isToken() || compositionKey !== null && nodeKey === compositionKey && !isComposing || // Check if character was added at the start or boundaries when not insertable, and we need
1052
+ // to clear this input from occurring as that action wasn't permitted.
1053
+ $isRangeSelection(prevSelection) && (parent !== null && !parent.canInsertTextBefore() && prevSelection.anchor.offset === 0 || prevSelection.anchor.key === textNode.__key && prevSelection.anchor.offset === 0 && !node.canInsertTextBefore() || prevSelection.focus.key === textNode.__key && prevSelection.focus.offset === prevTextContentSize && !node.canInsertTextAfter())) {
958
1054
  node.markDirty();
959
1055
  return;
960
1056
  }
@@ -1169,7 +1265,23 @@ function isDelete(keyCode) {
1169
1265
  function isSelectAll(keyCode, metaKey, ctrlKey) {
1170
1266
  return keyCode === 65 && controlOrMeta(metaKey, ctrlKey);
1171
1267
  }
1268
+ function $selectAll() {
1269
+ const root = $getRoot();
1270
+ const selection = root.select(0, root.getChildrenSize());
1271
+ $setSelection($normalizeSelection(selection));
1272
+ }
1172
1273
  function getCachedClassNameArray(classNamesTheme, classNameThemeType) {
1274
+ if (classNamesTheme.__lexicalClassNameCache === undefined) {
1275
+ classNamesTheme.__lexicalClassNameCache = {};
1276
+ }
1277
+
1278
+ const classNamesCache = classNamesTheme.__lexicalClassNameCache;
1279
+ const cachedClassNames = classNamesCache[classNameThemeType];
1280
+
1281
+ if (cachedClassNames !== undefined) {
1282
+ return cachedClassNames;
1283
+ }
1284
+
1173
1285
  const classNames = classNamesTheme[classNameThemeType]; // As we're using classList, we need
1174
1286
  // to handle className tokens that have spaces.
1175
1287
  // The easiest way to do this to convert the
@@ -1178,7 +1290,7 @@ function getCachedClassNameArray(classNamesTheme, classNameThemeType) {
1178
1290
 
1179
1291
  if (typeof classNames === 'string') {
1180
1292
  const classNamesArr = classNames.split(' ');
1181
- classNamesTheme[classNameThemeType] = classNamesArr;
1293
+ classNamesCache[classNameThemeType] = classNamesArr;
1182
1294
  return classNamesArr;
1183
1295
  }
1184
1296
 
@@ -1366,13 +1478,7 @@ function $addUpdateTag(tag) {
1366
1478
 
1367
1479
  editor._updateTags.add(tag);
1368
1480
  }
1369
- function $maybeMoveChildrenSelectionToParent(parentNode, offset = 0) {
1370
- if (offset !== 0) {
1371
- {
1372
- throw Error(`TODO`);
1373
- }
1374
- }
1375
-
1481
+ function $maybeMoveChildrenSelectionToParent(parentNode) {
1376
1482
  const selection = $getSelection();
1377
1483
 
1378
1484
  if (!$isRangeSelection(selection) || !$isElementNode(parentNode)) {
@@ -1741,102 +1847,6 @@ function $garbageCollectDetachedNodes(prevEditorState, editorState, dirtyLeaves,
1741
1847
  }
1742
1848
  }
1743
1849
 
1744
- /**
1745
- * Copyright (c) Meta Platforms, Inc. and affiliates.
1746
- *
1747
- * This source code is licensed under the MIT license found in the
1748
- * LICENSE file in the root directory of this source tree.
1749
- *
1750
- */
1751
-
1752
- function $canSimpleTextNodesBeMerged(node1, node2) {
1753
- const node1Mode = node1.__mode;
1754
- const node1Format = node1.__format;
1755
- const node1Style = node1.__style;
1756
- const node2Mode = node2.__mode;
1757
- const node2Format = node2.__format;
1758
- const node2Style = node2.__style;
1759
- return (node1Mode === null || node1Mode === node2Mode) && (node1Format === null || node1Format === node2Format) && (node1Style === null || node1Style === node2Style);
1760
- }
1761
-
1762
- function $mergeTextNodes(node1, node2) {
1763
- const writableNode1 = node1.mergeWithSibling(node2);
1764
-
1765
- const normalizedNodes = getActiveEditor()._normalizedNodes;
1766
-
1767
- normalizedNodes.add(node1.__key);
1768
- normalizedNodes.add(node2.__key);
1769
- return writableNode1;
1770
- }
1771
-
1772
- function $normalizeTextNode(textNode) {
1773
- let node = textNode;
1774
-
1775
- if (node.__text === '' && node.isSimpleText() && !node.isUnmergeable()) {
1776
- node.remove();
1777
- return;
1778
- } // Backward
1779
-
1780
-
1781
- let previousNode;
1782
-
1783
- while ((previousNode = node.getPreviousSibling()) !== null && $isTextNode(previousNode) && previousNode.isSimpleText() && !previousNode.isUnmergeable()) {
1784
- if (previousNode.__text === '') {
1785
- previousNode.remove();
1786
- } else if ($canSimpleTextNodesBeMerged(previousNode, node)) {
1787
- node = $mergeTextNodes(previousNode, node);
1788
- break;
1789
- } else {
1790
- break;
1791
- }
1792
- } // Forward
1793
-
1794
-
1795
- let nextNode;
1796
-
1797
- while ((nextNode = node.getNextSibling()) !== null && $isTextNode(nextNode) && nextNode.isSimpleText() && !nextNode.isUnmergeable()) {
1798
- if (nextNode.__text === '') {
1799
- nextNode.remove();
1800
- } else if ($canSimpleTextNodesBeMerged(node, nextNode)) {
1801
- node = $mergeTextNodes(node, nextNode);
1802
- break;
1803
- } else {
1804
- break;
1805
- }
1806
- }
1807
- }
1808
- function $normalizeSelection(selection) {
1809
- $normalizePoint(selection.anchor);
1810
- $normalizePoint(selection.focus);
1811
- return selection;
1812
- }
1813
-
1814
- function $normalizePoint(point) {
1815
- while (point.type === 'element') {
1816
- const node = point.getNode();
1817
- const offset = point.offset;
1818
- let nextNode;
1819
- let nextOffsetAtEnd;
1820
-
1821
- if (offset === node.getChildrenSize()) {
1822
- nextNode = node.getChildAtIndex(offset - 1);
1823
- nextOffsetAtEnd = true;
1824
- } else {
1825
- nextNode = node.getChildAtIndex(offset);
1826
- nextOffsetAtEnd = false;
1827
- }
1828
-
1829
- if ($isTextNode(nextNode)) {
1830
- point.set(nextNode.__key, nextOffsetAtEnd ? nextNode.getTextContentSize() : 0, 'text');
1831
- break;
1832
- } else if (!$isElementNode(nextNode)) {
1833
- break;
1834
- }
1835
-
1836
- point.set(nextNode.__key, nextOffsetAtEnd ? nextNode.getChildrenSize() : 0, 'element');
1837
- }
1838
- }
1839
-
1840
1850
  /**
1841
1851
  * Copyright (c) Meta Platforms, Inc. and affiliates.
1842
1852
  *
@@ -2919,10 +2929,12 @@ function onBeforeInput(event, editor) {
2919
2929
  case 'insertParagraph':
2920
2930
  {
2921
2931
  // Used for Android
2922
- $setCompositionKey(null); // Some browsers do not provide the type "insertLineBreak".
2932
+ $setCompositionKey(null); // Safari does not provide the type "insertLineBreak".
2923
2933
  // So instead, we need to infer it from the keyboard event.
2934
+ // We do not apply this logic to iOS to allow newline auto-capitalization
2935
+ // work without creating linebreaks when pressing Enter
2924
2936
 
2925
- if (isInsertLineBreak) {
2937
+ if (isInsertLineBreak && !IS_IOS) {
2926
2938
  isInsertLineBreak = false;
2927
2939
  dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
2928
2940
  } else {
@@ -2942,7 +2954,7 @@ function onBeforeInput(event, editor) {
2942
2954
  case 'deleteByComposition':
2943
2955
  {
2944
2956
  if ($canRemoveText(anchorNode, focusNode)) {
2945
- dispatchCommand(editor, REMOVE_TEXT_COMMAND, undefined);
2957
+ dispatchCommand(editor, REMOVE_TEXT_COMMAND, event);
2946
2958
  }
2947
2959
 
2948
2960
  break;
@@ -2951,7 +2963,7 @@ function onBeforeInput(event, editor) {
2951
2963
  case 'deleteByDrag':
2952
2964
  case 'deleteByCut':
2953
2965
  {
2954
- dispatchCommand(editor, REMOVE_TEXT_COMMAND, undefined);
2966
+ dispatchCommand(editor, REMOVE_TEXT_COMMAND, event);
2955
2967
  break;
2956
2968
  }
2957
2969
 
@@ -3269,10 +3281,15 @@ function onKeyDown(event, editor) {
3269
3281
  } else if (isSelectAll(keyCode, metaKey, ctrlKey)) {
3270
3282
  event.preventDefault();
3271
3283
  editor.update(() => {
3272
- const root = $getRoot();
3273
- root.select(0, root.getChildrenSize());
3284
+ $selectAll();
3274
3285
  });
3275
- }
3286
+ } // FF does it well (no need to override behavior)
3287
+
3288
+ } else if (!IS_FIREFOX && isSelectAll(keyCode, metaKey, ctrlKey)) {
3289
+ event.preventDefault();
3290
+ editor.update(() => {
3291
+ $selectAll();
3292
+ });
3276
3293
  }
3277
3294
  }
3278
3295
 
@@ -3528,6 +3545,8 @@ function removeNode(nodeToRemove, restoreSelection, preserveEmptyParent) {
3528
3545
  moveSelectionPointToSibling(focus, nodeToRemove, parent, nodeToRemove.getPreviousSibling(), nodeToRemove.getNextSibling());
3529
3546
  selectionMoved = true;
3530
3547
  }
3548
+ } else if ($isNodeSelection(selection) && restoreSelection && nodeToRemove.isSelected()) {
3549
+ nodeToRemove.selectPrevious();
3531
3550
  }
3532
3551
 
3533
3552
  if ($isRangeSelection(selection) && restoreSelection && !selectionMoved) {
@@ -4854,64 +4873,152 @@ class TextNode extends LexicalNode {
4854
4873
  this.__mode = 0;
4855
4874
  this.__detail = 0;
4856
4875
  }
4876
+ /**
4877
+ * Returns a 32-bit integer that represents the TextFormatTypes currently applied to the
4878
+ * TextNode. You probably don't want to use this method directly - consider using TextNode.hasFormat instead.
4879
+ *
4880
+ * @returns a number representing the format of the text node.
4881
+ */
4882
+
4857
4883
 
4858
4884
  getFormat() {
4859
4885
  const self = this.getLatest();
4860
4886
  return self.__format;
4861
4887
  }
4888
+ /**
4889
+ * Returns a 32-bit integer that represents the TextDetailTypes currently applied to the
4890
+ * TextNode. You probably don't want to use this method directly - consider using TextNode.isDirectionless
4891
+ * or TextNode.isUnmergeable instead.
4892
+ *
4893
+ * @returns a number representing the detail of the text node.
4894
+ */
4895
+
4862
4896
 
4863
4897
  getDetail() {
4864
4898
  const self = this.getLatest();
4865
4899
  return self.__detail;
4866
4900
  }
4901
+ /**
4902
+ * Returns the mode (TextModeType) of the TextNode, which may be "normal", "token", or "segmented"
4903
+ *
4904
+ * @returns TextModeType.
4905
+ */
4906
+
4867
4907
 
4868
4908
  getMode() {
4869
4909
  const self = this.getLatest();
4870
4910
  return TEXT_TYPE_TO_MODE[self.__mode];
4871
4911
  }
4912
+ /**
4913
+ * Returns the styles currently applied to the node. This is analogous to CSSText in the DOM.
4914
+ *
4915
+ * @returns CSSText-like string of styles applied to the underlying DOM node.
4916
+ */
4917
+
4872
4918
 
4873
4919
  getStyle() {
4874
4920
  const self = this.getLatest();
4875
4921
  return self.__style;
4876
4922
  }
4923
+ /**
4924
+ * Returns whether or not the node is in "token" mode. TextNodes in token mode can be navigated through character-by-character
4925
+ * with a RangeSelection, but are deleted as a single entity (not invdividually by character).
4926
+ *
4927
+ * @returns true if the node is in token mode, false otherwise.
4928
+ */
4929
+
4877
4930
 
4878
4931
  isToken() {
4879
4932
  const self = this.getLatest();
4880
4933
  return self.__mode === IS_TOKEN;
4881
4934
  }
4935
+ /**
4936
+ *
4937
+ * @returns true if Lexical detects that an IME or other 3rd-party script is attempting to
4938
+ * mutate the TextNode, false otherwise.
4939
+ */
4940
+
4882
4941
 
4883
4942
  isComposing() {
4884
4943
  return this.__key === $getCompositionKey();
4885
4944
  }
4945
+ /**
4946
+ * Returns whether or not the node is in "segemented" mode. TextNodes in segemented mode can be navigated through character-by-character
4947
+ * with a RangeSelection, but are deleted in space-delimited "segments".
4948
+ *
4949
+ * @returns true if the node is in segmented mode, false otherwise.
4950
+ */
4951
+
4886
4952
 
4887
4953
  isSegmented() {
4888
4954
  const self = this.getLatest();
4889
4955
  return self.__mode === IS_SEGMENTED;
4890
4956
  }
4957
+ /**
4958
+ * Returns whether or not the node is "directionless". Directionless nodes don't respect changes between RTL and LTR modes.
4959
+ *
4960
+ * @returns true if the node is directionless, false otherwise.
4961
+ */
4962
+
4891
4963
 
4892
4964
  isDirectionless() {
4893
4965
  const self = this.getLatest();
4894
4966
  return (self.__detail & IS_DIRECTIONLESS) !== 0;
4895
4967
  }
4968
+ /**
4969
+ * Returns whether or not the node is unmergeable. In some scenarios, Lexical tries to merge
4970
+ * adjacent TextNodes into a single TextNode. If a TextNode is unmergeable, this won't happen.
4971
+ *
4972
+ * @returns true if the node is unmergeable, false otherwise.
4973
+ */
4974
+
4896
4975
 
4897
4976
  isUnmergeable() {
4898
4977
  const self = this.getLatest();
4899
4978
  return (self.__detail & IS_UNMERGEABLE) !== 0;
4900
4979
  }
4980
+ /**
4981
+ * Returns whether or not the node has the provided format applied. Use this with the human-readable TextFormatType
4982
+ * string values to get the format of a TextNode.
4983
+ *
4984
+ * @param type - the TextFormatType to check for.
4985
+ *
4986
+ * @returns true if the node has the provided format, false otherwise.
4987
+ */
4988
+
4901
4989
 
4902
4990
  hasFormat(type) {
4903
4991
  const formatFlag = TEXT_TYPE_TO_FORMAT[type];
4904
4992
  return (this.getFormat() & formatFlag) !== 0;
4905
4993
  }
4994
+ /**
4995
+ * Returns whether or not the node is simple text. Simple text is defined as a TextNode that has the string type "text"
4996
+ * (i.e., not a subclass) and has no mode applied to it (i.e., not segmented or token).
4997
+ *
4998
+ * @returns true if the node is simple text, false otherwise.
4999
+ */
5000
+
4906
5001
 
4907
5002
  isSimpleText() {
4908
5003
  return this.__type === 'text' && this.__mode === 0;
4909
5004
  }
5005
+ /**
5006
+ * Returns the text content of the node as a string.
5007
+ *
5008
+ * @returns a string representing the text content of the node.
5009
+ */
5010
+
4910
5011
 
4911
5012
  getTextContent() {
4912
5013
  const self = this.getLatest();
4913
5014
  return self.__text;
4914
5015
  }
5016
+ /**
5017
+ * Returns the format flags applied to the node as a 32-bit integer.
5018
+ *
5019
+ * @returns a number representing the TextFormatTypes applied to the node.
5020
+ */
5021
+
4915
5022
 
4916
5023
  getFormatFlags(type, alignWithFormat) {
4917
5024
  const self = this.getLatest();
@@ -5114,14 +5221,35 @@ class TextNode extends LexicalNode {
5114
5221
 
5115
5222
  selectionTransform(prevSelection, nextSelection) {
5116
5223
  return;
5117
- } // TODO 0.5 This should just be a `string`.
5224
+ }
5225
+ /**
5226
+ * Sets the node format to the provided TextFormatType or 32-bit integer. Note that the TextFormatType
5227
+ * version of the argument can only specify one format and doing so will remove all other formats that
5228
+ * may be applied to the node. For toggling behavior, consider using {@link TextNode.toggleFormat}
5229
+ *
5230
+ * @param format - TextFormatType or 32-bit integer representing the node format.
5231
+ *
5232
+ * @returns this TextNode.
5233
+ * // TODO 0.12 This should just be a `string`.
5234
+ */
5118
5235
 
5119
5236
 
5120
5237
  setFormat(format) {
5121
5238
  const self = this.getWritable();
5122
5239
  self.__format = typeof format === 'string' ? TEXT_TYPE_TO_FORMAT[format] : format;
5123
5240
  return self;
5124
- } // TODO 0.5 This should just be a `string`.
5241
+ }
5242
+ /**
5243
+ * Sets the node detail to the provided TextDetailType or 32-bit integer. Note that the TextDetailType
5244
+ * version of the argument can only specify one detail value and doing so will remove all other detail values that
5245
+ * may be applied to the node. For toggling behavior, consider using {@link TextNode.toggleDirectionless}
5246
+ * or {@link TextNode.togglerUnmergeable}
5247
+ *
5248
+ * @param detail - TextDetailType or 32-bit integer representing the node detail.
5249
+ *
5250
+ * @returns this TextNode.
5251
+ * // TODO 0.12 This should just be a `string`.
5252
+ */
5125
5253
 
5126
5254
 
5127
5255
  setDetail(detail) {
@@ -5129,29 +5257,65 @@ class TextNode extends LexicalNode {
5129
5257
  self.__detail = typeof detail === 'string' ? DETAIL_TYPE_TO_DETAIL[detail] : detail;
5130
5258
  return self;
5131
5259
  }
5260
+ /**
5261
+ * Sets the node style to the provided CSSText-like string. Set this property as you
5262
+ * would an HTMLElement style attribute to apply inline styles to the underlying DOM Element.
5263
+ *
5264
+ * @param style - CSSText to be applied to the underlying HTMLElement.
5265
+ *
5266
+ * @returns this TextNode.
5267
+ */
5268
+
5132
5269
 
5133
5270
  setStyle(style) {
5134
5271
  const self = this.getWritable();
5135
5272
  self.__style = style;
5136
5273
  return self;
5137
5274
  }
5275
+ /**
5276
+ * Applies the provided format to this TextNode if it's not present. Removes it if it is present.
5277
+ * Prefer using this method to turn specific formats on and off.
5278
+ *
5279
+ * @param type - TextFormatType to toggle.
5280
+ *
5281
+ * @returns this TextNode.
5282
+ */
5283
+
5138
5284
 
5139
5285
  toggleFormat(type) {
5140
5286
  const formatFlag = TEXT_TYPE_TO_FORMAT[type];
5141
5287
  return this.setFormat(this.getFormat() ^ formatFlag);
5142
5288
  }
5289
+ /**
5290
+ * Toggles the directionless detail value of the node. Prefer using this method over setDetail.
5291
+ *
5292
+ * @returns this TextNode.
5293
+ */
5294
+
5143
5295
 
5144
5296
  toggleDirectionless() {
5145
5297
  const self = this.getWritable();
5146
5298
  self.__detail ^= IS_DIRECTIONLESS;
5147
5299
  return self;
5148
5300
  }
5301
+ /**
5302
+ * Toggles the unmergeable detail value of the node. Prefer using this method over setDetail.
5303
+ *
5304
+ * @returns this TextNode.
5305
+ */
5306
+
5149
5307
 
5150
5308
  toggleUnmergeable() {
5151
5309
  const self = this.getWritable();
5152
5310
  self.__detail ^= IS_UNMERGEABLE;
5153
5311
  return self;
5154
5312
  }
5313
+ /**
5314
+ * Sets the mode of the node.
5315
+ *
5316
+ * @returns this TextNode.
5317
+ */
5318
+
5155
5319
 
5156
5320
  setMode(type) {
5157
5321
  const mode = TEXT_MODE_TO_TYPE[type];
@@ -5164,6 +5328,14 @@ class TextNode extends LexicalNode {
5164
5328
  self.__mode = mode;
5165
5329
  return self;
5166
5330
  }
5331
+ /**
5332
+ * Sets the text content of the node.
5333
+ *
5334
+ * @param text - the string to set as the text value of the node.
5335
+ *
5336
+ * @returns this TextNode.
5337
+ */
5338
+
5167
5339
 
5168
5340
  setTextContent(text) {
5169
5341
  if (this.__text === text) {
@@ -5174,6 +5346,15 @@ class TextNode extends LexicalNode {
5174
5346
  self.__text = text;
5175
5347
  return self;
5176
5348
  }
5349
+ /**
5350
+ * Sets the current Lexical selection to be a RangeSelection with anchor and focus on this TextNode at the provided offsets.
5351
+ *
5352
+ * @param _anchorOffset - the offset at which the Selection anchor will be placed.
5353
+ * @param _focusOffset - the offset at which the Selection focus will be placed.
5354
+ *
5355
+ * @returns the new RangeSelection.
5356
+ */
5357
+
5177
5358
 
5178
5359
  select(_anchorOffset, _focusOffset) {
5179
5360
  errorOnReadOnly();
@@ -5212,6 +5393,18 @@ class TextNode extends LexicalNode {
5212
5393
 
5213
5394
  return selection;
5214
5395
  }
5396
+ /**
5397
+ * Inserts the provided text into this TextNode at the provided offset, deleting the number of characters
5398
+ * specified. Can optionally calculate a new selection after the operation is complete.
5399
+ *
5400
+ * @param offset - the offset at which the splice operation should begin.
5401
+ * @param delCount - the number of characters to delete, starting from the offset.
5402
+ * @param newText - the text to insert into the TextNode at the offset.
5403
+ * @param moveSelection - optional, whether or not to move selection to the end of the inserted substring.
5404
+ *
5405
+ * @returns this TextNode.
5406
+ */
5407
+
5215
5408
 
5216
5409
  spliceText(offset, delCount, newText, moveSelection) {
5217
5410
  const writableSelf = this.getWritable();
@@ -5238,14 +5431,39 @@ class TextNode extends LexicalNode {
5238
5431
  writableSelf.__text = updatedText;
5239
5432
  return writableSelf;
5240
5433
  }
5434
+ /**
5435
+ * This method is meant to be overriden by TextNode subclasses to control the behavior of those nodes
5436
+ * when a user event would cause text to be inserted before them in the editor. If true, Lexical will attempt
5437
+ * to insert text into this node. If false, it will insert the text in a new sibling node.
5438
+ *
5439
+ * @returns true if text can be inserted before the node, false otherwise.
5440
+ */
5441
+
5241
5442
 
5242
5443
  canInsertTextBefore() {
5243
5444
  return true;
5244
5445
  }
5446
+ /**
5447
+ * This method is meant to be overriden by TextNode subclasses to control the behavior of those nodes
5448
+ * when a user event would cause text to be inserted after them in the editor. If true, Lexical will attempt
5449
+ * to insert text into this node. If false, it will insert the text in a new sibling node.
5450
+ *
5451
+ * @returns true if text can be inserted after the node, false otherwise.
5452
+ */
5453
+
5245
5454
 
5246
5455
  canInsertTextAfter() {
5247
5456
  return true;
5248
5457
  }
5458
+ /**
5459
+ * Splits this TextNode at the provided character offsets, forming new TextNodes from the substrings
5460
+ * formed by the split, and inserting those new TextNodes into the editor, replacing the one that was split.
5461
+ *
5462
+ * @param splitOffsets - rest param of the text content character offsets at which this node should be split.
5463
+ *
5464
+ * @returns an Array containing the newly-created TextNodes.
5465
+ */
5466
+
5249
5467
 
5250
5468
  splitText(...splitOffsets) {
5251
5469
  errorOnReadOnly();
@@ -5359,6 +5577,14 @@ class TextNode extends LexicalNode {
5359
5577
 
5360
5578
  return splitNodes;
5361
5579
  }
5580
+ /**
5581
+ * Merges the target TextNode into this TextNode, removing the target node.
5582
+ *
5583
+ * @param target - the TextNode to merge into this one.
5584
+ *
5585
+ * @returns this TextNode.
5586
+ */
5587
+
5362
5588
 
5363
5589
  mergeWithSibling(target) {
5364
5590
  const isBefore = target === this.getPreviousSibling();
@@ -5403,6 +5629,14 @@ class TextNode extends LexicalNode {
5403
5629
  target.remove();
5404
5630
  return writableSelf;
5405
5631
  }
5632
+ /**
5633
+ * This method is meant to be overriden by TextNode subclasses to control the behavior of those nodes
5634
+ * when used with the registerLexicalTextEntity function. If you're using registerLexicalTextEntity, the
5635
+ * node class that you create and replace matched text with should return true from this method.
5636
+ *
5637
+ * @returns true if the node is to be treated as a "text entity", false otherwise.
5638
+ */
5639
+
5406
5640
 
5407
5641
  isTextEntity() {
5408
5642
  return false;
@@ -5895,7 +6129,14 @@ function $transferStartingElementPointToTextPoint(start, end, format, style) {
5895
6129
  if (placementNode === null) {
5896
6130
  element.append(target);
5897
6131
  } else {
5898
- placementNode.insertBefore(target);
6132
+ placementNode.insertBefore(target); // Fix the end point offset if it refers to the same element as start,
6133
+ // as we've now inserted another element before it. Note that we only
6134
+ // do it if selection is not collapsed as otherwise it'll transfer
6135
+ // both focus and anchor to the text node below
6136
+
6137
+ if (end.type === 'element' && end.key === start.key && end.offset !== start.offset) {
6138
+ end.set(end.key, end.offset + 1, 'element');
6139
+ }
5899
6140
  } // Transfer the element point to a text point.
5900
6141
 
5901
6142
 
@@ -6287,6 +6528,13 @@ class RangeSelection {
6287
6528
  anchor._selection = this;
6288
6529
  focus._selection = this;
6289
6530
  }
6531
+ /**
6532
+ * Used to check if the provided selections is equal to this one by value,
6533
+ * inluding anchor, focus, format, and style properties.
6534
+ * @param selection - the Selection to compare this one to.
6535
+ * @returns true if the Selections are equal, false otherwise.
6536
+ */
6537
+
6290
6538
 
6291
6539
  is(selection) {
6292
6540
  if (!$isRangeSelection(selection)) {
@@ -6295,14 +6543,34 @@ class RangeSelection {
6295
6543
 
6296
6544
  return this.anchor.is(selection.anchor) && this.focus.is(selection.focus) && this.format === selection.format && this.style === selection.style;
6297
6545
  }
6546
+ /**
6547
+ * Returns whether the Selection is "backwards", meaning the focus
6548
+ * logically precedes the anchor in the EditorState.
6549
+ * @returns true if the Selection is backwards, false otherwise.
6550
+ */
6551
+
6298
6552
 
6299
6553
  isBackward() {
6300
6554
  return this.focus.isBefore(this.anchor);
6301
6555
  }
6556
+ /**
6557
+ * Returns whether the Selection is "collapsed", meaning the anchor and focus are
6558
+ * the same node and have the same offset.
6559
+ *
6560
+ * @returns true if the Selection is collapsed, false otherwise.
6561
+ */
6562
+
6302
6563
 
6303
6564
  isCollapsed() {
6304
6565
  return this.anchor.is(this.focus);
6305
6566
  }
6567
+ /**
6568
+ * Gets all the nodes in the Selection. Uses caching to make it generally suitable
6569
+ * for use in hot paths.
6570
+ *
6571
+ * @returns an Array containing all the nodes in the Selection
6572
+ */
6573
+
6306
6574
 
6307
6575
  getNodes() {
6308
6576
  const cachedNodes = this._cachedNodes;
@@ -6355,6 +6623,15 @@ class RangeSelection {
6355
6623
 
6356
6624
  return nodes;
6357
6625
  }
6626
+ /**
6627
+ * Sets this Selection to be of type "text" at the provided anchor and focus values.
6628
+ *
6629
+ * @param anchorNode - the anchor node to set on the Selection
6630
+ * @param anchorOffset - the offset to set on the Selection
6631
+ * @param focusNode - the focus node to set on the Selection
6632
+ * @param focusOffset - the focus offset to set on the Selection
6633
+ */
6634
+
6358
6635
 
6359
6636
  setTextNodeRange(anchorNode, anchorOffset, focusNode, focusOffset) {
6360
6637
  $setPointValues(this.anchor, anchorNode.__key, anchorOffset, 'text');
@@ -6362,6 +6639,12 @@ class RangeSelection {
6362
6639
  this._cachedNodes = null;
6363
6640
  this.dirty = true;
6364
6641
  }
6642
+ /**
6643
+ * Gets the (plain) text content of all the nodes in the selection.
6644
+ *
6645
+ * @returns a string representing the text content of all the nodes in the Selection
6646
+ */
6647
+
6365
6648
 
6366
6649
  getTextContent() {
6367
6650
  const nodes = this.getNodes();
@@ -6419,6 +6702,13 @@ class RangeSelection {
6419
6702
 
6420
6703
  return textContent;
6421
6704
  }
6705
+ /**
6706
+ * Attempts to map a DOM selection range onto this Lexical Selection,
6707
+ * setting the anchor, focus, and type accordingly
6708
+ *
6709
+ * @param range a DOM Selection range conforming to the StaticRange interface.
6710
+ */
6711
+
6422
6712
 
6423
6713
  applyDOMRange(range) {
6424
6714
  const editor = getActiveEditor();
@@ -6435,6 +6725,12 @@ class RangeSelection {
6435
6725
  $setPointValues(this.focus, focusPoint.key, focusPoint.offset, focusPoint.type);
6436
6726
  this._cachedNodes = null;
6437
6727
  }
6728
+ /**
6729
+ * Creates a new RangeSelection, copying over all the property values from this one.
6730
+ *
6731
+ * @returns a new RangeSelection with the same property values as this one.
6732
+ */
6733
+
6438
6734
 
6439
6735
  clone() {
6440
6736
  const anchor = this.anchor;
@@ -6442,21 +6738,48 @@ class RangeSelection {
6442
6738
  const selection = new RangeSelection($createPoint(anchor.key, anchor.offset, anchor.type), $createPoint(focus.key, focus.offset, focus.type), this.format, this.style);
6443
6739
  return selection;
6444
6740
  }
6741
+ /**
6742
+ * Toggles the provided format on all the TextNodes in the Selection.
6743
+ *
6744
+ * @param format a string TextFormatType to toggle on the TextNodes in the selection
6745
+ */
6746
+
6445
6747
 
6446
6748
  toggleFormat(format) {
6447
6749
  this.format = toggleTextFormatType(this.format, format, null);
6448
6750
  this.dirty = true;
6449
6751
  }
6752
+ /**
6753
+ * Sets the value of the style property on the Selection
6754
+ *
6755
+ * @param style - the style to set at the value of the style property.
6756
+ */
6757
+
6450
6758
 
6451
6759
  setStyle(style) {
6452
6760
  this.style = style;
6453
6761
  this.dirty = true;
6454
6762
  }
6763
+ /**
6764
+ * Returns whether the provided TextFormatType is present on the Selection. This will be true if any node in the Selection
6765
+ * has the specified format.
6766
+ *
6767
+ * @param type the TextFormatType to check for.
6768
+ * @returns true if the provided format is currently toggled on on the Selection, false otherwise.
6769
+ */
6770
+
6455
6771
 
6456
6772
  hasFormat(type) {
6457
6773
  const formatFlag = TEXT_TYPE_TO_FORMAT[type];
6458
6774
  return (this.format & formatFlag) !== 0;
6459
6775
  }
6776
+ /**
6777
+ * Attempts to insert the provided text into the EditorState at the current Selection.
6778
+ * converts tabs, newlines, and carriage returns into LexicalNodes.
6779
+ *
6780
+ * @param text the text to insert into the Selection
6781
+ */
6782
+
6460
6783
 
6461
6784
  insertRawText(text) {
6462
6785
  const parts = text.split(/(\r?\n|\t)/);
@@ -6477,6 +6800,13 @@ class RangeSelection {
6477
6800
 
6478
6801
  this.insertNodes(nodes);
6479
6802
  }
6803
+ /**
6804
+ * Attempts to insert the provided text into the EditorState at the current Selection as a new
6805
+ * Lexical TextNode, according to a series of insertion heuristics based on the selection type and position.
6806
+ *
6807
+ * @param text the text to insert into the Selection
6808
+ */
6809
+
6480
6810
 
6481
6811
  insertText(text) {
6482
6812
  const anchor = this.anchor;
@@ -6752,10 +7082,21 @@ class RangeSelection {
6752
7082
  }
6753
7083
  }
6754
7084
  }
7085
+ /**
7086
+ * Removes the text in the Selection, adjusting the EditorState accordingly.
7087
+ */
7088
+
6755
7089
 
6756
7090
  removeText() {
6757
7091
  this.insertText('');
6758
7092
  }
7093
+ /**
7094
+ * Applies the provided format to the TextNodes in the Selection, splitting or
7095
+ * merging nodes as necessary.
7096
+ *
7097
+ * @param formatType the format type to apply to the nodes in the Selection.
7098
+ */
7099
+
6759
7100
 
6760
7101
  formatText(formatType) {
6761
7102
  if (this.isCollapsed()) {
@@ -6876,6 +7217,16 @@ class RangeSelection {
6876
7217
 
6877
7218
  this.format = firstNextFormat | lastNextFormat;
6878
7219
  }
7220
+ /**
7221
+ * Attempts to "intelligently" insert an arbitrary list of Lexical nodes into the EditorState at the
7222
+ * current Selection according to a set of heuristics that determine how surrounding nodes
7223
+ * should be changed, replaced, or moved to accomodate the incoming ones.
7224
+ *
7225
+ * @param nodes - the nodes to insert
7226
+ * @param selectStart - whether or not to select the start after the insertion.
7227
+ * @returns true if the nodes were inserted successfully, false otherwise.
7228
+ */
7229
+
6879
7230
 
6880
7231
  insertNodes(nodes, selectStart) {
6881
7232
  // If there is a range selected remove the text in it
@@ -7238,6 +7589,10 @@ class RangeSelection {
7238
7589
 
7239
7590
  return true;
7240
7591
  }
7592
+ /**
7593
+ * Inserts a new ParagraphNode into the EditorState at the current Selection
7594
+ */
7595
+
7241
7596
 
7242
7597
  insertParagraph() {
7243
7598
  if (!this.isCollapsed()) {
@@ -7359,6 +7714,13 @@ class RangeSelection {
7359
7714
  }
7360
7715
  }
7361
7716
  }
7717
+ /**
7718
+ * Inserts a logical linebreak, which may be a new LineBreakNode or a new ParagraphNode, into the EditorState at the
7719
+ * current Selection.
7720
+ *
7721
+ * @param selectStart whether or not to select the start of the insertion range after the operation completes.
7722
+ */
7723
+
7362
7724
 
7363
7725
  insertLineBreak(selectStart) {
7364
7726
  const lineBreakNode = $createLineBreakNode();
@@ -7380,10 +7742,24 @@ class RangeSelection {
7380
7742
  }
7381
7743
  }
7382
7744
  }
7745
+ /**
7746
+ * Returns the character-based offsets of the Selection, accounting for non-text Points
7747
+ * by using the children size or text content.
7748
+ *
7749
+ * @returns the character offsets for the Selection
7750
+ */
7751
+
7383
7752
 
7384
7753
  getCharacterOffsets() {
7385
7754
  return getCharacterOffsets(this);
7386
7755
  }
7756
+ /**
7757
+ * Extracts the nodes in the Selection, splitting nodes where necessary
7758
+ * to get offset-level precision.
7759
+ *
7760
+ * @returns The nodes in the Selection
7761
+ */
7762
+
7387
7763
 
7388
7764
  extract() {
7389
7765
  const selectedNodes = this.getNodes();
@@ -7437,6 +7813,16 @@ class RangeSelection {
7437
7813
 
7438
7814
  return selectedNodes;
7439
7815
  }
7816
+ /**
7817
+ * Modifies the Selection according to the parameters and a set of heuristics that account for
7818
+ * various node types. Can be used to safely move or extend selection by one logical "unit" without
7819
+ * dealing explicitly with all the possible node types.
7820
+ *
7821
+ * @param alter the type of modification to perform
7822
+ * @param isBackward whether or not selection is backwards
7823
+ * @param granularity the granularity at which to apply the modification
7824
+ */
7825
+
7440
7826
 
7441
7827
  modify(alter, isBackward, granularity) {
7442
7828
  const focus = this.focus;
@@ -7573,6 +7959,13 @@ class RangeSelection {
7573
7959
  }
7574
7960
  }
7575
7961
  }
7962
+ /**
7963
+ * Performs one logical character deletion operation on the EditorState based on the current Selection.
7964
+ * Handles different node types.
7965
+ *
7966
+ * @param isBackward whether or not the selection is backwards.
7967
+ */
7968
+
7576
7969
 
7577
7970
  deleteCharacter(isBackward) {
7578
7971
  const wasCollapsed = this.isCollapsed();
@@ -7605,6 +7998,8 @@ class RangeSelection {
7605
7998
  $setSelection(nodeSelection);
7606
7999
  } else {
7607
8000
  possibleNode.remove();
8001
+ const editor = getActiveEditor();
8002
+ editor.dispatchCommand(SELECTION_CHANGE_COMMAND, undefined);
7608
8003
  }
7609
8004
 
7610
8005
  return;
@@ -7659,6 +8054,13 @@ class RangeSelection {
7659
8054
  }
7660
8055
  }
7661
8056
  }
8057
+ /**
8058
+ * Performs one logical line deletion operation on the EditorState based on the current Selection.
8059
+ * Handles different node types.
8060
+ *
8061
+ * @param isBackward whether or not the selection is backwards.
8062
+ */
8063
+
7662
8064
 
7663
8065
  deleteLine(isBackward) {
7664
8066
  if (this.isCollapsed()) {
@@ -7678,6 +8080,13 @@ class RangeSelection {
7678
8080
 
7679
8081
  this.removeText();
7680
8082
  }
8083
+ /**
8084
+ * Performs one logical word deletion operation on the EditorState based on the current Selection.
8085
+ * Handles different node types.
8086
+ *
8087
+ * @param isBackward whether or not the selection is backwards.
8088
+ */
8089
+
7681
8090
 
7682
8091
  deleteWord(isBackward) {
7683
8092
  if (this.isCollapsed()) {
@@ -7726,7 +8135,7 @@ function $swapPoints(selection) {
7726
8135
  }
7727
8136
 
7728
8137
  function moveNativeSelection(domSelection, alter, direction, granularity) {
7729
- // @ts-expect-error Selection.modify() method applies a change to the current selection or cursor position,
8138
+ // Selection.modify() method applies a change to the current selection or cursor position,
7730
8139
  // but is still non-standard in some browsers.
7731
8140
  domSelection.modify(alter, direction, granularity);
7732
8141
  }
@@ -8396,7 +8805,7 @@ function updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
8396
8805
  markSelectionChangeFromDOMUpdate();
8397
8806
  }
8398
8807
  function $insertNodes(nodes, selectStart) {
8399
- let selection = $getSelection();
8808
+ let selection = $getSelection() || $getPreviousSelection();
8400
8809
 
8401
8810
  if (selection === null) {
8402
8811
  selection = $getRoot().selectEnd();
@@ -8825,7 +9234,7 @@ function handleDEVOnlyPendingUpdateGuarantees(pendingEditorState) {
8825
9234
  };
8826
9235
  }
8827
9236
 
8828
- function commitPendingUpdates(editor) {
9237
+ function commitPendingUpdates(editor, recoveryEditorState) {
8829
9238
  const pendingEditorState = editor._pendingEditorState;
8830
9239
  const rootElement = editor._rootElement;
8831
9240
  const shouldSkipDOM = editor._headless || rootElement === null;
@@ -8875,7 +9284,7 @@ function commitPendingUpdates(editor) {
8875
9284
  initMutationObserver(editor);
8876
9285
  editor._dirtyType = FULL_RECONCILE;
8877
9286
  isAttemptingToRecoverFromReconcilerError = true;
8878
- commitPendingUpdates(editor);
9287
+ commitPendingUpdates(editor, currentEditorState);
8879
9288
  isAttemptingToRecoverFromReconcilerError = false;
8880
9289
  } else {
8881
9290
  // To avoid a possible situation of infinite loops, lets throw
@@ -8963,7 +9372,7 @@ function commitPendingUpdates(editor) {
8963
9372
  }
8964
9373
 
8965
9374
  if (mutatedNodes !== null) {
8966
- triggerMutationListeners(editor, currentEditorState, pendingEditorState, mutatedNodes, tags, dirtyLeaves);
9375
+ triggerMutationListeners(editor, mutatedNodes, tags, dirtyLeaves);
8967
9376
  }
8968
9377
 
8969
9378
  if (!$isRangeSelection(pendingSelection) && pendingSelection !== null && (currentSelection === null || !currentSelection.is(pendingSelection))) {
@@ -8980,15 +9389,20 @@ function commitPendingUpdates(editor) {
8980
9389
  editor._decorators = pendingDecorators;
8981
9390
  editor._pendingDecorators = null;
8982
9391
  triggerListeners('decorator', editor, true, pendingDecorators);
8983
- }
9392
+ } // If reconciler fails, we reset whole editor (so current editor state becomes empty)
9393
+ // and attempt to re-render pendingEditorState. If that goes through we trigger
9394
+ // listeners, but instead use recoverEditorState which is current editor state before reset
9395
+ // This specifically important for collab that relies on prevEditorState from update
9396
+ // listener to calculate delta of changed nodes/properties
9397
+
8984
9398
 
8985
- triggerTextContentListeners(editor, currentEditorState, pendingEditorState);
9399
+ triggerTextContentListeners(editor, recoveryEditorState || currentEditorState, pendingEditorState);
8986
9400
  triggerListeners('update', editor, true, {
8987
9401
  dirtyElements,
8988
9402
  dirtyLeaves,
8989
9403
  editorState: pendingEditorState,
8990
9404
  normalizedNodes,
8991
- prevEditorState: currentEditorState,
9405
+ prevEditorState: recoveryEditorState || currentEditorState,
8992
9406
  tags
8993
9407
  });
8994
9408
  triggerDeferredUpdateCallbacks(editor, deferred);
@@ -9004,7 +9418,7 @@ function triggerTextContentListeners(editor, currentEditorState, pendingEditorSt
9004
9418
  }
9005
9419
  }
9006
9420
 
9007
- function triggerMutationListeners(editor, currentEditorState, pendingEditorState, mutatedNodes, updateTags, dirtyLeaves) {
9421
+ function triggerMutationListeners(editor, mutatedNodes, updateTags, dirtyLeaves) {
9008
9422
  const listeners = Array.from(editor._listeners.mutation);
9009
9423
  const listenersLength = listeners.length;
9010
9424
 
@@ -10101,7 +10515,7 @@ function exportNodeToJSON(node) {
10101
10515
 
10102
10516
  if (serializedNode.type !== nodeClass.getType()) {
10103
10517
  {
10104
- throw Error(`LexicalNode: Node ${nodeClass.name} does not implement .exportJSON().`);
10518
+ throw Error(`LexicalNode: Node ${nodeClass.name} does not match the serialized type. Check if .exportJSON() is implemented and it is returning the correct type.`);
10105
10519
  }
10106
10520
  } // @ts-expect-error TODO Replace Class utility type with InstanceType
10107
10521
 
@@ -10825,6 +11239,16 @@ class LexicalEditor {
10825
11239
  registeredNodes.forEach(node => node.transforms.delete(listener));
10826
11240
  };
10827
11241
  }
11242
+ /**
11243
+ * Used to assert that a certain node is registered, usually by plugins to ensure nodes that they
11244
+ * depend on have been registered.
11245
+ * @returns True if the editor has registered the provided node type, false otherwise.
11246
+ */
11247
+
11248
+
11249
+ hasNode(node) {
11250
+ return this._nodes.has(node.getType());
11251
+ }
10828
11252
  /**
10829
11253
  * Used to assert that certain nodes are registered, usually by plugins to ensure nodes that they
10830
11254
  * depend on have been registered.
@@ -10833,16 +11257,7 @@ class LexicalEditor {
10833
11257
 
10834
11258
 
10835
11259
  hasNodes(nodes) {
10836
- for (let i = 0; i < nodes.length; i++) {
10837
- const klass = nodes[i];
10838
- const type = klass.getType();
10839
-
10840
- if (!this._nodes.has(type)) {
10841
- return false;
10842
- }
10843
- }
10844
-
10845
- return true;
11260
+ return nodes.every(this.hasNode.bind(this));
10846
11261
  }
10847
11262
  /**
10848
11263
  * Dispatches a command of the specified type with the specified payload.
@@ -10934,6 +11349,10 @@ class LexicalEditor {
10934
11349
  nextRootElement.classList.add(...classNames);
10935
11350
  }
10936
11351
  } else {
11352
+ // If content editable is unmounted we'll reset editor state back to original
11353
+ // (or pending) editor state since there will be no reconciliation
11354
+ this._editorState = pendingEditorState;
11355
+ this._pendingEditorState = null;
10937
11356
  this._window = null;
10938
11357
  }
10939
11358