lexical 0.6.5 → 0.7.0

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
@@ -272,6 +272,7 @@ function $flushMutations$1(editor, mutations, observer) {
272
272
  // actually "on screen".
273
273
 
274
274
  const currentEditorState = editor._editorState;
275
+ const blockCursorElement = editor._blockCursorElement;
275
276
  let shouldRevertSelection = false;
276
277
  let possibleTextForFirefoxPaste = '';
277
278
 
@@ -304,7 +305,7 @@ function $flushMutations$1(editor, mutations, observer) {
304
305
  const node = getNodeFromDOMNode(addedDOM);
305
306
  const parentDOM = addedDOM.parentNode;
306
307
 
307
- if (parentDOM != null && node === null && (addedDOM.nodeName !== 'BR' || !isManagedLineBreak(addedDOM, parentDOM, editor))) {
308
+ if (parentDOM != null && addedDOM !== blockCursorElement && node === null && (addedDOM.nodeName !== 'BR' || !isManagedLineBreak(addedDOM, parentDOM, editor))) {
308
309
  if (IS_FIREFOX) {
309
310
  const possibleText = addedDOM.innerText || addedDOM.nodeValue;
310
311
 
@@ -326,7 +327,7 @@ function $flushMutations$1(editor, mutations, observer) {
326
327
  for (let s = 0; s < removedDOMsLength; s++) {
327
328
  const removedDOM = removedDOMs[s];
328
329
 
329
- if (removedDOM.nodeName === 'BR' && isManagedLineBreak(removedDOM, targetDOM, editor)) {
330
+ if (removedDOM.nodeName === 'BR' && isManagedLineBreak(removedDOM, targetDOM, editor) || blockCursorElement === removedDOM) {
330
331
  targetDOM.appendChild(removedDOM);
331
332
  unremovedBRs++;
332
333
  }
@@ -350,7 +351,7 @@ function $flushMutations$1(editor, mutations, observer) {
350
351
  if (badDOMTargets.size > 0) {
351
352
  for (const [targetDOM, targetNode] of badDOMTargets) {
352
353
  if ($isElementNode(targetNode)) {
353
- const childKeys = targetNode.__children;
354
+ const childKeys = targetNode.getChildrenKeys();
354
355
  let currentDOM = targetDOM.firstChild;
355
356
 
356
357
  for (let s = 0; s < childKeys.length; s++) {
@@ -590,22 +591,60 @@ function internalMarkParentElementsAsDirty(parentKey, nodeMap, dirtyElements) {
590
591
  }
591
592
  }
592
593
 
593
- function removeFromParent(writableNode) {
594
- const oldParent = writableNode.getParent();
594
+ function removeFromParent(node) {
595
+ const oldParent = node.getParent();
595
596
 
596
597
  if (oldParent !== null) {
598
+ const writableNode = node.getWritable();
597
599
  const writableParent = oldParent.getWritable();
598
- const children = writableParent.__children;
599
- const index = children.indexOf(writableNode.__key);
600
+ const prevSibling = node.getPreviousSibling();
601
+ const nextSibling = node.getNextSibling(); // TODO: this function duplicates a bunch of operations, can be simplified.
600
602
 
601
- if (index === -1) {
602
- {
603
- throw Error(`Node is not a child of its parent`);
603
+ if (prevSibling === null) {
604
+ if (nextSibling !== null) {
605
+ const writableNextSibling = nextSibling.getWritable();
606
+ writableParent.__first = nextSibling.__key;
607
+ writableNextSibling.__prev = null;
608
+ } else {
609
+ writableParent.__first = null;
610
+ }
611
+ } else {
612
+ const writablePrevSibling = prevSibling.getWritable();
613
+
614
+ if (nextSibling !== null) {
615
+ const writableNextSibling = nextSibling.getWritable();
616
+ writableNextSibling.__prev = writablePrevSibling.__key;
617
+ writablePrevSibling.__next = writableNextSibling.__key;
618
+ } else {
619
+ writablePrevSibling.__next = null;
604
620
  }
621
+
622
+ writableNode.__prev = null;
605
623
  }
606
624
 
607
- internalMarkSiblingsAsDirty(writableNode);
608
- children.splice(index, 1);
625
+ if (nextSibling === null) {
626
+ if (prevSibling !== null) {
627
+ const writablePrevSibling = prevSibling.getWritable();
628
+ writableParent.__last = prevSibling.__key;
629
+ writablePrevSibling.__next = null;
630
+ } else {
631
+ writableParent.__last = null;
632
+ }
633
+ } else {
634
+ const writableNextSibling = nextSibling.getWritable();
635
+
636
+ if (prevSibling !== null) {
637
+ const writablePrevSibling = prevSibling.getWritable();
638
+ writablePrevSibling.__next = writableNextSibling.__key;
639
+ writableNextSibling.__prev = writablePrevSibling.__key;
640
+ } else {
641
+ writableNextSibling.__prev = null;
642
+ }
643
+
644
+ writableNode.__next = null;
645
+ }
646
+
647
+ writableParent.__size--;
609
648
  writableNode.__parent = null;
610
649
  }
611
650
  } // Never use this function directly! It will break
@@ -1207,7 +1246,7 @@ function resolveElement(element, isBackward, focusOffset) {
1207
1246
  return block.getChildAtIndex(isBackward ? offset - 1 : offset);
1208
1247
  }
1209
1248
 
1210
- function $getDecoratorNode(focus, isBackward) {
1249
+ function $getAdjacentNode(focus, isBackward) {
1211
1250
  const focusOffset = focus.offset;
1212
1251
 
1213
1252
  if (focus.type === 'element') {
@@ -1437,16 +1476,95 @@ function errorOnInsertTextNodeOnRoot(node, insertNode) {
1437
1476
  }
1438
1477
  }
1439
1478
  }
1440
- function $getNodeByKeyOrThrow(key) {
1441
- const node = $getNodeByKey(key);
1442
1479
 
1443
- if (node === null) {
1444
- {
1445
- throw Error(`Expected node with key ${key} to exist but it's not in the nodeMap.`);
1480
+ function createBlockCursorElement(editorConfig) {
1481
+ const theme = editorConfig.theme;
1482
+ const element = document.createElement('div');
1483
+ element.contentEditable = 'false';
1484
+ element.setAttribute('data-lexical-cursor', 'true');
1485
+ let blockCursorTheme = theme.blockCursor;
1486
+
1487
+ if (blockCursorTheme !== undefined) {
1488
+ if (typeof blockCursorTheme === 'string') {
1489
+ const classNamesArr = blockCursorTheme.split(' '); // @ts-expect-error: intentional
1490
+
1491
+ blockCursorTheme = theme.blockCursor = classNamesArr;
1492
+ }
1493
+
1494
+ if (blockCursorTheme !== undefined) {
1495
+ element.classList.add(...blockCursorTheme);
1446
1496
  }
1447
1497
  }
1448
1498
 
1449
- return node;
1499
+ return element;
1500
+ }
1501
+
1502
+ function needsBlockCursor(node) {
1503
+ return ($isDecoratorNode(node) || $isElementNode(node) && !node.canBeEmpty()) && !node.isInline();
1504
+ }
1505
+
1506
+ function removeDOMBlockCursorElement(blockCursorElement, editor, rootElement) {
1507
+ rootElement.style.removeProperty('caret-color');
1508
+ editor._blockCursorElement = null;
1509
+ const parentElement = blockCursorElement.parentElement;
1510
+
1511
+ if (parentElement !== null) {
1512
+ parentElement.removeChild(blockCursorElement);
1513
+ }
1514
+ }
1515
+ function updateDOMBlockCursorElement(editor, rootElement, nextSelection) {
1516
+ let blockCursorElement = editor._blockCursorElement;
1517
+
1518
+ if ($isRangeSelection(nextSelection) && nextSelection.isCollapsed() && nextSelection.anchor.type === 'element' && rootElement.contains(document.activeElement)) {
1519
+ const anchor = nextSelection.anchor;
1520
+ const elementNode = anchor.getNode();
1521
+ const offset = anchor.offset;
1522
+ const elementNodeSize = elementNode.getChildrenSize();
1523
+ let isBlockCursor = false;
1524
+ let insertBeforeElement = null;
1525
+
1526
+ if (offset === elementNodeSize) {
1527
+ const child = elementNode.getChildAtIndex(offset - 1);
1528
+
1529
+ if (needsBlockCursor(child)) {
1530
+ isBlockCursor = true;
1531
+ }
1532
+ } else {
1533
+ const child = elementNode.getChildAtIndex(offset);
1534
+
1535
+ if (needsBlockCursor(child)) {
1536
+ const sibling = child.getPreviousSibling();
1537
+
1538
+ if (sibling === null || needsBlockCursor(sibling)) {
1539
+ isBlockCursor = true;
1540
+ insertBeforeElement = editor.getElementByKey(child.__key);
1541
+ }
1542
+ }
1543
+ }
1544
+
1545
+ if (isBlockCursor) {
1546
+ const elementDOM = editor.getElementByKey(elementNode.__key);
1547
+
1548
+ if (blockCursorElement === null) {
1549
+ editor._blockCursorElement = blockCursorElement = createBlockCursorElement(editor._config);
1550
+ }
1551
+
1552
+ rootElement.style.caretColor = 'transparent';
1553
+
1554
+ if (insertBeforeElement === null) {
1555
+ elementDOM.appendChild(blockCursorElement);
1556
+ } else {
1557
+ elementDOM.insertBefore(blockCursorElement, insertBeforeElement);
1558
+ }
1559
+
1560
+ return;
1561
+ }
1562
+ } // Remove cursor
1563
+
1564
+
1565
+ if (blockCursorElement !== null) {
1566
+ removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
1567
+ }
1450
1568
  }
1451
1569
 
1452
1570
  /**
@@ -1475,12 +1593,10 @@ function $garbageCollectDetachedDecorators(editor, pendingEditorState) {
1475
1593
  }
1476
1594
 
1477
1595
  function $garbageCollectDetachedDeepChildNodes(node, parentKey, prevNodeMap, nodeMap, dirtyNodes) {
1478
- const children = node.__children;
1479
- const childrenLength = children.length;
1596
+ let child = node.getFirstChild();
1480
1597
 
1481
- for (let i = 0; i < childrenLength; i++) {
1482
- const childKey = children[i];
1483
- const child = nodeMap.get(childKey);
1598
+ while (child !== null) {
1599
+ const childKey = child.__key;
1484
1600
 
1485
1601
  if (child !== undefined && child.__parent === parentKey) {
1486
1602
  if ($isElementNode(child)) {
@@ -1495,6 +1611,8 @@ function $garbageCollectDetachedDeepChildNodes(node, parentKey, prevNodeMap, nod
1495
1611
 
1496
1612
  nodeMap.delete(childKey);
1497
1613
  }
1614
+
1615
+ child = child.isAttached() ? child.getNextSibling() : null;
1498
1616
  }
1499
1617
  }
1500
1618
 
@@ -1661,7 +1779,10 @@ function destroyNode(key, parentDOM) {
1661
1779
 
1662
1780
  if (parentDOM !== null) {
1663
1781
  const dom = getPrevElementByKeyOrThrow(key);
1664
- parentDOM.removeChild(dom);
1782
+
1783
+ if (dom.parentNode === parentDOM) {
1784
+ parentDOM.removeChild(dom);
1785
+ }
1665
1786
  } // This logic is really important, otherwise we will leak DOM nodes
1666
1787
  // when their corresponding LexicalNodes are removed from the editor state.
1667
1788
 
@@ -1671,7 +1792,7 @@ function destroyNode(key, parentDOM) {
1671
1792
  }
1672
1793
 
1673
1794
  if ($isElementNode(node)) {
1674
- const children = node.__children;
1795
+ const children = createChildrenArray(node, activePrevNodeMap);
1675
1796
  destroyChildren(children, 0, children.length - 1, null);
1676
1797
  }
1677
1798
 
@@ -1742,16 +1863,15 @@ function createNode(key, parentDOM, insertDOM) {
1742
1863
 
1743
1864
  if ($isElementNode(node)) {
1744
1865
  const indent = node.__indent;
1866
+ const childrenSize = node.__size;
1745
1867
 
1746
1868
  if (indent !== 0) {
1747
1869
  setElementIndent(dom, indent);
1748
1870
  }
1749
1871
 
1750
- const children = node.__children;
1751
- const childrenLength = children.length;
1752
-
1753
- if (childrenLength !== 0) {
1754
- const endIndex = childrenLength - 1;
1872
+ if (childrenSize !== 0) {
1873
+ const endIndex = childrenSize - 1;
1874
+ const children = createChildrenArray(node, activeNextNodeMap);
1755
1875
  createChildrenWithDirection(children, endIndex, node, dom);
1756
1876
  }
1757
1877
 
@@ -1762,7 +1882,7 @@ function createNode(key, parentDOM, insertDOM) {
1762
1882
  }
1763
1883
 
1764
1884
  if (!node.isInline()) {
1765
- reconcileElementTerminatingLineBreak(null, children, dom);
1885
+ reconcileElementTerminatingLineBreak(null, node, dom);
1766
1886
  }
1767
1887
 
1768
1888
  if ($textContentRequiresDoubleLinebreakAtEnd(node)) {
@@ -1837,16 +1957,15 @@ function createChildren(children, _startIndex, endIndex, dom, insertDOM) {
1837
1957
  subTreeTextContent = previousSubTreeTextContent + subTreeTextContent;
1838
1958
  }
1839
1959
 
1840
- function isLastChildLineBreakOrDecorator(children, nodeMap) {
1841
- const childKey = children[children.length - 1];
1960
+ function isLastChildLineBreakOrDecorator(childKey, nodeMap) {
1842
1961
  const node = nodeMap.get(childKey);
1843
1962
  return $isLineBreakNode(node) || $isDecoratorNode(node) && node.isInline();
1844
1963
  } // If we end an element with a LineBreakNode, then we need to add an additional <br>
1845
1964
 
1846
1965
 
1847
- function reconcileElementTerminatingLineBreak(prevChildren, nextChildren, dom) {
1848
- const prevLineBreak = prevChildren !== null && (prevChildren.length === 0 || isLastChildLineBreakOrDecorator(prevChildren, activePrevNodeMap));
1849
- const nextLineBreak = nextChildren !== null && (nextChildren.length === 0 || isLastChildLineBreakOrDecorator(nextChildren, activeNextNodeMap));
1966
+ function reconcileElementTerminatingLineBreak(prevElement, nextElement, dom) {
1967
+ const prevLineBreak = prevElement !== null && (prevElement.__size === 0 || isLastChildLineBreakOrDecorator(prevElement.__last, activePrevNodeMap));
1968
+ const nextLineBreak = nextElement.__size === 0 || isLastChildLineBreakOrDecorator(nextElement.__last, activeNextNodeMap);
1850
1969
 
1851
1970
  if (prevLineBreak) {
1852
1971
  if (!nextLineBreak) {
@@ -1929,53 +2048,78 @@ function reconcileBlockDirection(element, dom) {
1929
2048
  }
1930
2049
  }
1931
2050
 
1932
- function reconcileChildrenWithDirection(prevChildren, nextChildren, element, dom) {
2051
+ function reconcileChildrenWithDirection(prevElement, nextElement, dom) {
1933
2052
  const previousSubTreeDirectionTextContent = subTreeDirectionedTextContent;
1934
2053
  subTreeDirectionedTextContent = '';
1935
- reconcileChildren(element, prevChildren, nextChildren, dom);
1936
- reconcileBlockDirection(element, dom);
2054
+ reconcileChildren(prevElement, nextElement, dom);
2055
+ reconcileBlockDirection(nextElement, dom);
1937
2056
  subTreeDirectionedTextContent = previousSubTreeDirectionTextContent;
1938
2057
  }
1939
2058
 
1940
- function reconcileChildren(element, prevChildren, nextChildren, dom) {
2059
+ function createChildrenArray(element, nodeMap) {
2060
+ const children = [];
2061
+ let nodeKey = element.__first;
2062
+
2063
+ while (nodeKey !== null) {
2064
+ const node = nodeMap.get(nodeKey);
2065
+
2066
+ if (node === undefined) {
2067
+ {
2068
+ throw Error(`createChildrenArray: node does not exist in nodeMap`);
2069
+ }
2070
+ }
2071
+
2072
+ children.push(nodeKey);
2073
+ nodeKey = node.__next;
2074
+ }
2075
+
2076
+ return children;
2077
+ }
2078
+
2079
+ function reconcileChildren(prevElement, nextElement, dom) {
1941
2080
  const previousSubTreeTextContent = subTreeTextContent;
2081
+ const prevChildrenSize = prevElement.__size;
2082
+ const nextChildrenSize = nextElement.__size;
1942
2083
  subTreeTextContent = '';
1943
- const prevChildrenLength = prevChildren.length;
1944
- const nextChildrenLength = nextChildren.length;
1945
2084
 
1946
- if (prevChildrenLength === 1 && nextChildrenLength === 1) {
1947
- const prevChildKey = prevChildren[0];
1948
- const nextChildKey = nextChildren[0];
2085
+ if (prevChildrenSize === 1 && nextChildrenSize === 1) {
2086
+ const prevFirstChildKey = prevElement.__first;
2087
+ const nextFrstChildKey = nextElement.__first;
1949
2088
 
1950
- if (prevChildKey === nextChildKey) {
1951
- reconcileNode(prevChildKey, dom);
2089
+ if (prevFirstChildKey === nextFrstChildKey) {
2090
+ reconcileNode(prevFirstChildKey, dom);
1952
2091
  } else {
1953
- const lastDOM = getPrevElementByKeyOrThrow(prevChildKey);
1954
- const replacementDOM = createNode(nextChildKey, null, null);
2092
+ const lastDOM = getPrevElementByKeyOrThrow(prevFirstChildKey);
2093
+ const replacementDOM = createNode(nextFrstChildKey, null, null);
1955
2094
  dom.replaceChild(replacementDOM, lastDOM);
1956
- destroyNode(prevChildKey, null);
2095
+ destroyNode(prevFirstChildKey, null);
1957
2096
  }
1958
- } else if (prevChildrenLength === 0) {
1959
- if (nextChildrenLength !== 0) {
1960
- createChildren(nextChildren, 0, nextChildrenLength - 1, dom, null);
1961
- }
1962
- } else if (nextChildrenLength === 0) {
1963
- if (prevChildrenLength !== 0) {
1964
- // @ts-expect-error: internal field
1965
- const lexicalLineBreak = dom.__lexicalLineBreak;
1966
- const canUseFastPath = lexicalLineBreak == null;
1967
- destroyChildren(prevChildren, 0, prevChildrenLength - 1, canUseFastPath ? null : dom);
1968
-
1969
- if (canUseFastPath) {
1970
- // Fast path for removing DOM nodes
1971
- dom.textContent = '';
2097
+ } else {
2098
+ const prevChildren = createChildrenArray(prevElement, activePrevNodeMap);
2099
+ const nextChildren = createChildrenArray(nextElement, activeNextNodeMap);
2100
+
2101
+ if (prevChildrenSize === 0) {
2102
+ if (nextChildrenSize !== 0) {
2103
+ createChildren(nextChildren, 0, nextChildrenSize - 1, dom, null);
2104
+ }
2105
+ } else if (nextChildrenSize === 0) {
2106
+ if (prevChildrenSize !== 0) {
2107
+ // @ts-expect-error: internal field
2108
+ const lexicalLineBreak = dom.__lexicalLineBreak;
2109
+ const canUseFastPath = lexicalLineBreak == null;
2110
+ destroyChildren(prevChildren, 0, prevChildrenSize - 1, canUseFastPath ? null : dom);
2111
+
2112
+ if (canUseFastPath) {
2113
+ // Fast path for removing DOM nodes
2114
+ dom.textContent = '';
2115
+ }
1972
2116
  }
2117
+ } else {
2118
+ reconcileNodeChildren(prevChildren, nextChildren, prevChildrenSize, nextChildrenSize, dom);
1973
2119
  }
1974
- } else {
1975
- reconcileNodeChildren(prevChildren, nextChildren, prevChildrenLength, nextChildrenLength, element, dom);
1976
2120
  }
1977
2121
 
1978
- if ($textContentRequiresDoubleLinebreakAtEnd(element)) {
2122
+ if ($textContentRequiresDoubleLinebreakAtEnd(nextElement)) {
1979
2123
  subTreeTextContent += DOUBLE_LINE_BREAK;
1980
2124
  } // @ts-expect-error: internal field
1981
2125
 
@@ -2064,15 +2208,11 @@ function reconcileNode(key, parentDOM) {
2064
2208
  setElementFormat(dom, nextFormat);
2065
2209
  }
2066
2210
 
2067
- const prevChildren = prevNode.__children;
2068
- const nextChildren = nextNode.__children;
2069
- const childrenAreDifferent = prevChildren !== nextChildren;
2070
-
2071
- if (childrenAreDifferent || isDirty) {
2072
- reconcileChildrenWithDirection(prevChildren, nextChildren, nextNode, dom);
2211
+ if (isDirty) {
2212
+ reconcileChildrenWithDirection(prevNode, nextNode, dom);
2073
2213
 
2074
2214
  if (!$isRootNode(nextNode) && !nextNode.isInline()) {
2075
- reconcileElementTerminatingLineBreak(prevChildren, nextChildren, dom);
2215
+ reconcileElementTerminatingLineBreak(prevNode, nextNode, dom);
2076
2216
  }
2077
2217
  }
2078
2218
 
@@ -2132,10 +2272,16 @@ function getFirstChild(element) {
2132
2272
  }
2133
2273
 
2134
2274
  function getNextSibling(element) {
2135
- return element.nextSibling;
2275
+ let nextSibling = element.nextSibling;
2276
+
2277
+ if (nextSibling !== null && nextSibling === activeEditor$1._blockCursorElement) {
2278
+ nextSibling = nextSibling.nextSibling;
2279
+ }
2280
+
2281
+ return nextSibling;
2136
2282
  }
2137
2283
 
2138
- function reconcileNodeChildren(prevChildren, nextChildren, prevChildrenLength, nextChildrenLength, element, dom) {
2284
+ function reconcileNodeChildren(prevChildren, nextChildren, prevChildrenLength, nextChildrenLength, dom) {
2139
2285
  const prevEndIndex = prevChildrenLength - 1;
2140
2286
  const nextEndIndex = nextChildrenLength - 1;
2141
2287
  let prevChildrenSet;
@@ -3099,7 +3245,9 @@ function addRootElementEvents(rootElement, editor) {
3099
3245
  return dispatchCommand(editor, FOCUS_COMMAND, event);
3100
3246
 
3101
3247
  case 'blur':
3102
- return dispatchCommand(editor, BLUR_COMMAND, event);
3248
+ {
3249
+ return dispatchCommand(editor, BLUR_COMMAND, event);
3250
+ }
3103
3251
 
3104
3252
  case 'drop':
3105
3253
  return dispatchCommand(editor, DROP_COMMAND, event);
@@ -3913,10 +4061,10 @@ class RangeSelection {
3913
4061
  textNode.select();
3914
4062
 
3915
4063
  if (startOffset === 0) {
3916
- firstNode.insertBefore(textNode);
4064
+ firstNode.insertBefore(textNode, false);
3917
4065
  } else {
3918
4066
  const [targetNode] = firstNode.splitText(startOffset);
3919
- targetNode.insertAfter(textNode);
4067
+ targetNode.insertAfter(textNode, false);
3920
4068
  } // When composing, we need to adjust the anchor offset so that
3921
4069
  // we correctly replace that right range.
3922
4070
 
@@ -3967,7 +4115,7 @@ class RangeSelection {
3967
4115
  if ($isTextNode(lastNode) && !lastNode.isToken() && endOffset !== lastNode.getTextContentSize()) {
3968
4116
  if (lastNode.isSegmented()) {
3969
4117
  const textNode = $createTextNode(lastNode.getTextContent());
3970
- lastNode.replace(textNode);
4118
+ lastNode.replace(textNode, false);
3971
4119
  lastNode = textNode;
3972
4120
  }
3973
4121
 
@@ -4010,7 +4158,7 @@ class RangeSelection {
4010
4158
  if (lastNodeChild.isAttached()) {
4011
4159
  if (!selectedNodesSet.has(lastNodeChild) || lastNodeChild.is(lastElementChild)) {
4012
4160
  if (!firstAndLastElementsAreEqual) {
4013
- insertionTarget.insertAfter(lastNodeChild);
4161
+ insertionTarget.insertAfter(lastNodeChild, false);
4014
4162
  }
4015
4163
  } else {
4016
4164
  lastNodeChild.remove();
@@ -4055,7 +4203,7 @@ class RangeSelection {
4055
4203
  } else {
4056
4204
  const textNode = $createTextNode(text);
4057
4205
  textNode.select();
4058
- firstNode.replace(textNode);
4206
+ firstNode.replace(textNode, false);
4059
4207
  } // Remove all selected nodes that haven't already been removed.
4060
4208
 
4061
4209
 
@@ -4392,7 +4540,7 @@ class RangeSelection {
4392
4540
  lastNode = node;
4393
4541
 
4394
4542
  if ($isDecoratorNode(node) && !node.isInline()) {
4395
- target = target.insertAfter(node);
4543
+ target = target.insertAfter(node, false);
4396
4544
  } else if (!$isElementNode(node)) {
4397
4545
  const firstChild = target.getFirstChild();
4398
4546
 
@@ -4419,12 +4567,12 @@ class RangeSelection {
4419
4567
 
4420
4568
  target = node;
4421
4569
  } else {
4422
- target = target.insertAfter(node);
4570
+ target = target.insertAfter(node, false);
4423
4571
  }
4424
4572
  }
4425
4573
  } else if (!$isElementNode(node) || $isElementNode(node) && node.isInline() || $isDecoratorNode(target) && !target.isInline()) {
4426
4574
  lastNode = node;
4427
- target = target.insertAfter(node);
4575
+ target = target.insertAfter(node, false);
4428
4576
  } else {
4429
4577
  const nextTarget = target.getParentOrThrow(); // if we're inserting an Element after a LineBreak, we want to move the target to the parent
4430
4578
  // and remove the LineBreak so we don't have empty space.
@@ -4578,7 +4726,7 @@ class RangeSelection {
4578
4726
  paragraph.select();
4579
4727
 
4580
4728
  if (child !== null) {
4581
- child.insertBefore(paragraph);
4729
+ child.insertBefore(paragraph, false);
4582
4730
  } else {
4583
4731
  currentElement.append(paragraph);
4584
4732
  }
@@ -4593,7 +4741,7 @@ class RangeSelection {
4593
4741
 
4594
4742
  if (anchorOffset === 0 && nodesToMoveLength > 0 && currentElement.isInline()) {
4595
4743
  const parent = currentElement.getParentOrThrow();
4596
- const newElement = parent.insertNewAfter(this);
4744
+ const newElement = parent.insertNewAfter(this, false);
4597
4745
 
4598
4746
  if ($isElementNode(newElement)) {
4599
4747
  const children = parent.getChildren();
@@ -4606,7 +4754,7 @@ class RangeSelection {
4606
4754
  return;
4607
4755
  }
4608
4756
 
4609
- const newElement = currentElement.insertNewAfter(this);
4757
+ const newElement = currentElement.insertNewAfter(this, false);
4610
4758
 
4611
4759
  if (newElement === null) {
4612
4760
  // Handle as a line break insertion
@@ -4738,7 +4886,7 @@ class RangeSelection {
4738
4886
  const anchor = this.anchor;
4739
4887
  const collapse = alter === 'move'; // Handle the selection movement around decorators.
4740
4888
 
4741
- const possibleNode = $getDecoratorNode(focus, isBackward);
4889
+ const possibleNode = $getAdjacentNode(focus, isBackward);
4742
4890
 
4743
4891
  if ($isDecoratorNode(possibleNode) && !possibleNode.isIsolated()) {
4744
4892
  // Make it possible to move selection from range selection to
@@ -4793,6 +4941,16 @@ class RangeSelection {
4793
4941
 
4794
4942
  if (!domSelection) {
4795
4943
  return;
4944
+ }
4945
+
4946
+ const editor = getActiveEditor();
4947
+ const blockCursorElement = editor._blockCursorElement;
4948
+ const rootElement = editor._rootElement; // Remove the block cursor element if it exists. This will ensure selection
4949
+ // works as intended. If we leave it in the DOM all sorts of strange bugs
4950
+ // occur. :/
4951
+
4952
+ if (rootElement !== null && blockCursorElement !== null && $isElementNode(possibleNode) && !possibleNode.isInline() && !possibleNode.canBeEmpty()) {
4953
+ removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
4796
4954
  } // We use the DOM selection.modify API here to "tell" us what the selection
4797
4955
  // will be. We then use it to update the Lexical selection accordingly. This
4798
4956
  // is much more reliable than waiting for a beforeinput and using the ranges
@@ -4875,7 +5033,7 @@ class RangeSelection {
4875
5033
  } // Handle the deletion around decorators.
4876
5034
 
4877
5035
 
4878
- const possibleNode = $getDecoratorNode(focus, isBackward);
5036
+ const possibleNode = $getAdjacentNode(focus, isBackward);
4879
5037
 
4880
5038
  if ($isDecoratorNode(possibleNode) && !possibleNode.isIsolated()) {
4881
5039
  // Make it possible to move selection from range selection to
@@ -5070,7 +5228,7 @@ function shouldResolveAncestor(resolvedElement, resolvedOffset, lastPoint) {
5070
5228
  return lastPoint === null || parent === null || !parent.canBeEmpty() || parent !== lastPoint.getNode();
5071
5229
  }
5072
5230
 
5073
- function internalResolveSelectionPoint(dom, offset, lastPoint) {
5231
+ function internalResolveSelectionPoint(dom, offset, lastPoint, editor) {
5074
5232
  let resolvedOffset = offset;
5075
5233
  let resolvedNode; // If we have selection on an element, we will
5076
5234
  // need to figure out (using the offset) what text
@@ -5091,7 +5249,16 @@ function internalResolveSelectionPoint(dom, offset, lastPoint) {
5091
5249
  resolvedOffset = childNodesLength - 1;
5092
5250
  }
5093
5251
 
5094
- const childDOM = childNodes[resolvedOffset];
5252
+ let childDOM = childNodes[resolvedOffset];
5253
+ let hasBlockCursor = false;
5254
+
5255
+ if (childDOM === editor._blockCursorElement) {
5256
+ childDOM = childNodes[resolvedOffset + 1];
5257
+ hasBlockCursor = true;
5258
+ } else if (editor._blockCursorElement !== null) {
5259
+ resolvedOffset--;
5260
+ }
5261
+
5095
5262
  resolvedNode = getNodeFromDOM(childDOM);
5096
5263
 
5097
5264
  if ($isTextNode(resolvedNode)) {
@@ -5114,7 +5281,7 @@ function internalResolveSelectionPoint(dom, offset, lastPoint) {
5114
5281
  resolvedOffset = 0;
5115
5282
  } else {
5116
5283
  child = descendant;
5117
- resolvedElement = child.getParentOrThrow();
5284
+ resolvedElement = $isElementNode(child) ? child : child.getParentOrThrow();
5118
5285
  }
5119
5286
  }
5120
5287
 
@@ -5122,7 +5289,7 @@ function internalResolveSelectionPoint(dom, offset, lastPoint) {
5122
5289
  resolvedNode = child;
5123
5290
  resolvedElement = null;
5124
5291
  resolvedOffset = getTextNodeOffset(child, moveSelectionToEnd);
5125
- } else if (child !== resolvedElement && moveSelectionToEnd) {
5292
+ } else if (child !== resolvedElement && moveSelectionToEnd && !hasBlockCursor) {
5126
5293
  resolvedOffset++;
5127
5294
  }
5128
5295
  } else {
@@ -5231,13 +5398,13 @@ function internalResolveSelectionPoints(anchorDOM, anchorOffset, focusDOM, focus
5231
5398
  return null;
5232
5399
  }
5233
5400
 
5234
- const resolvedAnchorPoint = internalResolveSelectionPoint(anchorDOM, anchorOffset, $isRangeSelection(lastSelection) ? lastSelection.anchor : null);
5401
+ const resolvedAnchorPoint = internalResolveSelectionPoint(anchorDOM, anchorOffset, $isRangeSelection(lastSelection) ? lastSelection.anchor : null, editor);
5235
5402
 
5236
5403
  if (resolvedAnchorPoint === null) {
5237
5404
  return null;
5238
5405
  }
5239
5406
 
5240
- const resolvedFocusPoint = internalResolveSelectionPoint(focusDOM, focusOffset, $isRangeSelection(lastSelection) ? lastSelection.focus : null);
5407
+ const resolvedFocusPoint = internalResolveSelectionPoint(focusDOM, focusOffset, $isRangeSelection(lastSelection) ? lastSelection.focus : null, editor);
5241
5408
 
5242
5409
  if (resolvedFocusPoint === null) {
5243
5410
  return null;
@@ -5689,6 +5856,11 @@ let activeEditor = null;
5689
5856
  let isReadOnlyMode = false;
5690
5857
  let isAttemptingToRecoverFromReconcilerError = false;
5691
5858
  let infiniteTransformCount = 0;
5859
+ const observerOptions = {
5860
+ characterData: true,
5861
+ childList: true,
5862
+ subtree: true
5863
+ };
5692
5864
  function isCurrentlyReadOnlyMode() {
5693
5865
  return isReadOnlyMode;
5694
5866
  }
@@ -5969,9 +6141,9 @@ function handleDEVOnlyPendingUpdateGuarantees(pendingEditorState) {
5969
6141
  function commitPendingUpdates(editor) {
5970
6142
  const pendingEditorState = editor._pendingEditorState;
5971
6143
  const rootElement = editor._rootElement;
5972
- const headless = editor._headless;
6144
+ const shouldSkipDOM = editor._headless || rootElement === null;
5973
6145
 
5974
- if (rootElement === null && !headless || pendingEditorState === null) {
6146
+ if (pendingEditorState === null) {
5975
6147
  return;
5976
6148
  } // ======
5977
6149
  // Reconciliation has started.
@@ -5991,7 +6163,7 @@ function commitPendingUpdates(editor) {
5991
6163
  editor._pendingEditorState = null;
5992
6164
  editor._editorState = pendingEditorState;
5993
6165
 
5994
- if (!headless && needsUpdate && observer !== null) {
6166
+ if (!shouldSkipDOM && needsUpdate && observer !== null) {
5995
6167
  activeEditor = editor;
5996
6168
  activeEditorState = pendingEditorState;
5997
6169
  isReadOnlyMode = false; // We don't want updates to sync block the reconciliation.
@@ -6025,11 +6197,7 @@ function commitPendingUpdates(editor) {
6025
6197
 
6026
6198
  return;
6027
6199
  } finally {
6028
- observer.observe(rootElement, {
6029
- characterData: true,
6030
- childList: true,
6031
- subtree: true
6032
- });
6200
+ observer.observe(rootElement, observerOptions);
6033
6201
  editor._updating = previouslyUpdating;
6034
6202
  activeEditorState = previousActiveEditorState;
6035
6203
  isReadOnlyMode = previousReadOnlyMode;
@@ -6074,7 +6242,7 @@ function commitPendingUpdates(editor) {
6074
6242
  // Reconciliation has finished. Now update selection and trigger listeners.
6075
6243
  // ======
6076
6244
 
6077
- const domSelection = headless ? null : getDOMSelection(); // Attempt to update the DOM selection, including focusing of the root element,
6245
+ const domSelection = shouldSkipDOM ? null : getDOMSelection(); // Attempt to update the DOM selection, including focusing of the root element,
6078
6246
  // and scroll into view if needed.
6079
6247
 
6080
6248
  if (editor._editable && // domSelection will be null in headless
@@ -6083,7 +6251,25 @@ function commitPendingUpdates(editor) {
6083
6251
  activeEditorState = pendingEditorState;
6084
6252
 
6085
6253
  try {
6086
- updateDOMSelection(currentSelection, pendingSelection, editor, domSelection, tags, rootElement, dirtyLeavesCount);
6254
+ if (observer !== null) {
6255
+ observer.disconnect();
6256
+ }
6257
+
6258
+ if (needsUpdate || pendingSelection === null || pendingSelection.dirty) {
6259
+ const blockCursorElement = editor._blockCursorElement;
6260
+
6261
+ if (blockCursorElement !== null) {
6262
+ removeDOMBlockCursorElement(blockCursorElement, editor, rootElement);
6263
+ }
6264
+
6265
+ updateDOMSelection(currentSelection, pendingSelection, editor, domSelection, tags, rootElement, dirtyLeavesCount);
6266
+ }
6267
+
6268
+ updateDOMBlockCursorElement(editor, rootElement, pendingSelection);
6269
+
6270
+ if (observer !== null) {
6271
+ observer.observe(rootElement, observerOptions);
6272
+ }
6087
6273
  } finally {
6088
6274
  activeEditor = previousActiveEditor;
6089
6275
  activeEditorState = previousActiveEditorState;
@@ -6384,7 +6570,10 @@ function beginUpdate(editor, updateFn, options) {
6384
6570
  infiniteTransformCount = 0;
6385
6571
  }
6386
6572
 
6387
- const shouldUpdate = editor._dirtyType !== NO_DIRTY_NODES || editorStateHasDirtySelection(pendingEditorState, editor);
6573
+ const windowObj = editor._window;
6574
+ const windowEvent = windowObj !== null ? window.event : null;
6575
+ const eventType = windowEvent != null ? windowEvent.type : null;
6576
+ const shouldUpdate = editor._dirtyType !== NO_DIRTY_NODES || editorStateHasDirtySelection(pendingEditorState, editor) || editor._blockCursorElement !== null && eventType === 'blur';
6388
6577
 
6389
6578
  if (shouldUpdate) {
6390
6579
  if (pendingEditorState._flushSync) {
@@ -6451,29 +6640,20 @@ function removeNode(nodeToRemove, restoreSelection, preserveEmptyParent) {
6451
6640
  }
6452
6641
  }
6453
6642
 
6454
- const writableParent = parent.getWritable();
6455
- const parentChildren = writableParent.__children;
6456
- const index = parentChildren.indexOf(key);
6457
-
6458
- if (index === -1) {
6459
- {
6460
- throw Error(`Node is not a child of its parent`);
6461
- }
6462
- }
6463
-
6464
- const writableNodeToRemove = nodeToRemove.getWritable();
6465
- internalMarkSiblingsAsDirty(nodeToRemove);
6466
- removeFromParent(writableNodeToRemove);
6467
-
6468
6643
  if ($isRangeSelection(selection) && restoreSelection && !selectionMoved) {
6644
+ // Doing this is O(n) so lets avoid it unless we need to do it
6645
+ const index = nodeToRemove.getIndexWithinParent();
6646
+ removeFromParent(nodeToRemove);
6469
6647
  $updateElementSelectionOnCreateDeleteNode(selection, parent, index, -1);
6648
+ } else {
6649
+ removeFromParent(nodeToRemove);
6470
6650
  }
6471
6651
 
6472
6652
  if (!preserveEmptyParent && !$isRootOrShadowRoot(parent) && !parent.canBeEmpty() && parent.isEmpty()) {
6473
6653
  removeNode(parent, restoreSelection);
6474
6654
  }
6475
6655
 
6476
- if ($isRootNode(parent) && parent.isEmpty()) {
6656
+ if (restoreSelection && $isRootNode(parent) && parent.isEmpty()) {
6477
6657
  parent.selectEnd();
6478
6658
  }
6479
6659
  }
@@ -6572,8 +6752,19 @@ class LexicalNode {
6572
6752
  return -1;
6573
6753
  }
6574
6754
 
6575
- const children = parent.__children;
6576
- return children.indexOf(this.__key);
6755
+ let node = parent.getFirstChild();
6756
+ let index = 0;
6757
+
6758
+ while (node !== null) {
6759
+ if (this.is(node)) {
6760
+ return index;
6761
+ }
6762
+
6763
+ index++;
6764
+ node = node.getNextSibling();
6765
+ }
6766
+
6767
+ return -1;
6577
6768
  }
6578
6769
 
6579
6770
  getParent() {
@@ -6651,62 +6842,44 @@ class LexicalNode {
6651
6842
  }
6652
6843
 
6653
6844
  getPreviousSibling() {
6654
- const parent = this.getParent();
6655
-
6656
- if (parent === null) {
6657
- return null;
6658
- }
6659
-
6660
- const children = parent.__children;
6661
- const index = children.indexOf(this.__key);
6662
-
6663
- if (index <= 0) {
6664
- return null;
6665
- }
6666
-
6667
- return $getNodeByKey(children[index - 1]);
6845
+ const self = this.getLatest();
6846
+ const prevKey = self.__prev;
6847
+ return prevKey === null ? null : $getNodeByKey(prevKey);
6668
6848
  }
6669
6849
 
6670
6850
  getPreviousSiblings() {
6671
- const parent = this.getParent();
6851
+ const siblings = [];
6852
+ const parent = this.getParentOrThrow();
6853
+ let node = parent.getFirstChild();
6672
6854
 
6673
- if (parent === null) {
6674
- return [];
6855
+ while (node !== null) {
6856
+ if (node.is(this)) {
6857
+ break;
6858
+ }
6859
+
6860
+ siblings.push(node);
6861
+ node = node.getNextSibling();
6675
6862
  }
6676
6863
 
6677
- const children = parent.__children;
6678
- const index = children.indexOf(this.__key);
6679
- return children.slice(0, index).map(childKey => $getNodeByKeyOrThrow(childKey));
6864
+ return siblings;
6680
6865
  }
6681
6866
 
6682
6867
  getNextSibling() {
6683
- const parent = this.getParent();
6684
-
6685
- if (parent === null) {
6686
- return null;
6687
- }
6688
-
6689
- const children = parent.__children;
6690
- const childrenLength = children.length;
6691
- const index = children.indexOf(this.__key);
6692
-
6693
- if (index >= childrenLength - 1) {
6694
- return null;
6695
- }
6696
-
6697
- return $getNodeByKey(children[index + 1]);
6868
+ const self = this.getLatest();
6869
+ const nextKey = self.__next;
6870
+ return nextKey === null ? null : $getNodeByKey(nextKey);
6698
6871
  }
6699
6872
 
6700
6873
  getNextSiblings() {
6701
- const parent = this.getParent();
6874
+ const siblings = [];
6875
+ let node = this.getNextSibling();
6702
6876
 
6703
- if (parent === null) {
6704
- return [];
6877
+ while (node !== null) {
6878
+ siblings.push(node);
6879
+ node = node.getNextSibling();
6705
6880
  }
6706
6881
 
6707
- const children = parent.__children;
6708
- const index = children.indexOf(this.__key);
6709
- return children.slice(index + 1).map(childKey => $getNodeByKeyOrThrow(childKey));
6882
+ return siblings;
6710
6883
  }
6711
6884
 
6712
6885
  getCommonAncestor(node) {
@@ -6767,7 +6940,7 @@ class LexicalNode {
6767
6940
  const parent = node.getParentOrThrow();
6768
6941
 
6769
6942
  if (parent === commonAncestor) {
6770
- indexA = parent.__children.indexOf(node.__key);
6943
+ indexA = node.getIndexWithinParent();
6771
6944
  break;
6772
6945
  }
6773
6946
 
@@ -6780,7 +6953,7 @@ class LexicalNode {
6780
6953
  const parent = node.getParentOrThrow();
6781
6954
 
6782
6955
  if (parent === commonAncestor) {
6783
- indexB = parent.__children.indexOf(node.__key);
6956
+ indexB = node.getIndexWithinParent();
6784
6957
  break;
6785
6958
  }
6786
6959
 
@@ -6930,7 +7103,6 @@ class LexicalNode {
6930
7103
  mutableNode.__prev = latestNode.__prev;
6931
7104
 
6932
7105
  if ($isElementNode(latestNode) && $isElementNode(mutableNode)) {
6933
- mutableNode.__children = Array.from(latestNode.__children);
6934
7106
  mutableNode.__first = latestNode.__first;
6935
7107
  mutableNode.__last = latestNode.__last;
6936
7108
  mutableNode.__size = latestNode.__size;
@@ -7008,31 +7180,45 @@ class LexicalNode {
7008
7180
  removeNode(this, true, preserveEmptyParent);
7009
7181
  }
7010
7182
 
7011
- replace(replaceWith) {
7183
+ replace(replaceWith, restoreSelection = true) {
7012
7184
  errorOnReadOnly();
7013
7185
  errorOnInsertTextNodeOnRoot(this, replaceWith);
7186
+ const self = this.getLatest();
7014
7187
  const toReplaceKey = this.__key;
7188
+ const key = replaceWith.__key;
7015
7189
  const writableReplaceWith = replaceWith.getWritable();
7190
+ const writableParent = this.getParentOrThrow().getWritable();
7191
+ const size = writableParent.__size;
7016
7192
  removeFromParent(writableReplaceWith);
7017
- const newParent = this.getParentOrThrow();
7018
- const writableParent = newParent.getWritable();
7019
- const children = writableParent.__children;
7020
- const index = children.indexOf(this.__key);
7021
- const newKey = writableReplaceWith.__key;
7193
+ const prevSibling = self.getPreviousSibling();
7194
+ const nextSibling = self.getNextSibling();
7195
+ const prevKey = self.__prev;
7196
+ const nextKey = self.__next;
7197
+ const parentKey = self.__parent;
7198
+ removeNode(self, false);
7022
7199
 
7023
- if (index === -1) {
7024
- {
7025
- throw Error(`Node is not a child of its parent`);
7026
- }
7200
+ if (prevSibling === null) {
7201
+ writableParent.__first = key;
7202
+ } else {
7203
+ const writablePrevSibling = prevSibling.getWritable();
7204
+ writablePrevSibling.__next = key;
7027
7205
  }
7028
7206
 
7029
- children.splice(index, 0, newKey);
7030
- writableReplaceWith.__parent = newParent.__key;
7031
- removeNode(this, false);
7032
- internalMarkSiblingsAsDirty(writableReplaceWith);
7207
+ writableReplaceWith.__prev = prevKey;
7208
+
7209
+ if (nextSibling === null) {
7210
+ writableParent.__last = key;
7211
+ } else {
7212
+ const writableNextSibling = nextSibling.getWritable();
7213
+ writableNextSibling.__prev = key;
7214
+ }
7215
+
7216
+ writableReplaceWith.__next = nextKey;
7217
+ writableReplaceWith.__parent = parentKey;
7218
+ writableParent.__size = size;
7033
7219
  const selection = $getSelection();
7034
7220
 
7035
- if ($isRangeSelection(selection)) {
7221
+ if ($isRangeSelection(selection) && restoreSelection) {
7036
7222
  const anchor = selection.anchor;
7037
7223
  const focus = selection.focus;
7038
7224
 
@@ -7046,24 +7232,25 @@ class LexicalNode {
7046
7232
  }
7047
7233
 
7048
7234
  if ($getCompositionKey() === toReplaceKey) {
7049
- $setCompositionKey(newKey);
7235
+ $setCompositionKey(key);
7050
7236
  }
7051
7237
 
7052
7238
  return writableReplaceWith;
7053
7239
  }
7054
7240
 
7055
- insertAfter(nodeToInsert) {
7241
+ insertAfter(nodeToInsert, restoreSelection = true) {
7056
7242
  errorOnReadOnly();
7057
7243
  errorOnInsertTextNodeOnRoot(this, nodeToInsert);
7058
7244
  const writableSelf = this.getWritable();
7059
7245
  const writableNodeToInsert = nodeToInsert.getWritable();
7060
7246
  const oldParent = writableNodeToInsert.getParent();
7061
7247
  const selection = $getSelection();
7062
- const oldIndex = nodeToInsert.getIndexWithinParent();
7063
7248
  let elementAnchorSelectionOnNode = false;
7064
7249
  let elementFocusSelectionOnNode = false;
7065
7250
 
7066
7251
  if (oldParent !== null) {
7252
+ // TODO: this is O(n), can we improve?
7253
+ const oldIndex = nodeToInsert.getIndexWithinParent();
7067
7254
  removeFromParent(writableNodeToInsert);
7068
7255
 
7069
7256
  if ($isRangeSelection(selection)) {
@@ -7075,22 +7262,26 @@ class LexicalNode {
7075
7262
  }
7076
7263
  }
7077
7264
 
7265
+ const nextSibling = this.getNextSibling();
7078
7266
  const writableParent = this.getParentOrThrow().getWritable();
7079
7267
  const insertKey = writableNodeToInsert.__key;
7080
- writableNodeToInsert.__parent = writableSelf.__parent;
7081
- const children = writableParent.__children;
7082
- const index = children.indexOf(writableSelf.__key);
7268
+ const nextKey = writableSelf.__next;
7083
7269
 
7084
- if (index === -1) {
7085
- {
7086
- throw Error(`Node is not a child of its parent`);
7087
- }
7270
+ if (nextSibling === null) {
7271
+ writableParent.__last = insertKey;
7272
+ } else {
7273
+ const writableNextSibling = nextSibling.getWritable();
7274
+ writableNextSibling.__prev = insertKey;
7088
7275
  }
7089
7276
 
7090
- children.splice(index + 1, 0, insertKey);
7091
- internalMarkSiblingsAsDirty(writableNodeToInsert);
7277
+ writableParent.__size++;
7278
+ writableSelf.__next = insertKey;
7279
+ writableNodeToInsert.__next = nextKey;
7280
+ writableNodeToInsert.__prev = writableSelf.__key;
7281
+ writableNodeToInsert.__parent = writableSelf.__parent;
7092
7282
 
7093
- if ($isRangeSelection(selection)) {
7283
+ if (restoreSelection && $isRangeSelection(selection)) {
7284
+ const index = this.getIndexWithinParent();
7094
7285
  $updateElementSelectionOnCreateDeleteNode(selection, writableParent, index + 1);
7095
7286
  const writableParentKey = writableParent.__key;
7096
7287
 
@@ -7106,30 +7297,36 @@ class LexicalNode {
7106
7297
  return nodeToInsert;
7107
7298
  }
7108
7299
 
7109
- insertBefore(nodeToInsert) {
7300
+ insertBefore(nodeToInsert, restoreSelection = true) {
7110
7301
  errorOnReadOnly();
7111
7302
  errorOnInsertTextNodeOnRoot(this, nodeToInsert);
7112
7303
  const writableSelf = this.getWritable();
7113
7304
  const writableNodeToInsert = nodeToInsert.getWritable();
7305
+ const insertKey = writableNodeToInsert.__key;
7114
7306
  removeFromParent(writableNodeToInsert);
7307
+ const prevSibling = this.getPreviousSibling();
7115
7308
  const writableParent = this.getParentOrThrow().getWritable();
7116
- const insertKey = writableNodeToInsert.__key;
7117
- writableNodeToInsert.__parent = writableSelf.__parent;
7118
- const children = writableParent.__children;
7119
- const index = children.indexOf(writableSelf.__key);
7309
+ const prevKey = writableSelf.__prev; // TODO: this is O(n), can we improve?
7120
7310
 
7121
- if (index === -1) {
7122
- {
7123
- throw Error(`Node is not a child of its parent`);
7124
- }
7311
+ const index = this.getIndexWithinParent();
7312
+
7313
+ if (prevSibling === null) {
7314
+ writableParent.__first = insertKey;
7315
+ } else {
7316
+ const writablePrevSibling = prevSibling.getWritable();
7317
+ writablePrevSibling.__next = insertKey;
7125
7318
  }
7126
7319
 
7127
- children.splice(index, 0, insertKey);
7128
- internalMarkSiblingsAsDirty(writableNodeToInsert);
7320
+ writableParent.__size++;
7321
+ writableSelf.__prev = insertKey;
7322
+ writableNodeToInsert.__prev = prevKey;
7323
+ writableNodeToInsert.__next = writableSelf.__key;
7324
+ writableNodeToInsert.__parent = writableSelf.__parent;
7129
7325
  const selection = $getSelection();
7130
7326
 
7131
- if ($isRangeSelection(selection)) {
7132
- $updateElementSelectionOnCreateDeleteNode(selection, writableParent, index);
7327
+ if (restoreSelection && $isRangeSelection(selection)) {
7328
+ const parent = this.getParentOrThrow();
7329
+ $updateElementSelectionOnCreateDeleteNode(selection, parent, index);
7133
7330
  }
7134
7331
 
7135
7332
  return nodeToInsert;
@@ -7256,13 +7453,9 @@ class ElementNode extends LexicalNode {
7256
7453
 
7257
7454
  /** @internal */
7258
7455
 
7259
- /** @internal */
7260
-
7261
7456
  /** @internal */
7262
7457
  constructor(key) {
7263
- super(key); // TODO: remove children and switch to using first/last as part of linked list work
7264
-
7265
- this.__children = [];
7458
+ super(key);
7266
7459
  this.__first = null;
7267
7460
  this.__last = null;
7268
7461
  this.__size = 0;
@@ -7287,28 +7480,32 @@ class ElementNode extends LexicalNode {
7287
7480
  }
7288
7481
 
7289
7482
  getChildren() {
7290
- const self = this.getLatest();
7291
- const children = self.__children;
7292
- const childrenNodes = [];
7483
+ const children = [];
7484
+ let child = this.getFirstChild();
7293
7485
 
7294
- for (let i = 0; i < children.length; i++) {
7295
- const childNode = $getNodeByKey(children[i]);
7296
-
7297
- if (childNode !== null) {
7298
- childrenNodes.push(childNode);
7299
- }
7486
+ while (child !== null) {
7487
+ children.push(child);
7488
+ child = child.getNextSibling();
7300
7489
  }
7301
7490
 
7302
- return childrenNodes;
7491
+ return children;
7303
7492
  }
7304
7493
 
7305
7494
  getChildrenKeys() {
7306
- return this.getLatest().__children;
7495
+ const children = [];
7496
+ let child = this.getFirstChild();
7497
+
7498
+ while (child !== null) {
7499
+ children.push(child.__key);
7500
+ child = child.getNextSibling();
7501
+ }
7502
+
7503
+ return children;
7307
7504
  }
7308
7505
 
7309
7506
  getChildrenSize() {
7310
7507
  const self = this.getLatest();
7311
- return self.__children.length;
7508
+ return self.__size;
7312
7509
  }
7313
7510
 
7314
7511
  isEmpty() {
@@ -7323,24 +7520,25 @@ class ElementNode extends LexicalNode {
7323
7520
 
7324
7521
  isLastChild() {
7325
7522
  const self = this.getLatest();
7326
- const parent = self.getParentOrThrow();
7327
- return parent.getLastChild() === self;
7523
+ const parentLastChild = this.getParentOrThrow().getLastChild();
7524
+ return parentLastChild !== null && parentLastChild.is(self);
7328
7525
  }
7329
7526
 
7330
7527
  getAllTextNodes() {
7331
7528
  const textNodes = [];
7332
- const self = this.getLatest();
7333
- const children = self.__children;
7529
+ let child = this.getFirstChild();
7334
7530
 
7335
- for (let i = 0; i < children.length; i++) {
7336
- const childNode = $getNodeByKey(children[i]);
7531
+ while (child !== null) {
7532
+ if ($isTextNode(child)) {
7533
+ textNodes.push(child);
7534
+ }
7337
7535
 
7338
- if ($isTextNode(childNode)) {
7339
- textNodes.push(childNode);
7340
- } else if ($isElementNode(childNode)) {
7341
- const subChildrenNodes = childNode.getAllTextNodes();
7536
+ if ($isElementNode(child)) {
7537
+ const subChildrenNodes = child.getAllTextNodes();
7342
7538
  textNodes.push(...subChildrenNodes);
7343
7539
  }
7540
+
7541
+ child = child.getNextSibling();
7344
7542
  }
7345
7543
 
7346
7544
  return textNodes;
@@ -7400,14 +7598,8 @@ class ElementNode extends LexicalNode {
7400
7598
 
7401
7599
  getFirstChild() {
7402
7600
  const self = this.getLatest();
7403
- const children = self.__children;
7404
- const childrenLength = children.length;
7405
-
7406
- if (childrenLength === 0) {
7407
- return null;
7408
- }
7409
-
7410
- return $getNodeByKey(children[0]);
7601
+ const firstKey = self.__first;
7602
+ return firstKey === null ? null : $getNodeByKey(firstKey);
7411
7603
  }
7412
7604
 
7413
7605
  getFirstChildOrThrow() {
@@ -7424,14 +7616,8 @@ class ElementNode extends LexicalNode {
7424
7616
 
7425
7617
  getLastChild() {
7426
7618
  const self = this.getLatest();
7427
- const children = self.__children;
7428
- const childrenLength = children.length;
7429
-
7430
- if (childrenLength === 0) {
7431
- return null;
7432
- }
7433
-
7434
- return $getNodeByKey(children[childrenLength - 1]);
7619
+ const lastKey = self.__last;
7620
+ return lastKey === null ? null : $getNodeByKey(lastKey);
7435
7621
  }
7436
7622
 
7437
7623
  getLastChildOrThrow() {
@@ -7447,15 +7633,39 @@ class ElementNode extends LexicalNode {
7447
7633
  }
7448
7634
 
7449
7635
  getChildAtIndex(index) {
7450
- const self = this.getLatest();
7451
- const children = self.__children;
7452
- const key = children[index];
7636
+ const size = this.getChildrenSize();
7637
+ let node;
7638
+ let i;
7639
+
7640
+ if (index < size / 2) {
7641
+ node = this.getFirstChild();
7642
+ i = 0;
7643
+
7644
+ while (node !== null && i <= index) {
7645
+ if (i === index) {
7646
+ return node;
7647
+ }
7648
+
7649
+ node = node.getNextSibling();
7650
+ i++;
7651
+ }
7453
7652
 
7454
- if (key === undefined) {
7455
7653
  return null;
7456
7654
  }
7457
7655
 
7458
- return $getNodeByKey(key);
7656
+ node = this.getLastChild();
7657
+ i = size - 1;
7658
+
7659
+ while (node !== null && i >= index) {
7660
+ if (i === index) {
7661
+ return node;
7662
+ }
7663
+
7664
+ node = node.getPreviousSibling();
7665
+ i--;
7666
+ }
7667
+
7668
+ return null;
7459
7669
  }
7460
7670
 
7461
7671
  getTextContent() {
@@ -7497,6 +7707,22 @@ class ElementNode extends LexicalNode {
7497
7707
  let focusOffset = _focusOffset;
7498
7708
  const childrenCount = this.getChildrenSize();
7499
7709
 
7710
+ if (!this.canBeEmpty()) {
7711
+ if (_anchorOffset === 0 && _focusOffset === 0) {
7712
+ const firstChild = this.getFirstChild();
7713
+
7714
+ if ($isTextNode(firstChild) || $isElementNode(firstChild)) {
7715
+ return firstChild.select(0, 0);
7716
+ }
7717
+ } else if ((_anchorOffset === undefined || _anchorOffset === childrenCount) && (_focusOffset === undefined || _focusOffset === childrenCount)) {
7718
+ const lastChild = this.getLastChild();
7719
+
7720
+ if ($isTextNode(lastChild) || $isElementNode(lastChild)) {
7721
+ return lastChild.select();
7722
+ }
7723
+ }
7724
+ }
7725
+
7500
7726
  if (anchorOffset === undefined) {
7501
7727
  anchorOffset = childrenCount;
7502
7728
  }
@@ -7578,55 +7804,108 @@ class ElementNode extends LexicalNode {
7578
7804
  }
7579
7805
 
7580
7806
  splice(start, deleteCount, nodesToInsert) {
7807
+ const nodesToInsertLength = nodesToInsert.length;
7808
+ const oldSize = this.getChildrenSize();
7581
7809
  const writableSelf = this.getWritable();
7582
7810
  const writableSelfKey = writableSelf.__key;
7583
- const writableSelfChildren = writableSelf.__children;
7584
- const nodesToInsertLength = nodesToInsert.length;
7585
- const nodesToInsertKeys = []; // Remove nodes to insert from their previous parent
7811
+ const nodesToInsertKeys = [];
7812
+ const nodesToRemoveKeys = [];
7813
+ const nodeAfterRange = this.getChildAtIndex(start + deleteCount);
7814
+ let nodeBeforeRange = null;
7815
+ let newSize = oldSize - deleteCount + nodesToInsertLength;
7816
+
7817
+ if (start !== 0) {
7818
+ if (start === oldSize) {
7819
+ nodeBeforeRange = this.getLastChild();
7820
+ } else {
7821
+ const node = this.getChildAtIndex(start);
7822
+
7823
+ if (node !== null) {
7824
+ nodeBeforeRange = node.getPreviousSibling();
7825
+ }
7826
+ }
7827
+ }
7828
+
7829
+ if (deleteCount > 0) {
7830
+ let nodeToDelete = nodeBeforeRange === null ? this.getFirstChild() : nodeBeforeRange.getNextSibling();
7831
+
7832
+ for (let i = 0; i < deleteCount; i++) {
7833
+ if (nodeToDelete === null) {
7834
+ {
7835
+ throw Error(`splice: sibling not found`);
7836
+ }
7837
+ }
7838
+
7839
+ const nextSibling = nodeToDelete.getNextSibling();
7840
+ const nodeKeyToDelete = nodeToDelete.__key;
7841
+ const writableNodeToDelete = nodeToDelete.getWritable();
7842
+ removeFromParent(writableNodeToDelete);
7843
+ nodesToRemoveKeys.push(nodeKeyToDelete);
7844
+ nodeToDelete = nextSibling;
7845
+ }
7846
+ }
7847
+
7848
+ let prevNode = nodeBeforeRange;
7586
7849
 
7587
7850
  for (let i = 0; i < nodesToInsertLength; i++) {
7588
7851
  const nodeToInsert = nodesToInsert[i];
7852
+
7853
+ if (prevNode !== null && nodeToInsert.is(prevNode)) {
7854
+ nodeBeforeRange = prevNode = prevNode.getPreviousSibling();
7855
+ }
7856
+
7589
7857
  const writableNodeToInsert = nodeToInsert.getWritable();
7590
7858
 
7859
+ if (writableNodeToInsert.__parent === writableSelfKey) {
7860
+ newSize--;
7861
+ }
7862
+
7863
+ removeFromParent(writableNodeToInsert);
7864
+ const nodeKeyToInsert = nodeToInsert.__key;
7865
+
7866
+ if (prevNode === null) {
7867
+ writableSelf.__first = nodeKeyToInsert;
7868
+ writableNodeToInsert.__prev = null;
7869
+ } else {
7870
+ const writablePrevNode = prevNode.getWritable();
7871
+ writablePrevNode.__next = nodeKeyToInsert;
7872
+ writableNodeToInsert.__prev = writablePrevNode.__key;
7873
+ }
7874
+
7591
7875
  if (nodeToInsert.__key === writableSelfKey) {
7592
7876
  {
7593
7877
  throw Error(`append: attempting to append self`);
7594
7878
  }
7595
- }
7879
+ } // Set child parent to self
7596
7880
 
7597
- removeFromParent(writableNodeToInsert); // Set child parent to self
7598
7881
 
7599
7882
  writableNodeToInsert.__parent = writableSelfKey;
7600
- const newKey = writableNodeToInsert.__key;
7601
- nodesToInsertKeys.push(newKey);
7602
- } // Mark range edges siblings as dirty
7603
-
7604
-
7605
- const nodeBeforeRange = this.getChildAtIndex(start - 1);
7606
-
7607
- if (nodeBeforeRange) {
7608
- internalMarkNodeAsDirty(nodeBeforeRange);
7883
+ nodesToInsertKeys.push(nodeKeyToInsert);
7884
+ prevNode = nodeToInsert;
7609
7885
  }
7610
7886
 
7611
- const nodeAfterRange = this.getChildAtIndex(start + deleteCount);
7612
-
7613
- if (nodeAfterRange) {
7614
- internalMarkNodeAsDirty(nodeAfterRange);
7615
- } // Remove defined range of children
7616
-
7887
+ if (start + deleteCount === oldSize) {
7888
+ if (prevNode !== null) {
7889
+ const writablePrevNode = prevNode.getWritable();
7890
+ writablePrevNode.__next = null;
7891
+ writableSelf.__last = prevNode.__key;
7892
+ }
7893
+ } else if (nodeAfterRange !== null) {
7894
+ const writableNodeAfterRange = nodeAfterRange.getWritable();
7617
7895
 
7618
- let nodesToRemoveKeys; // Using faster push when only appending nodes
7896
+ if (prevNode !== null) {
7897
+ const writablePrevNode = prevNode.getWritable();
7898
+ writableNodeAfterRange.__prev = prevNode.__key;
7899
+ writablePrevNode.__next = nodeAfterRange.__key;
7900
+ } else {
7901
+ writableNodeAfterRange.__prev = null;
7902
+ }
7903
+ }
7619
7904
 
7620
- if (start === writableSelfChildren.length) {
7621
- writableSelfChildren.push(...nodesToInsertKeys);
7622
- nodesToRemoveKeys = [];
7623
- } else {
7624
- nodesToRemoveKeys = writableSelfChildren.splice(start, deleteCount, ...nodesToInsertKeys);
7625
- } // In case of deletion we need to adjust selection, unlink removed nodes
7905
+ writableSelf.__size = newSize; // In case of deletion we need to adjust selection, unlink removed nodes
7626
7906
  // and clean up node itself if it becomes empty. None of these needed
7627
7907
  // for insertion-only cases
7628
7908
 
7629
-
7630
7909
  if (nodesToRemoveKeys.length) {
7631
7910
  // Adjusting selection, in case node that was anchor/focus will be deleted
7632
7911
  const selection = $getSelection();
@@ -7645,22 +7924,10 @@ class ElementNode extends LexicalNode {
7645
7924
 
7646
7925
  if (isPointRemoved(focus, nodesToRemoveKeySet, nodesToInsertKeySet)) {
7647
7926
  moveSelectionPointToSibling(focus, focus.getNode(), this, nodeBeforeRange, nodeAfterRange);
7648
- } // Unlink removed nodes from current parent
7649
-
7650
-
7651
- const nodesToRemoveKeysLength = nodesToRemoveKeys.length;
7652
-
7653
- for (let i = 0; i < nodesToRemoveKeysLength; i++) {
7654
- const nodeToRemove = $getNodeByKey(nodesToRemoveKeys[i]);
7655
-
7656
- if (nodeToRemove != null) {
7657
- const writableNodeToRemove = nodeToRemove.getWritable();
7658
- writableNodeToRemove.__parent = null;
7659
- }
7660
7927
  } // Cleanup if node can't be empty
7661
7928
 
7662
7929
 
7663
- if (writableSelfChildren.length === 0 && !this.canBeEmpty() && !$isRootOrShadowRoot(this)) {
7930
+ if (newSize === 0 && !this.canBeEmpty() && !$isRootOrShadowRoot(this)) {
7664
7931
  this.remove();
7665
7932
  }
7666
7933
  }
@@ -7682,7 +7949,7 @@ class ElementNode extends LexicalNode {
7682
7949
  } // These are intended to be extends for specific element heuristics.
7683
7950
 
7684
7951
 
7685
- insertNewAfter(selection) {
7952
+ insertNewAfter(selection, restoreSelection) {
7686
7953
  return null;
7687
7954
  }
7688
7955
 
@@ -8215,6 +8482,13 @@ function createTextInnerDOM(innerDOM, node, innerTag, format, text, config) {
8215
8482
 
8216
8483
 
8217
8484
  class TextNode extends LexicalNode {
8485
+ /** @internal */
8486
+
8487
+ /** @internal */
8488
+
8489
+ /** @internal */
8490
+
8491
+ /** @internal */
8218
8492
  static getType() {
8219
8493
  return 'text';
8220
8494
  }
@@ -8621,7 +8895,6 @@ class TextNode extends LexicalNode {
8621
8895
 
8622
8896
  const firstPart = parts[0];
8623
8897
  const parent = self.getParentOrThrow();
8624
- const parentKey = parent.__key;
8625
8898
  let writableNode;
8626
8899
  const format = self.getFormat();
8627
8900
  const style = self.getStyle();
@@ -8631,7 +8904,6 @@ class TextNode extends LexicalNode {
8631
8904
  if (self.isSegmented()) {
8632
8905
  // Create a new TextNode
8633
8906
  writableNode = $createTextNode(firstPart);
8634
- writableNode.__parent = parentKey;
8635
8907
  writableNode.__format = format;
8636
8908
  writableNode.__style = style;
8637
8909
  writableNode.__detail = detail;
@@ -8680,22 +8952,19 @@ class TextNode extends LexicalNode {
8680
8952
  }
8681
8953
 
8682
8954
  textSize = nextTextSize;
8683
- sibling.__parent = parentKey;
8684
8955
  splitNodes.push(sibling);
8685
8956
  } // Insert the nodes into the parent's children
8686
8957
 
8687
8958
 
8688
8959
  internalMarkSiblingsAsDirty(this);
8689
8960
  const writableParent = parent.getWritable();
8690
- const writableParentChildren = writableParent.__children;
8691
- const insertionIndex = writableParentChildren.indexOf(key);
8692
- const splitNodesKeys = splitNodes.map(splitNode => splitNode.__key);
8961
+ const insertionIndex = this.getIndexWithinParent();
8693
8962
 
8694
8963
  if (hasReplacedSelf) {
8695
- writableParentChildren.splice(insertionIndex, 0, ...splitNodesKeys);
8964
+ writableParent.splice(insertionIndex, 0, splitNodes);
8696
8965
  this.remove();
8697
8966
  } else {
8698
- writableParentChildren.splice(insertionIndex, 1, ...splitNodesKeys);
8967
+ writableParent.splice(insertionIndex, 1, splitNodes);
8699
8968
  }
8700
8969
 
8701
8970
  if ($isRangeSelection(selection)) {
@@ -8969,11 +9238,11 @@ class ParagraphNode extends ElementNode {
8969
9238
  } // Mutation
8970
9239
 
8971
9240
 
8972
- insertNewAfter() {
9241
+ insertNewAfter(_, restoreSelection) {
8973
9242
  const newElement = $createParagraphNode();
8974
9243
  const direction = this.getDirection();
8975
9244
  newElement.setDirection(direction);
8976
- this.insertAfter(newElement);
9245
+ this.insertAfter(newElement, restoreSelection);
8977
9246
  return newElement;
8978
9247
  }
8979
9248
 
@@ -9047,6 +9316,7 @@ function resetEditor(editor, prevRootElement, nextRootElement, pendingEditorStat
9047
9316
  editor._normalizedNodes = new Set();
9048
9317
  editor._updateTags = new Set();
9049
9318
  editor._updates = [];
9319
+ editor._blockCursorElement = null;
9050
9320
  const observer = editor._observer;
9051
9321
 
9052
9322
  if (observer !== null) {
@@ -9235,6 +9505,7 @@ class LexicalEditor {
9235
9505
  this._editable = true;
9236
9506
  this._headless = parentEditor !== null && parentEditor._headless;
9237
9507
  this._window = null;
9508
+ this._blockCursorElement = null;
9238
9509
  }
9239
9510
 
9240
9511
  isComposing() {
@@ -9557,7 +9828,7 @@ class LexicalEditor {
9557
9828
  * LICENSE file in the root directory of this source tree.
9558
9829
  *
9559
9830
  */
9560
- const VERSION = '0.6.5';
9831
+ const VERSION = '0.7.0';
9561
9832
 
9562
9833
  /**
9563
9834
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -9618,7 +9889,7 @@ exports.$createNodeSelection = $createNodeSelection;
9618
9889
  exports.$createParagraphNode = $createParagraphNode;
9619
9890
  exports.$createRangeSelection = $createRangeSelection;
9620
9891
  exports.$createTextNode = $createTextNode;
9621
- exports.$getDecoratorNode = $getDecoratorNode;
9892
+ exports.$getAdjacentNode = $getAdjacentNode;
9622
9893
  exports.$getNearestNodeFromDOMNode = $getNearestNodeFromDOMNode;
9623
9894
  exports.$getNearestRootOrShadowRoot = $getNearestRootOrShadowRoot;
9624
9895
  exports.$getNodeByKey = $getNodeByKey;