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 +187 -85
- package/Lexical.js.flow +6 -6
- package/Lexical.prod.js +169 -169
- package/LexicalEditor.d.ts +11 -10
- package/LexicalNode.d.ts +3 -0
- package/LexicalUpdates.d.ts +1 -1
- package/LexicalUtils.d.ts +5 -1
- package/LexicalVersion.d.ts +1 -1
- package/index.d.ts +4 -1
- package/nodes/LexicalDecoratorNode.d.ts +1 -0
- package/nodes/LexicalElementNode.d.ts +5 -0
- package/nodes/LexicalGridCellNode.d.ts +2 -0
- package/nodes/LexicalLineBreakNode.d.ts +1 -0
- package/nodes/LexicalParagraphNode.d.ts +1 -0
- package/nodes/LexicalRootNode.d.ts +2 -0
- package/nodes/LexicalTextNode.d.ts +1 -0
- package/package.json +1 -1
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
|
-
|
|
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
|
|
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 =
|
|
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 >
|
|
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 =
|
|
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,
|
|
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
|
|
2783
|
+
const domSelection = getDOMSelection();
|
|
2760
2784
|
|
|
2761
|
-
if (
|
|
2785
|
+
if (domSelection === null) {
|
|
2762
2786
|
return;
|
|
2763
2787
|
}
|
|
2764
2788
|
|
|
2765
|
-
const nextActiveEditor = getNearestEditorFromDOMNode(
|
|
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(
|
|
2829
|
+
onSelectionChange(domSelection, prevActiveEditor, false);
|
|
2782
2830
|
}
|
|
2783
2831
|
|
|
2784
|
-
onSelectionChange(
|
|
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 (
|
|
2870
|
+
if (editor.isEditable()) {
|
|
2810
2871
|
onEvent(event, editor);
|
|
2811
2872
|
}
|
|
2812
2873
|
} : event => {
|
|
2813
|
-
if (
|
|
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()
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
5677
|
+
if (!pendingEditorState._readOnly) {
|
|
5678
|
+
pendingEditorState._readOnly = true;
|
|
5595
5679
|
|
|
5596
|
-
|
|
5597
|
-
|
|
5680
|
+
{
|
|
5681
|
+
handleDEVOnlyPendingUpdateGuarantees(pendingEditorState);
|
|
5598
5682
|
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5683
|
+
if ($isRangeSelection(pendingSelection)) {
|
|
5684
|
+
Object.freeze(pendingSelection.anchor);
|
|
5685
|
+
Object.freeze(pendingSelection.focus);
|
|
5686
|
+
}
|
|
5603
5687
|
|
|
5604
|
-
|
|
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 (
|
|
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.
|
|
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.
|
|
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
|
|
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),
|
|
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,
|
|
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.
|
|
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
|
-
|
|
8695
|
-
const listenerSetOrMap = this._listeners.
|
|
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
|
-
|
|
8959
|
-
return this.
|
|
9057
|
+
isEditable() {
|
|
9058
|
+
return this._editable;
|
|
8960
9059
|
}
|
|
8961
9060
|
|
|
8962
|
-
|
|
8963
|
-
if (this.
|
|
8964
|
-
this.
|
|
8965
|
-
triggerListeners('
|
|
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.
|
|
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;
|