lexical 0.3.11 → 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)) {
@@ -4890,7 +4967,11 @@ function internalCreateSelection(editor) {
4890
4967
  return internalCreateRangeSelection(lastSelection, domSelection, editor);
4891
4968
  }
4892
4969
  function internalCreateRangeSelection(lastSelection, domSelection, editor) {
4893
- // 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
4894
4975
  // selection where possible, unless an actual user selection
4895
4976
  // change has occurred. When we do need to create a new selection
4896
4977
  // we validate we can have text nodes for both anchor and focus
@@ -4903,7 +4984,9 @@ function internalCreateRangeSelection(lastSelection, domSelection, editor) {
4903
4984
  // we generally bail out of doing an update to selection during
4904
4985
  // reconciliation unless there are dirty nodes that need
4905
4986
  // reconciling.
4906
- const windowEvent = window.event;
4987
+
4988
+
4989
+ const windowEvent = windowObj.event;
4907
4990
  const eventType = windowEvent ? windowEvent.type : undefined;
4908
4991
  const isSelectionChange = eventType === 'selectionchange';
4909
4992
  const useDOMSelection = !getIsProcesssingMutations() && (isSelectionChange || eventType === 'beforeinput' || eventType === 'compositionstart' || eventType === 'compositionend' || eventType === 'click' && windowEvent && windowEvent.detail === 3 || eventType === undefined);
@@ -5591,17 +5674,19 @@ function commitPendingUpdates(editor) {
5591
5674
  }
5592
5675
  }
5593
5676
 
5594
- pendingEditorState._readOnly = true;
5677
+ if (!pendingEditorState._readOnly) {
5678
+ pendingEditorState._readOnly = true;
5595
5679
 
5596
- {
5597
- handleDEVOnlyPendingUpdateGuarantees(pendingEditorState);
5680
+ {
5681
+ handleDEVOnlyPendingUpdateGuarantees(pendingEditorState);
5598
5682
 
5599
- if ($isRangeSelection(pendingSelection)) {
5600
- Object.freeze(pendingSelection.anchor);
5601
- Object.freeze(pendingSelection.focus);
5602
- }
5683
+ if ($isRangeSelection(pendingSelection)) {
5684
+ Object.freeze(pendingSelection.anchor);
5685
+ Object.freeze(pendingSelection.focus);
5686
+ }
5603
5687
 
5604
- Object.freeze(pendingSelection);
5688
+ Object.freeze(pendingSelection);
5689
+ }
5605
5690
  }
5606
5691
 
5607
5692
  const dirtyLeaves = editor._dirtyLeaves;
@@ -5629,7 +5714,7 @@ function commitPendingUpdates(editor) {
5629
5714
  const domSelection = headless ? null : getDOMSelection(); // Attempt to update the DOM selection, including focusing of the root element,
5630
5715
  // and scroll into view if needed.
5631
5716
 
5632
- if (!editor._readOnly && // domSelection will be null in headless
5717
+ if (editor._editable && // domSelection will be null in headless
5633
5718
  domSelection !== null && (needsUpdate || pendingSelection === null || pendingSelection.dirty)) {
5634
5719
  activeEditor = editor;
5635
5720
  activeEditorState = pendingEditorState;
@@ -5836,7 +5921,7 @@ function beginUpdate(editor, updateFn, options) {
5836
5921
  let pendingEditorState = editor._pendingEditorState;
5837
5922
  let editorStateWasCloned = false;
5838
5923
 
5839
- if (pendingEditorState === null) {
5924
+ if (pendingEditorState === null || pendingEditorState._readOnly) {
5840
5925
  pendingEditorState = editor._pendingEditorState = cloneEditorState(currentEditorState);
5841
5926
  editorStateWasCloned = true;
5842
5927
  }
@@ -6536,7 +6621,6 @@ class LexicalNode {
6536
6621
 
6537
6622
 
6538
6623
  remove(preserveEmptyParent) {
6539
- errorOnReadOnly();
6540
6624
  removeNode(this, true, preserveEmptyParent);
6541
6625
  }
6542
6626
 
@@ -6637,7 +6721,6 @@ class LexicalNode {
6637
6721
  }
6638
6722
 
6639
6723
  insertBefore(nodeToInsert) {
6640
- errorOnReadOnly();
6641
6724
  const writableSelf = this.getWritable();
6642
6725
  const writableNodeToInsert = nodeToInsert.getWritable();
6643
6726
  removeFromParent(writableNodeToInsert);
@@ -6735,6 +6818,8 @@ function errorOnTypeKlassMismatch(type, klass) {
6735
6818
  * LICENSE file in the root directory of this source tree.
6736
6819
  *
6737
6820
  */
6821
+ /** @noInheritDoc */
6822
+
6738
6823
  class DecoratorNode extends LexicalNode {
6739
6824
  constructor(key) {
6740
6825
  super(key);
@@ -6766,7 +6851,16 @@ function $isDecoratorNode(node) {
6766
6851
  * LICENSE file in the root directory of this source tree.
6767
6852
  *
6768
6853
  */
6854
+
6855
+ /** @noInheritDoc */
6769
6856
  class ElementNode extends LexicalNode {
6857
+ /** @internal */
6858
+
6859
+ /** @internal */
6860
+
6861
+ /** @internal */
6862
+
6863
+ /** @internal */
6770
6864
  constructor(key) {
6771
6865
  super(key);
6772
6866
  this.__children = [];
@@ -7041,7 +7135,6 @@ class ElementNode extends LexicalNode {
7041
7135
  }
7042
7136
 
7043
7137
  clear() {
7044
- errorOnReadOnly();
7045
7138
  const writableSelf = this.getWritable();
7046
7139
  const children = this.getChildren();
7047
7140
  children.forEach(child => child.remove());
@@ -7049,33 +7142,28 @@ class ElementNode extends LexicalNode {
7049
7142
  }
7050
7143
 
7051
7144
  append(...nodesToAppend) {
7052
- errorOnReadOnly();
7053
7145
  return this.splice(this.getChildrenSize(), 0, nodesToAppend);
7054
7146
  }
7055
7147
 
7056
7148
  setDirection(direction) {
7057
- errorOnReadOnly();
7058
7149
  const self = this.getWritable();
7059
7150
  self.__dir = direction;
7060
7151
  return self;
7061
7152
  }
7062
7153
 
7063
7154
  setFormat(type) {
7064
- errorOnReadOnly();
7065
7155
  const self = this.getWritable();
7066
7156
  self.__format = type !== '' ? ELEMENT_TYPE_TO_FORMAT[type] : 0;
7067
7157
  return this;
7068
7158
  }
7069
7159
 
7070
7160
  setIndent(indentLevel) {
7071
- errorOnReadOnly();
7072
7161
  const self = this.getWritable();
7073
7162
  self.__indent = indentLevel;
7074
7163
  return this;
7075
7164
  }
7076
7165
 
7077
7166
  splice(start, deleteCount, nodesToInsert) {
7078
- errorOnReadOnly();
7079
7167
  const writableSelf = this.getWritable();
7080
7168
  const writableSelfKey = writableSelf.__key;
7081
7169
  const writableSelfChildren = writableSelf.__children;
@@ -7265,7 +7353,10 @@ function $isElementNode(node) {
7265
7353
  * LICENSE file in the root directory of this source tree.
7266
7354
  *
7267
7355
  */
7356
+
7357
+ /** @noInheritDoc */
7268
7358
  class RootNode extends ElementNode {
7359
+ /** @internal */
7269
7360
  static getType() {
7270
7361
  return 'root';
7271
7362
  }
@@ -7468,6 +7559,8 @@ class EditorState {
7468
7559
  * LICENSE file in the root directory of this source tree.
7469
7560
  *
7470
7561
  */
7562
+
7563
+ /** @noInheritDoc */
7471
7564
  class LineBreakNode extends LexicalNode {
7472
7565
  static getType() {
7473
7566
  return 'linebreak';
@@ -7688,6 +7781,8 @@ function createTextInnerDOM(innerDOM, node, innerTag, format, text, config) {
7688
7781
  setTextThemeClassNames(innerTag, 0, format, innerDOM, textClassNames);
7689
7782
  }
7690
7783
  }
7784
+ /** @noInheritDoc */
7785
+
7691
7786
 
7692
7787
  class TextNode extends LexicalNode {
7693
7788
  static getType() {
@@ -7930,26 +8025,23 @@ class TextNode extends LexicalNode {
7930
8025
 
7931
8026
  selectionTransform(prevSelection, nextSelection) {
7932
8027
  return;
7933
- } // TODO 0.4 This should just be a `string`.
8028
+ } // TODO 0.5 This should just be a `string`.
7934
8029
 
7935
8030
 
7936
8031
  setFormat(format) {
7937
- errorOnReadOnly();
7938
8032
  const self = this.getWritable();
7939
8033
  self.__format = typeof format === 'string' ? TEXT_TYPE_TO_FORMAT[format] : format;
7940
8034
  return self;
7941
- } // TODO 0.4 This should just be a `string`.
8035
+ } // TODO 0.5 This should just be a `string`.
7942
8036
 
7943
8037
 
7944
8038
  setDetail(detail) {
7945
- errorOnReadOnly();
7946
8039
  const self = this.getWritable();
7947
8040
  self.__detail = typeof detail === 'string' ? DETAIL_TYPE_TO_DETAIL[detail] : detail;
7948
8041
  return self;
7949
8042
  }
7950
8043
 
7951
8044
  setStyle(style) {
7952
- errorOnReadOnly();
7953
8045
  const self = this.getWritable();
7954
8046
  self.__style = style;
7955
8047
  return self;
@@ -7961,21 +8053,18 @@ class TextNode extends LexicalNode {
7961
8053
  }
7962
8054
 
7963
8055
  toggleDirectionless() {
7964
- errorOnReadOnly();
7965
8056
  const self = this.getWritable();
7966
8057
  self.__detail ^= IS_DIRECTIONLESS;
7967
8058
  return self;
7968
8059
  }
7969
8060
 
7970
8061
  toggleUnmergeable() {
7971
- errorOnReadOnly();
7972
8062
  const self = this.getWritable();
7973
8063
  self.__detail ^= IS_UNMERGEABLE;
7974
8064
  return self;
7975
8065
  }
7976
8066
 
7977
8067
  setMode(type) {
7978
- errorOnReadOnly();
7979
8068
  const mode = TEXT_MODE_TO_TYPE[type];
7980
8069
  const self = this.getWritable();
7981
8070
  self.__mode = mode;
@@ -7983,7 +8072,6 @@ class TextNode extends LexicalNode {
7983
8072
  }
7984
8073
 
7985
8074
  setTextContent(text) {
7986
- errorOnReadOnly();
7987
8075
  const writableSelf = this.getWritable();
7988
8076
  writableSelf.__text = text;
7989
8077
  return writableSelf;
@@ -8028,7 +8116,6 @@ class TextNode extends LexicalNode {
8028
8116
  }
8029
8117
 
8030
8118
  spliceText(offset, delCount, newText, moveSelection) {
8031
- errorOnReadOnly();
8032
8119
  const writableSelf = this.getWritable();
8033
8120
  const text = writableSelf.__text;
8034
8121
  const handledTextLength = newText.length;
@@ -8358,6 +8445,8 @@ function $isTextNode(node) {
8358
8445
  * LICENSE file in the root directory of this source tree.
8359
8446
  *
8360
8447
  */
8448
+
8449
+ /** @noInheritDoc */
8361
8450
  class ParagraphNode extends ElementNode {
8362
8451
  static getType() {
8363
8452
  return 'paragraph';
@@ -8559,7 +8648,7 @@ function createEditor(editorConfig) {
8559
8648
  const initialEditorState = config.editorState;
8560
8649
  const nodes = [RootNode, TextNode, LineBreakNode, ParagraphNode, ...(config.nodes || [])];
8561
8650
  const onError = config.onError;
8562
- const isReadOnly = config.readOnly || false;
8651
+ const isEditable = config.editable || true;
8563
8652
  let registeredNodes;
8564
8653
 
8565
8654
  if (editorConfig === undefined && activeEditor !== null) {
@@ -8619,7 +8708,7 @@ function createEditor(editorConfig) {
8619
8708
  disableEvents,
8620
8709
  namespace,
8621
8710
  theme
8622
- }, onError ? onError : console.error, initializeConversionCache(registeredNodes), isReadOnly);
8711
+ }, onError ? onError : console.error, initializeConversionCache(registeredNodes), isEditable);
8623
8712
 
8624
8713
  if (initialEditorState !== undefined) {
8625
8714
  editor._pendingEditorState = initialEditorState;
@@ -8629,7 +8718,7 @@ function createEditor(editorConfig) {
8629
8718
  return editor;
8630
8719
  }
8631
8720
  class LexicalEditor {
8632
- constructor(editorState, parentEditor, nodes, config, onError, htmlConversions, readOnly) {
8721
+ constructor(editorState, parentEditor, nodes, config, onError, htmlConversions, editable) {
8633
8722
  this._parentEditor = parentEditor; // The root element associated with this editor
8634
8723
 
8635
8724
  this._rootElement = null; // The current editor state
@@ -8647,8 +8736,8 @@ class LexicalEditor {
8647
8736
 
8648
8737
  this._listeners = {
8649
8738
  decorator: new Set(),
8739
+ editable: new Set(),
8650
8740
  mutation: new Map(),
8651
- readonly: new Set(),
8652
8741
  root: new Set(),
8653
8742
  textcontent: new Set(),
8654
8743
  update: new Set()
@@ -8675,8 +8764,9 @@ class LexicalEditor {
8675
8764
  this._key = createUID();
8676
8765
  this._onError = onError;
8677
8766
  this._htmlConversions = htmlConversions;
8678
- this._readOnly = false;
8767
+ this._editable = true;
8679
8768
  this._headless = false;
8769
+ this._window = null;
8680
8770
  }
8681
8771
 
8682
8772
  isComposing() {
@@ -8691,8 +8781,8 @@ class LexicalEditor {
8691
8781
  };
8692
8782
  }
8693
8783
 
8694
- registerReadOnlyListener(listener) {
8695
- const listenerSetOrMap = this._listeners.readonly;
8784
+ registerEditableListener(listener) {
8785
+ const listenerSetOrMap = this._listeners.editable;
8696
8786
  listenerSetOrMap.add(listener);
8697
8787
  return () => {
8698
8788
  listenerSetOrMap.delete(listener);
@@ -8837,11 +8927,13 @@ class LexicalEditor {
8837
8927
  }
8838
8928
 
8839
8929
  if (nextRootElement !== null) {
8930
+ const windowObj = getDefaultView(nextRootElement);
8840
8931
  const style = nextRootElement.style;
8841
8932
  style.userSelect = 'text';
8842
8933
  style.whiteSpace = 'pre-wrap';
8843
8934
  style.wordBreak = 'break-word';
8844
8935
  nextRootElement.setAttribute('data-lexical-editor', 'true');
8936
+ this._window = windowObj;
8845
8937
  this._dirtyType = FULL_RECONCILE;
8846
8938
  initMutationObserver(this);
8847
8939
 
@@ -8852,6 +8944,8 @@ class LexicalEditor {
8852
8944
  if (!this._config.disableEvents) {
8853
8945
  addRootElementEvents(nextRootElement, this);
8854
8946
  }
8947
+ } else {
8948
+ this._window = null;
8855
8949
  }
8856
8950
 
8857
8951
  triggerListeners('root', this, false, nextRootElement, prevRootElement);
@@ -8937,7 +9031,12 @@ class LexicalEditor {
8937
9031
  callbackFn();
8938
9032
  }
8939
9033
  }
8940
- });
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
+ }
8941
9040
  }
8942
9041
  }
8943
9042
 
@@ -8955,14 +9054,14 @@ class LexicalEditor {
8955
9054
  }
8956
9055
  }
8957
9056
 
8958
- isReadOnly() {
8959
- return this._readOnly;
9057
+ isEditable() {
9058
+ return this._editable;
8960
9059
  }
8961
9060
 
8962
- setReadOnly(readOnly) {
8963
- if (this._readOnly !== readOnly) {
8964
- this._readOnly = readOnly;
8965
- triggerListeners('readonly', this, true, readOnly);
9061
+ setEditable(editable) {
9062
+ if (this._editable !== editable) {
9063
+ this._editable = editable;
9064
+ triggerListeners('editable', this, true, editable);
8966
9065
  }
8967
9066
  }
8968
9067
 
@@ -8981,7 +9080,7 @@ class LexicalEditor {
8981
9080
  * LICENSE file in the root directory of this source tree.
8982
9081
  *
8983
9082
  */
8984
- const VERSION = '0.3.11';
9083
+ const VERSION = '0.4.0';
8985
9084
 
8986
9085
  /**
8987
9086
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -8990,7 +9089,10 @@ const VERSION = '0.3.11';
8990
9089
  * LICENSE file in the root directory of this source tree.
8991
9090
  *
8992
9091
  */
9092
+
9093
+ /** @noInheritDoc */
8993
9094
  class GridCellNode extends ElementNode {
9095
+ /** @internal */
8994
9096
  constructor(colSpan, key) {
8995
9097
  super(key);
8996
9098
  this.__colSpan = colSpan;