lexical 0.3.9 → 0.4.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
@@ -196,9 +196,9 @@ function updateTimeStamp(event) {
196
196
  lastTextEntryTimeStamp = event.timeStamp;
197
197
  }
198
198
 
199
- function initTextEntryListener() {
199
+ function initTextEntryListener(editor) {
200
200
  if (lastTextEntryTimeStamp === 0) {
201
- window.addEventListener('textInput', updateTimeStamp, true);
201
+ getWindow(editor).addEventListener('textInput', updateTimeStamp, true);
202
202
  }
203
203
  }
204
204
 
@@ -394,7 +394,7 @@ function $flushMutations$1(editor, mutations, observer) {
394
394
  $setSelection(selection);
395
395
  }
396
396
 
397
- if (IS_FIREFOX && isFirefoxClipboardEvents()) {
397
+ if (IS_FIREFOX && isFirefoxClipboardEvents(editor)) {
398
398
  selection.insertRawText(possibleTextForFirefoxPaste);
399
399
  }
400
400
  }
@@ -412,7 +412,7 @@ function flushRootMutations(editor) {
412
412
  }
413
413
  }
414
414
  function initMutationObserver(editor) {
415
- initTextEntryListener();
415
+ initTextEntryListener(editor);
416
416
  editor._observer = new MutationObserver((mutations, observer) => {
417
417
  $flushMutations$1(editor, mutations, observer);
418
418
  });
@@ -444,13 +444,15 @@ const scheduleMicroTask = typeof queueMicrotask === 'function' ? queueMicrotask
444
444
  // No window prefix intended (#1400)
445
445
  Promise.resolve().then(fn);
446
446
  };
447
+ function $isSelectionCapturedInDecorator(node) {
448
+ return $isDecoratorNode($getNearestNodeFromDOMNode(node));
449
+ } // TODO change to $ function
447
450
 
448
451
  function isSelectionCapturedInDecoratorInput(anchorDOM) {
449
452
  const activeElement = document.activeElement;
450
453
  const nodeName = activeElement !== null ? activeElement.nodeName : null;
451
454
  return !$isDecoratorNode($getNearestNodeFromDOMNode(anchorDOM)) || nodeName !== 'INPUT' && nodeName !== 'TEXTAREA';
452
455
  }
453
-
454
456
  function isSelectionWithinEditor(editor, anchorDOM, focusDOM) {
455
457
  const rootElement = editor.getRootElement();
456
458
 
@@ -468,7 +470,7 @@ function getNearestEditorFromDOMNode(node) {
468
470
  // @ts-expect-error: internal field
469
471
  const editor = currentNode.__lexicalEditor;
470
472
 
471
- if (editor != null && !editor.isReadOnly()) {
473
+ if (editor != null) {
472
474
  return editor;
473
475
  }
474
476
 
@@ -951,8 +953,8 @@ function $shouldPreventDefaultAndInsertText(selection, text) {
951
953
  const backingAnchorElement = getActiveEditor().getElementByKey(anchorKey);
952
954
  const textLength = text.length;
953
955
  return anchorKey !== focus.key || // If we're working with a non-text node.
954
- !$isTextNode(anchorNode) || // If we are replacing a range with a single character, and not composing.
955
- textLength < 2 && anchor.offset !== focus.offset && !anchorNode.isComposing() || // Any non standard text node.
956
+ !$isTextNode(anchorNode) || // If we are replacing a range with a single character or grapheme, and not composing.
957
+ (textLength < 2 || doesContainGrapheme(text)) && anchor.offset !== focus.offset && !anchorNode.isComposing() || // Any non standard text node.
956
958
  $isTokenOrInertOrSegmented(anchorNode) || // If the text length is more than a single character and we're either
957
959
  // dealing with this in "beforeinput" or where the node has already recently
958
960
  // been changed (thus is dirty).
@@ -1216,8 +1218,8 @@ function $getDecoratorNode(focus, isBackward) {
1216
1218
 
1217
1219
  return null;
1218
1220
  }
1219
- function isFirefoxClipboardEvents() {
1220
- const event = window.event;
1221
+ function isFirefoxClipboardEvents(editor) {
1222
+ const event = getWindow(editor).event;
1221
1223
  const inputType = event && event.inputType;
1222
1224
  return inputType === 'insertFromPaste' || inputType === 'insertFromPasteAsQuotation';
1223
1225
  }
@@ -1254,7 +1256,7 @@ function scrollIntoViewIfNeeded(editor, anchor, rootElement, tags) {
1254
1256
  if (element !== null) {
1255
1257
  const rect = element.getBoundingClientRect();
1256
1258
 
1257
- if (rect.bottom > window.innerHeight) {
1259
+ if (rect.bottom > getWindow(editor).innerHeight) {
1258
1260
  element.scrollIntoView(false);
1259
1261
  } else if (rect.top < 0) {
1260
1262
  element.scrollIntoView();
@@ -1317,6 +1319,22 @@ function $hasAncestor(child, targetNode) {
1317
1319
  return false;
1318
1320
  }
1319
1321
 
1322
+ function getDefaultView(domElem) {
1323
+ const ownerDoc = domElem.ownerDocument;
1324
+ return ownerDoc && ownerDoc.defaultView || null;
1325
+ }
1326
+ function getWindow(editor) {
1327
+ const windowObj = editor._window;
1328
+
1329
+ if (windowObj === null) {
1330
+ {
1331
+ throw Error(`window object not found`);
1332
+ }
1333
+ }
1334
+
1335
+ return windowObj;
1336
+ }
1337
+
1320
1338
  /**
1321
1339
  * Copyright (c) Meta Platforms, Inc. and affiliates.
1322
1340
  *
@@ -1925,9 +1943,6 @@ function reconcileNode(key, parentDOM) {
1925
1943
  if (decorator !== null) {
1926
1944
  reconcileDecorator(key, decorator);
1927
1945
  }
1928
-
1929
- subTreeTextContent += text;
1930
- editorTextContent += text;
1931
1946
  } else if ($isTextNode(nextNode) && !nextNode.isDirectionless()) {
1932
1947
  // Handle text content, for LTR, LTR cases.
1933
1948
  subTreeDirectionedTextContent += text;
@@ -2119,7 +2134,7 @@ function getPrevElementByKeyOrThrow(key) {
2119
2134
  */
2120
2135
  const PASS_THROUGH_COMMAND = Object.freeze({});
2121
2136
  const ANDROID_COMPOSITION_LATENCY = 30;
2122
- const rootElementEvents = [['keydown', onKeyDown], ['compositionstart', onCompositionStart], ['compositionend', onCompositionEnd], ['input', onInput], ['click', onClick], ['cut', PASS_THROUGH_COMMAND], ['copy', PASS_THROUGH_COMMAND], ['dragstart', PASS_THROUGH_COMMAND], ['dragover', PASS_THROUGH_COMMAND], ['dragend', PASS_THROUGH_COMMAND], ['paste', PASS_THROUGH_COMMAND], ['focus', PASS_THROUGH_COMMAND], ['blur', PASS_THROUGH_COMMAND], ['drop', PASS_THROUGH_COMMAND]];
2137
+ const rootElementEvents = [['keydown', onKeyDown], ['mousedown', onMouseDown], ['compositionstart', onCompositionStart], ['compositionend', onCompositionEnd], ['input', onInput], ['click', onClick], ['cut', PASS_THROUGH_COMMAND], ['copy', PASS_THROUGH_COMMAND], ['dragstart', PASS_THROUGH_COMMAND], ['dragover', PASS_THROUGH_COMMAND], ['dragend', PASS_THROUGH_COMMAND], ['paste', PASS_THROUGH_COMMAND], ['focus', PASS_THROUGH_COMMAND], ['blur', PASS_THROUGH_COMMAND], ['drop', PASS_THROUGH_COMMAND]];
2123
2138
 
2124
2139
  if (CAN_USE_BEFORE_INPUT) {
2125
2140
  rootElementEvents.push(['beforeinput', (event, editor) => onBeforeInput(event, editor)]);
@@ -2129,6 +2144,7 @@ let lastKeyDownTimeStamp = 0;
2129
2144
  let lastKeyCode = 0;
2130
2145
  let rootElementsRegistered = 0;
2131
2146
  let isSelectionChangeFromDOMUpdate = false;
2147
+ let isSelectionChangeFromMouseDown = false;
2132
2148
  let isInsertLineBreak = false;
2133
2149
  let isFirefoxEndingComposition = false;
2134
2150
  let collapsedSelectionFormat = [0, 0, 'root', 0];
@@ -2187,7 +2203,7 @@ function onSelectionChange(domSelection, editor, isActive) {
2187
2203
  // instead of getting the format from the anchor node.
2188
2204
 
2189
2205
 
2190
- const windowEvent = window.event;
2206
+ const windowEvent = getWindow(editor).event;
2191
2207
  const currentTimeStamp = windowEvent ? windowEvent.timeStamp : performance.now();
2192
2208
  const [lastFormat, lastOffset, lastKey, timeStamp] = collapsedSelectionFormat;
2193
2209
 
@@ -2246,24 +2262,27 @@ function onClick(event, editor) {
2246
2262
  domSelection.removeAllRanges();
2247
2263
  selection.dirty = true;
2248
2264
  }
2249
- } else if (domSelection && $isNodeSelection(selection)) {
2250
- const domAnchor = domSelection.anchorNode; // If the user is attempting to click selection back onto text, then
2251
- // we should attempt create a range selection.
2252
- // When we click on an empty paragraph node or the end of a paragraph that ends
2253
- // with an image/poll, the nodeType will be ELEMENT_NODE
2254
-
2255
- const allowedNodeType = [DOM_ELEMENT_TYPE, DOM_TEXT_TYPE];
2256
-
2257
- if (domAnchor !== null && allowedNodeType.includes(domAnchor.nodeType)) {
2258
- const newSelection = internalCreateRangeSelection(lastSelection, domSelection, editor);
2259
- $setSelection(newSelection);
2260
- }
2261
2265
  }
2262
2266
 
2263
2267
  dispatchCommand(editor, CLICK_COMMAND, event);
2264
2268
  });
2265
2269
  }
2266
2270
 
2271
+ function onMouseDown(event, editor) {
2272
+ // TODO implement text drag & drop
2273
+ const target = event.target;
2274
+
2275
+ if (target instanceof Node) {
2276
+ updateEditor(editor, () => {
2277
+ // Drag & drop should not recompute selection until mouse up; otherwise the initially
2278
+ // selected content is lost.
2279
+ if (!$isSelectionCapturedInDecorator(target)) {
2280
+ isSelectionChangeFromMouseDown = true;
2281
+ }
2282
+ });
2283
+ }
2284
+ }
2285
+
2267
2286
  function $applyTargetRange(selection, event) {
2268
2287
  if (event.getTargetRanges) {
2269
2288
  const targetRange = event.getTargetRanges()[0];
@@ -2290,7 +2309,7 @@ function onBeforeInput(event, editor) {
2290
2309
  // user has dom.event.clipboardevents.enabled disabled in
2291
2310
  // about:config. In that case, we need to process the
2292
2311
  // pasted content in the DOM mutation phase.
2293
- IS_FIREFOX && isFirefoxClipboardEvents()) {
2312
+ IS_FIREFOX && isFirefoxClipboardEvents(editor)) {
2294
2313
  return;
2295
2314
  } else if (inputType === 'insertCompositionText') {
2296
2315
  return;
@@ -2330,7 +2349,7 @@ function onBeforeInput(event, editor) {
2330
2349
  }
2331
2350
  } else {
2332
2351
  event.preventDefault();
2333
- dispatchCommand(editor, DELETE_CHARACTER_COMMAND, false);
2352
+ dispatchCommand(editor, DELETE_CHARACTER_COMMAND, true);
2334
2353
  }
2335
2354
 
2336
2355
  return;
@@ -2635,6 +2654,11 @@ function onCompositionEnd(event, editor) {
2635
2654
  }
2636
2655
 
2637
2656
  function onKeyDown(event, editor) {
2657
+ if (hasStoppedLexicalPropagation(event)) {
2658
+ return;
2659
+ }
2660
+
2661
+ stopLexicalPropagation(event);
2638
2662
  lastKeyDownTimeStamp = event.timeStamp;
2639
2663
  lastKeyCode = event.keyCode;
2640
2664
 
@@ -2756,16 +2780,40 @@ function getRootElementRemoveHandles(rootElement) {
2756
2780
  const activeNestedEditorsMap = new Map();
2757
2781
 
2758
2782
  function onDocumentSelectionChange(event) {
2759
- const selection = getDOMSelection();
2783
+ const domSelection = getDOMSelection();
2760
2784
 
2761
- if (!selection) {
2785
+ if (domSelection === null) {
2762
2786
  return;
2763
2787
  }
2764
2788
 
2765
- const nextActiveEditor = getNearestEditorFromDOMNode(selection.anchorNode);
2789
+ const nextActiveEditor = getNearestEditorFromDOMNode(domSelection.anchorNode);
2766
2790
 
2767
2791
  if (nextActiveEditor === null) {
2768
2792
  return;
2793
+ }
2794
+
2795
+ if (isSelectionChangeFromMouseDown) {
2796
+ isSelectionChangeFromMouseDown = false;
2797
+ updateEditor(nextActiveEditor, () => {
2798
+ const lastSelection = $getPreviousSelection();
2799
+ const domAnchorNode = domSelection.anchorNode;
2800
+
2801
+ if (domAnchorNode === null) {
2802
+ return;
2803
+ }
2804
+
2805
+ const nodeType = domAnchorNode.nodeType; // If the user is attempting to click selection back onto text, then
2806
+ // we should attempt create a range selection.
2807
+ // When we click on an empty paragraph node or the end of a paragraph that ends
2808
+ // with an image/poll, the nodeType will be ELEMENT_NODE
2809
+
2810
+ if (nodeType !== DOM_ELEMENT_TYPE && nodeType !== DOM_TEXT_TYPE) {
2811
+ return;
2812
+ }
2813
+
2814
+ const newSelection = internalCreateRangeSelection(lastSelection, domSelection, nextActiveEditor);
2815
+ $setSelection(newSelection);
2816
+ });
2769
2817
  } // When editor receives selection change event, we're checking if
2770
2818
  // it has any sibling editors (within same parent editor) that were active
2771
2819
  // before, and trigger selection change on it to nullify selection.
@@ -2778,10 +2826,10 @@ function onDocumentSelectionChange(event) {
2778
2826
  const prevActiveEditor = activeNestedEditor || rootEditor;
2779
2827
 
2780
2828
  if (prevActiveEditor !== nextActiveEditor) {
2781
- onSelectionChange(selection, prevActiveEditor, false);
2829
+ onSelectionChange(domSelection, prevActiveEditor, false);
2782
2830
  }
2783
2831
 
2784
- onSelectionChange(selection, nextActiveEditor, true); // If newly selected editor is nested, then add it to the map, clean map otherwise
2832
+ onSelectionChange(domSelection, nextActiveEditor, true); // If newly selected editor is nested, then add it to the map, clean map otherwise
2785
2833
 
2786
2834
  if (nextActiveEditor !== rootEditor) {
2787
2835
  activeNestedEditorsMap.set(rootEditorKey, nextActiveEditor);
@@ -2790,6 +2838,19 @@ function onDocumentSelectionChange(event) {
2790
2838
  }
2791
2839
  }
2792
2840
 
2841
+ function stopLexicalPropagation(event) {
2842
+ // We attach a special property to ensure the same event doesn't re-fire
2843
+ // for parent editors.
2844
+ // @ts-ignore
2845
+ event._lexicalHandled = true;
2846
+ }
2847
+
2848
+ function hasStoppedLexicalPropagation(event) {
2849
+ // @ts-ignore
2850
+ const stopped = event._lexicalHandled === true;
2851
+ return stopped;
2852
+ }
2853
+
2793
2854
  function addRootElementEvents(rootElement, editor) {
2794
2855
  // We only want to have a single global selectionchange event handler, shared
2795
2856
  // between all editor instances.
@@ -2806,11 +2867,11 @@ function addRootElementEvents(rootElement, editor) {
2806
2867
  for (let i = 0; i < rootElementEvents.length; i++) {
2807
2868
  const [eventName, onEvent] = rootElementEvents[i];
2808
2869
  const eventHandler = typeof onEvent === 'function' ? event => {
2809
- if (!editor.isReadOnly()) {
2870
+ if (editor.isEditable()) {
2810
2871
  onEvent(event, editor);
2811
2872
  }
2812
2873
  } : event => {
2813
- if (!editor.isReadOnly()) {
2874
+ if (editor.isEditable()) {
2814
2875
  switch (eventName) {
2815
2876
  case 'cut':
2816
2877
  return dispatchCommand(editor, CUT_COMMAND, event);
@@ -4095,11 +4156,18 @@ class RangeSelection {
4095
4156
  target = target.insertAfter(node);
4096
4157
  }
4097
4158
  }
4098
- } else if (!$isElementNode(node) || $isElementNode(node) && node.isInline() || $isDecoratorNode(target) && target.isTopLevel() || $isLineBreakNode(target)) {
4159
+ } else if (!$isElementNode(node) || $isElementNode(node) && node.isInline() || $isDecoratorNode(target) && target.isTopLevel()) {
4099
4160
  lastNode = node;
4100
4161
  target = target.insertAfter(node);
4101
4162
  } else {
4102
- target = node.getParentOrThrow(); // Re-try again with the target being the parent
4163
+ const nextTarget = target.getParentOrThrow(); // if we're inserting an Element after a LineBreak, we want to move the target to the parent
4164
+ // and remove the LineBreak so we don't have empty space.
4165
+
4166
+ if ($isLineBreakNode(target)) {
4167
+ target.remove();
4168
+ }
4169
+
4170
+ target = nextTarget; // Re-try again with the target being the parent
4103
4171
 
4104
4172
  i--;
4105
4173
  continue;
@@ -4125,7 +4193,7 @@ class RangeSelection {
4125
4193
  if ($isElementNode(target)) {
4126
4194
  // If the last node to be inserted was a text node,
4127
4195
  // then we should attempt to move selection to that.
4128
- const lastChild = $isTextNode(lastNode) ? lastNode : target.getLastDescendant();
4196
+ const lastChild = $isTextNode(lastNode) ? lastNode : $isElementNode(lastNode) && lastNode.isInline() ? lastNode.getLastDescendant() : target.getLastDescendant();
4129
4197
 
4130
4198
  if (!selectStart) {
4131
4199
  // Handle moving selection to end for elements
@@ -4402,6 +4470,15 @@ class RangeSelection {
4402
4470
  const possibleNode = $getDecoratorNode(focus, isBackward);
4403
4471
 
4404
4472
  if ($isDecoratorNode(possibleNode) && !possibleNode.isIsolated()) {
4473
+ // Make it possible to move selection from range selection to
4474
+ // node selection on the node.
4475
+ if (collapse) {
4476
+ const nodeSelection = $createNodeSelection();
4477
+ nodeSelection.add(possibleNode.__key);
4478
+ $setSelection(nodeSelection);
4479
+ return;
4480
+ }
4481
+
4405
4482
  const sibling = isBackward ? possibleNode.getPreviousSibling() : possibleNode.getNextSibling();
4406
4483
 
4407
4484
  if (!$isTextNode(sibling)) {
@@ -4524,7 +4601,18 @@ class RangeSelection {
4524
4601
 
4525
4602
  deleteLine(isBackward) {
4526
4603
  if (this.isCollapsed()) {
4527
- this.modify('extend', isBackward, 'lineboundary');
4604
+ if (this.anchor.type === 'text') {
4605
+ this.modify('extend', isBackward, 'lineboundary');
4606
+ } // If selection is extended to cover text edge then extend it one character more
4607
+ // to delete its parent element. Otherwise text content will be deleted but empty
4608
+ // parent node will remain
4609
+
4610
+
4611
+ const endPoint = isBackward ? this.focus : this.anchor;
4612
+
4613
+ if (endPoint.offset === 0) {
4614
+ this.modify('extend', isBackward, 'character');
4615
+ }
4528
4616
  }
4529
4617
 
4530
4618
  this.removeText();
@@ -4879,7 +4967,11 @@ function internalCreateSelection(editor) {
4879
4967
  return internalCreateRangeSelection(lastSelection, domSelection, editor);
4880
4968
  }
4881
4969
  function internalCreateRangeSelection(lastSelection, domSelection, editor) {
4882
- // When we create a selection, we try to use the previous
4970
+ const windowObj = editor._window;
4971
+
4972
+ if (windowObj === null) {
4973
+ return null;
4974
+ } // When we create a selection, we try to use the previous
4883
4975
  // selection where possible, unless an actual user selection
4884
4976
  // change has occurred. When we do need to create a new selection
4885
4977
  // we validate we can have text nodes for both anchor and focus
@@ -4892,7 +4984,9 @@ function internalCreateRangeSelection(lastSelection, domSelection, editor) {
4892
4984
  // we generally bail out of doing an update to selection during
4893
4985
  // reconciliation unless there are dirty nodes that need
4894
4986
  // reconciling.
4895
- const windowEvent = window.event;
4987
+
4988
+
4989
+ const windowEvent = windowObj.event;
4896
4990
  const eventType = windowEvent ? windowEvent.type : undefined;
4897
4991
  const isSelectionChange = eventType === 'selectionchange';
4898
4992
  const useDOMSelection = !getIsProcesssingMutations() && (isSelectionChange || eventType === 'beforeinput' || eventType === 'compositionstart' || eventType === 'compositionend' || eventType === 'click' && windowEvent && windowEvent.detail === 3 || eventType === undefined);
@@ -5580,17 +5674,19 @@ function commitPendingUpdates(editor) {
5580
5674
  }
5581
5675
  }
5582
5676
 
5583
- pendingEditorState._readOnly = true;
5677
+ if (!pendingEditorState._readOnly) {
5678
+ pendingEditorState._readOnly = true;
5584
5679
 
5585
- {
5586
- handleDEVOnlyPendingUpdateGuarantees(pendingEditorState);
5680
+ {
5681
+ handleDEVOnlyPendingUpdateGuarantees(pendingEditorState);
5587
5682
 
5588
- if ($isRangeSelection(pendingSelection)) {
5589
- Object.freeze(pendingSelection.anchor);
5590
- Object.freeze(pendingSelection.focus);
5591
- }
5683
+ if ($isRangeSelection(pendingSelection)) {
5684
+ Object.freeze(pendingSelection.anchor);
5685
+ Object.freeze(pendingSelection.focus);
5686
+ }
5592
5687
 
5593
- Object.freeze(pendingSelection);
5688
+ Object.freeze(pendingSelection);
5689
+ }
5594
5690
  }
5595
5691
 
5596
5692
  const dirtyLeaves = editor._dirtyLeaves;
@@ -5618,7 +5714,7 @@ function commitPendingUpdates(editor) {
5618
5714
  const domSelection = headless ? null : getDOMSelection(); // Attempt to update the DOM selection, including focusing of the root element,
5619
5715
  // and scroll into view if needed.
5620
5716
 
5621
- if (!editor._readOnly && // domSelection will be null in headless
5717
+ if (editor._editable && // domSelection will be null in headless
5622
5718
  domSelection !== null && (needsUpdate || pendingSelection === null || pendingSelection.dirty)) {
5623
5719
  activeEditor = editor;
5624
5720
  activeEditorState = pendingEditorState;
@@ -5825,7 +5921,7 @@ function beginUpdate(editor, updateFn, options) {
5825
5921
  let pendingEditorState = editor._pendingEditorState;
5826
5922
  let editorStateWasCloned = false;
5827
5923
 
5828
- if (pendingEditorState === null) {
5924
+ if (pendingEditorState === null || pendingEditorState._readOnly) {
5829
5925
  pendingEditorState = editor._pendingEditorState = cloneEditorState(currentEditorState);
5830
5926
  editorStateWasCloned = true;
5831
5927
  }
@@ -6525,7 +6621,6 @@ class LexicalNode {
6525
6621
 
6526
6622
 
6527
6623
  remove(preserveEmptyParent) {
6528
- errorOnReadOnly();
6529
6624
  removeNode(this, true, preserveEmptyParent);
6530
6625
  }
6531
6626
 
@@ -6626,7 +6721,6 @@ class LexicalNode {
6626
6721
  }
6627
6722
 
6628
6723
  insertBefore(nodeToInsert) {
6629
- errorOnReadOnly();
6630
6724
  const writableSelf = this.getWritable();
6631
6725
  const writableNodeToInsert = nodeToInsert.getWritable();
6632
6726
  removeFromParent(writableNodeToInsert);
@@ -6724,6 +6818,8 @@ function errorOnTypeKlassMismatch(type, klass) {
6724
6818
  * LICENSE file in the root directory of this source tree.
6725
6819
  *
6726
6820
  */
6821
+ /** @noInheritDoc */
6822
+
6727
6823
  class DecoratorNode extends LexicalNode {
6728
6824
  constructor(key) {
6729
6825
  super(key);
@@ -6755,7 +6851,16 @@ function $isDecoratorNode(node) {
6755
6851
  * LICENSE file in the root directory of this source tree.
6756
6852
  *
6757
6853
  */
6854
+
6855
+ /** @noInheritDoc */
6758
6856
  class ElementNode extends LexicalNode {
6857
+ /** @internal */
6858
+
6859
+ /** @internal */
6860
+
6861
+ /** @internal */
6862
+
6863
+ /** @internal */
6759
6864
  constructor(key) {
6760
6865
  super(key);
6761
6866
  this.__children = [];
@@ -7030,7 +7135,6 @@ class ElementNode extends LexicalNode {
7030
7135
  }
7031
7136
 
7032
7137
  clear() {
7033
- errorOnReadOnly();
7034
7138
  const writableSelf = this.getWritable();
7035
7139
  const children = this.getChildren();
7036
7140
  children.forEach(child => child.remove());
@@ -7038,33 +7142,28 @@ class ElementNode extends LexicalNode {
7038
7142
  }
7039
7143
 
7040
7144
  append(...nodesToAppend) {
7041
- errorOnReadOnly();
7042
7145
  return this.splice(this.getChildrenSize(), 0, nodesToAppend);
7043
7146
  }
7044
7147
 
7045
7148
  setDirection(direction) {
7046
- errorOnReadOnly();
7047
7149
  const self = this.getWritable();
7048
7150
  self.__dir = direction;
7049
7151
  return self;
7050
7152
  }
7051
7153
 
7052
7154
  setFormat(type) {
7053
- errorOnReadOnly();
7054
7155
  const self = this.getWritable();
7055
7156
  self.__format = type !== '' ? ELEMENT_TYPE_TO_FORMAT[type] : 0;
7056
7157
  return this;
7057
7158
  }
7058
7159
 
7059
7160
  setIndent(indentLevel) {
7060
- errorOnReadOnly();
7061
7161
  const self = this.getWritable();
7062
7162
  self.__indent = indentLevel;
7063
7163
  return this;
7064
7164
  }
7065
7165
 
7066
7166
  splice(start, deleteCount, nodesToInsert) {
7067
- errorOnReadOnly();
7068
7167
  const writableSelf = this.getWritable();
7069
7168
  const writableSelfKey = writableSelf.__key;
7070
7169
  const writableSelfChildren = writableSelf.__children;
@@ -7254,7 +7353,10 @@ function $isElementNode(node) {
7254
7353
  * LICENSE file in the root directory of this source tree.
7255
7354
  *
7256
7355
  */
7356
+
7357
+ /** @noInheritDoc */
7257
7358
  class RootNode extends ElementNode {
7359
+ /** @internal */
7258
7360
  static getType() {
7259
7361
  return 'root';
7260
7362
  }
@@ -7457,6 +7559,8 @@ class EditorState {
7457
7559
  * LICENSE file in the root directory of this source tree.
7458
7560
  *
7459
7561
  */
7562
+
7563
+ /** @noInheritDoc */
7460
7564
  class LineBreakNode extends LexicalNode {
7461
7565
  static getType() {
7462
7566
  return 'linebreak';
@@ -7677,6 +7781,8 @@ function createTextInnerDOM(innerDOM, node, innerTag, format, text, config) {
7677
7781
  setTextThemeClassNames(innerTag, 0, format, innerDOM, textClassNames);
7678
7782
  }
7679
7783
  }
7784
+ /** @noInheritDoc */
7785
+
7680
7786
 
7681
7787
  class TextNode extends LexicalNode {
7682
7788
  static getType() {
@@ -7919,26 +8025,23 @@ class TextNode extends LexicalNode {
7919
8025
 
7920
8026
  selectionTransform(prevSelection, nextSelection) {
7921
8027
  return;
7922
- } // TODO 0.4 This should just be a `string`.
8028
+ } // TODO 0.5 This should just be a `string`.
7923
8029
 
7924
8030
 
7925
8031
  setFormat(format) {
7926
- errorOnReadOnly();
7927
8032
  const self = this.getWritable();
7928
8033
  self.__format = typeof format === 'string' ? TEXT_TYPE_TO_FORMAT[format] : format;
7929
8034
  return self;
7930
- } // TODO 0.4 This should just be a `string`.
8035
+ } // TODO 0.5 This should just be a `string`.
7931
8036
 
7932
8037
 
7933
8038
  setDetail(detail) {
7934
- errorOnReadOnly();
7935
8039
  const self = this.getWritable();
7936
8040
  self.__detail = typeof detail === 'string' ? DETAIL_TYPE_TO_DETAIL[detail] : detail;
7937
8041
  return self;
7938
8042
  }
7939
8043
 
7940
8044
  setStyle(style) {
7941
- errorOnReadOnly();
7942
8045
  const self = this.getWritable();
7943
8046
  self.__style = style;
7944
8047
  return self;
@@ -7950,21 +8053,18 @@ class TextNode extends LexicalNode {
7950
8053
  }
7951
8054
 
7952
8055
  toggleDirectionless() {
7953
- errorOnReadOnly();
7954
8056
  const self = this.getWritable();
7955
8057
  self.__detail ^= IS_DIRECTIONLESS;
7956
8058
  return self;
7957
8059
  }
7958
8060
 
7959
8061
  toggleUnmergeable() {
7960
- errorOnReadOnly();
7961
8062
  const self = this.getWritable();
7962
8063
  self.__detail ^= IS_UNMERGEABLE;
7963
8064
  return self;
7964
8065
  }
7965
8066
 
7966
8067
  setMode(type) {
7967
- errorOnReadOnly();
7968
8068
  const mode = TEXT_MODE_TO_TYPE[type];
7969
8069
  const self = this.getWritable();
7970
8070
  self.__mode = mode;
@@ -7972,7 +8072,6 @@ class TextNode extends LexicalNode {
7972
8072
  }
7973
8073
 
7974
8074
  setTextContent(text) {
7975
- errorOnReadOnly();
7976
8075
  const writableSelf = this.getWritable();
7977
8076
  writableSelf.__text = text;
7978
8077
  return writableSelf;
@@ -8017,7 +8116,6 @@ class TextNode extends LexicalNode {
8017
8116
  }
8018
8117
 
8019
8118
  spliceText(offset, delCount, newText, moveSelection) {
8020
- errorOnReadOnly();
8021
8119
  const writableSelf = this.getWritable();
8022
8120
  const text = writableSelf.__text;
8023
8121
  const handledTextLength = newText.length;
@@ -8347,6 +8445,8 @@ function $isTextNode(node) {
8347
8445
  * LICENSE file in the root directory of this source tree.
8348
8446
  *
8349
8447
  */
8448
+
8449
+ /** @noInheritDoc */
8350
8450
  class ParagraphNode extends ElementNode {
8351
8451
  static getType() {
8352
8452
  return 'paragraph';
@@ -8548,7 +8648,7 @@ function createEditor(editorConfig) {
8548
8648
  const initialEditorState = config.editorState;
8549
8649
  const nodes = [RootNode, TextNode, LineBreakNode, ParagraphNode, ...(config.nodes || [])];
8550
8650
  const onError = config.onError;
8551
- const isReadOnly = config.readOnly || false;
8651
+ const isEditable = config.editable || true;
8552
8652
  let registeredNodes;
8553
8653
 
8554
8654
  if (editorConfig === undefined && activeEditor !== null) {
@@ -8608,7 +8708,7 @@ function createEditor(editorConfig) {
8608
8708
  disableEvents,
8609
8709
  namespace,
8610
8710
  theme
8611
- }, onError ? onError : console.error, initializeConversionCache(registeredNodes), isReadOnly);
8711
+ }, onError ? onError : console.error, initializeConversionCache(registeredNodes), isEditable);
8612
8712
 
8613
8713
  if (initialEditorState !== undefined) {
8614
8714
  editor._pendingEditorState = initialEditorState;
@@ -8618,7 +8718,7 @@ function createEditor(editorConfig) {
8618
8718
  return editor;
8619
8719
  }
8620
8720
  class LexicalEditor {
8621
- constructor(editorState, parentEditor, nodes, config, onError, htmlConversions, readOnly) {
8721
+ constructor(editorState, parentEditor, nodes, config, onError, htmlConversions, editable) {
8622
8722
  this._parentEditor = parentEditor; // The root element associated with this editor
8623
8723
 
8624
8724
  this._rootElement = null; // The current editor state
@@ -8636,8 +8736,8 @@ class LexicalEditor {
8636
8736
 
8637
8737
  this._listeners = {
8638
8738
  decorator: new Set(),
8739
+ editable: new Set(),
8639
8740
  mutation: new Map(),
8640
- readonly: new Set(),
8641
8741
  root: new Set(),
8642
8742
  textcontent: new Set(),
8643
8743
  update: new Set()
@@ -8664,8 +8764,9 @@ class LexicalEditor {
8664
8764
  this._key = createUID();
8665
8765
  this._onError = onError;
8666
8766
  this._htmlConversions = htmlConversions;
8667
- this._readOnly = false;
8767
+ this._editable = true;
8668
8768
  this._headless = false;
8769
+ this._window = null;
8669
8770
  }
8670
8771
 
8671
8772
  isComposing() {
@@ -8680,8 +8781,8 @@ class LexicalEditor {
8680
8781
  };
8681
8782
  }
8682
8783
 
8683
- registerReadOnlyListener(listener) {
8684
- const listenerSetOrMap = this._listeners.readonly;
8784
+ registerEditableListener(listener) {
8785
+ const listenerSetOrMap = this._listeners.editable;
8685
8786
  listenerSetOrMap.add(listener);
8686
8787
  return () => {
8687
8788
  listenerSetOrMap.delete(listener);
@@ -8826,11 +8927,13 @@ class LexicalEditor {
8826
8927
  }
8827
8928
 
8828
8929
  if (nextRootElement !== null) {
8930
+ const windowObj = getDefaultView(nextRootElement);
8829
8931
  const style = nextRootElement.style;
8830
8932
  style.userSelect = 'text';
8831
8933
  style.whiteSpace = 'pre-wrap';
8832
8934
  style.wordBreak = 'break-word';
8833
8935
  nextRootElement.setAttribute('data-lexical-editor', 'true');
8936
+ this._window = windowObj;
8834
8937
  this._dirtyType = FULL_RECONCILE;
8835
8938
  initMutationObserver(this);
8836
8939
 
@@ -8841,6 +8944,8 @@ class LexicalEditor {
8841
8944
  if (!this._config.disableEvents) {
8842
8945
  addRootElementEvents(nextRootElement, this);
8843
8946
  }
8947
+ } else {
8948
+ this._window = null;
8844
8949
  }
8845
8950
 
8846
8951
  triggerListeners('root', this, false, nextRootElement, prevRootElement);
@@ -8926,7 +9031,12 @@ class LexicalEditor {
8926
9031
  callbackFn();
8927
9032
  }
8928
9033
  }
8929
- });
9034
+ }); // In the case where onUpdate doesn't fire (due to the focus update not
9035
+ // occuring).
9036
+
9037
+ if (this._pendingEditorState === null) {
9038
+ rootElement.removeAttribute('autocapitalize');
9039
+ }
8930
9040
  }
8931
9041
  }
8932
9042
 
@@ -8944,14 +9054,14 @@ class LexicalEditor {
8944
9054
  }
8945
9055
  }
8946
9056
 
8947
- isReadOnly() {
8948
- return this._readOnly;
9057
+ isEditable() {
9058
+ return this._editable;
8949
9059
  }
8950
9060
 
8951
- setReadOnly(readOnly) {
8952
- if (this._readOnly !== readOnly) {
8953
- this._readOnly = readOnly;
8954
- triggerListeners('readonly', this, true, readOnly);
9061
+ setEditable(editable) {
9062
+ if (this._editable !== editable) {
9063
+ this._editable = editable;
9064
+ triggerListeners('editable', this, true, editable);
8955
9065
  }
8956
9066
  }
8957
9067
 
@@ -8970,7 +9080,7 @@ class LexicalEditor {
8970
9080
  * LICENSE file in the root directory of this source tree.
8971
9081
  *
8972
9082
  */
8973
- const VERSION = '0.3.9';
9083
+ const VERSION = '0.4.0';
8974
9084
 
8975
9085
  /**
8976
9086
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -8979,7 +9089,10 @@ const VERSION = '0.3.9';
8979
9089
  * LICENSE file in the root directory of this source tree.
8980
9090
  *
8981
9091
  */
9092
+
9093
+ /** @noInheritDoc */
8982
9094
  class GridCellNode extends ElementNode {
9095
+ /** @internal */
8983
9096
  constructor(colSpan, key) {
8984
9097
  super(key);
8985
9098
  this.__colSpan = colSpan;