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 +199 -86
- 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)) {
|
|
@@ -4524,7 +4601,18 @@ class RangeSelection {
|
|
|
4524
4601
|
|
|
4525
4602
|
deleteLine(isBackward) {
|
|
4526
4603
|
if (this.isCollapsed()) {
|
|
4527
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
5677
|
+
if (!pendingEditorState._readOnly) {
|
|
5678
|
+
pendingEditorState._readOnly = true;
|
|
5584
5679
|
|
|
5585
|
-
|
|
5586
|
-
|
|
5680
|
+
{
|
|
5681
|
+
handleDEVOnlyPendingUpdateGuarantees(pendingEditorState);
|
|
5587
5682
|
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5683
|
+
if ($isRangeSelection(pendingSelection)) {
|
|
5684
|
+
Object.freeze(pendingSelection.anchor);
|
|
5685
|
+
Object.freeze(pendingSelection.focus);
|
|
5686
|
+
}
|
|
5592
5687
|
|
|
5593
|
-
|
|
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 (
|
|
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.
|
|
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.
|
|
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
|
|
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),
|
|
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,
|
|
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.
|
|
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
|
-
|
|
8684
|
-
const listenerSetOrMap = this._listeners.
|
|
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
|
-
|
|
8948
|
-
return this.
|
|
9057
|
+
isEditable() {
|
|
9058
|
+
return this._editable;
|
|
8949
9059
|
}
|
|
8950
9060
|
|
|
8951
|
-
|
|
8952
|
-
if (this.
|
|
8953
|
-
this.
|
|
8954
|
-
triggerListeners('
|
|
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.
|
|
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;
|