lexical 0.3.4 → 0.3.7
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 +291 -176
- package/Lexical.js.flow +6 -2
- package/Lexical.prod.js +166 -162
- package/LexicalConstants.d.ts +4 -3
- package/LexicalEditor.d.ts +13 -11
- package/LexicalNode.d.ts +7 -7
- package/LexicalSelection.d.ts +6 -6
- package/LexicalUtils.d.ts +6 -7
- package/LexicalVersion.d.ts +1 -1
- package/index.d.ts +1 -1
- package/nodes/LexicalElementNode.d.ts +1 -1
- package/nodes/LexicalTextNode.d.ts +3 -2
- package/package.json +1 -4
package/Lexical.dev.js
CHANGED
|
@@ -148,6 +148,10 @@ const TEXT_TYPE_TO_FORMAT = {
|
|
|
148
148
|
superscript: IS_SUPERSCRIPT,
|
|
149
149
|
underline: IS_UNDERLINE
|
|
150
150
|
};
|
|
151
|
+
const DETAIL_TYPE_TO_DETAIL = {
|
|
152
|
+
directionless: IS_DIRECTIONLESS,
|
|
153
|
+
unmergeable: IS_UNMERGEABLE
|
|
154
|
+
};
|
|
151
155
|
const ELEMENT_TYPE_TO_FORMAT = {
|
|
152
156
|
center: IS_ALIGN_CENTER,
|
|
153
157
|
justify: IS_ALIGN_JUSTIFY,
|
|
@@ -200,7 +204,8 @@ function initTextEntryListener() {
|
|
|
200
204
|
|
|
201
205
|
function isManagedLineBreak(dom, target, editor) {
|
|
202
206
|
return (// @ts-expect-error: internal field
|
|
203
|
-
target.__lexicalLineBreak === dom ||
|
|
207
|
+
target.__lexicalLineBreak === dom || // @ts-ignore We intentionally add this to the Node.
|
|
208
|
+
dom[`__lexicalKey_${editor._key}`] !== undefined
|
|
204
209
|
);
|
|
205
210
|
}
|
|
206
211
|
|
|
@@ -222,7 +227,10 @@ function handleTextMutation(target, node, editor) {
|
|
|
222
227
|
}
|
|
223
228
|
|
|
224
229
|
const text = target.nodeValue;
|
|
225
|
-
|
|
230
|
+
|
|
231
|
+
if (text !== null) {
|
|
232
|
+
$updateTextNodeFromDOMContent(node, text, anchorOffset, focusOffset, false);
|
|
233
|
+
}
|
|
226
234
|
}
|
|
227
235
|
|
|
228
236
|
function shouldUpdateTextNodeFromMutation(selection, targetDOM, targetNode) {
|
|
@@ -245,7 +253,7 @@ function $flushMutations$1(editor, mutations, observer) {
|
|
|
245
253
|
updateEditor(editor, () => {
|
|
246
254
|
const selection = $getSelection() || getLastSelection(editor);
|
|
247
255
|
const badDOMTargets = new Map();
|
|
248
|
-
const rootElement = editor.getRootElement(); // We use the current
|
|
256
|
+
const rootElement = editor.getRootElement(); // We use the current editor state, as that reflects what is
|
|
249
257
|
// actually "on screen".
|
|
250
258
|
|
|
251
259
|
const currentEditorState = editor._editorState;
|
|
@@ -661,8 +669,9 @@ function $getNodeByKey(key, _editorState) {
|
|
|
661
669
|
return node;
|
|
662
670
|
}
|
|
663
671
|
function getNodeFromDOMNode(dom, editorState) {
|
|
664
|
-
const editor = getActiveEditor();
|
|
665
|
-
|
|
672
|
+
const editor = getActiveEditor(); // @ts-ignore We intentionally add this to the Node.
|
|
673
|
+
|
|
674
|
+
const key = dom[`__lexicalKey_${editor._key}`];
|
|
666
675
|
|
|
667
676
|
if (key !== undefined) {
|
|
668
677
|
return $getNodeByKey(key, editorState);
|
|
@@ -771,7 +780,8 @@ dom, editor) {
|
|
|
771
780
|
let node = dom;
|
|
772
781
|
|
|
773
782
|
while (node != null) {
|
|
774
|
-
|
|
783
|
+
// @ts-ignore We intentionally add this to the Node.
|
|
784
|
+
const key = node[`__lexicalKey_${editor._key}`];
|
|
775
785
|
|
|
776
786
|
if (key !== undefined) {
|
|
777
787
|
return key;
|
|
@@ -827,7 +837,9 @@ function $updateSelectedTextFromDOM(editor, isCompositionEnd, data) {
|
|
|
827
837
|
focusOffset = offset;
|
|
828
838
|
}
|
|
829
839
|
|
|
830
|
-
|
|
840
|
+
if (textContent !== null) {
|
|
841
|
+
$updateTextNodeFromDOMContent(node, textContent, anchorOffset, focusOffset, isCompositionEnd);
|
|
842
|
+
}
|
|
831
843
|
}
|
|
832
844
|
}
|
|
833
845
|
}
|
|
@@ -897,6 +909,11 @@ function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focu
|
|
|
897
909
|
}
|
|
898
910
|
}
|
|
899
911
|
|
|
912
|
+
function $previousSiblingDoesNotAcceptText(node) {
|
|
913
|
+
const previousSibling = node.getPreviousSibling();
|
|
914
|
+
return ($isTextNode(previousSibling) || $isElementNode(previousSibling) && previousSibling.isInline()) && !previousSibling.canInsertTextAfter();
|
|
915
|
+
}
|
|
916
|
+
|
|
900
917
|
function $shouldInsertTextAfterOrBeforeTextNode(selection, node) {
|
|
901
918
|
if (node.isSegmented()) {
|
|
902
919
|
return true;
|
|
@@ -909,7 +926,7 @@ function $shouldInsertTextAfterOrBeforeTextNode(selection, node) {
|
|
|
909
926
|
const offset = selection.anchor.offset;
|
|
910
927
|
const parent = node.getParentOrThrow();
|
|
911
928
|
const isToken = node.isToken();
|
|
912
|
-
const shouldInsertTextBefore = offset === 0 && (!node.canInsertTextBefore() || !parent.canInsertTextBefore() || isToken);
|
|
929
|
+
const shouldInsertTextBefore = offset === 0 && (!node.canInsertTextBefore() || !parent.canInsertTextBefore() || isToken || $previousSiblingDoesNotAcceptText(node));
|
|
913
930
|
const shouldInsertTextAfter = node.getTextContentSize() === offset && (!node.canInsertTextBefore() || !parent.canInsertTextBefore() || isToken);
|
|
914
931
|
return shouldInsertTextBefore || shouldInsertTextAfter;
|
|
915
932
|
} // This function is used to determine if Lexical should attempt to override
|
|
@@ -1023,6 +1040,8 @@ function isCopy(keyCode, shiftKey, metaKey, ctrlKey) {
|
|
|
1023
1040
|
if (keyCode === 67) {
|
|
1024
1041
|
return IS_APPLE ? metaKey : ctrlKey;
|
|
1025
1042
|
}
|
|
1043
|
+
|
|
1044
|
+
return false;
|
|
1026
1045
|
}
|
|
1027
1046
|
function isCut(keyCode, shiftKey, metaKey, ctrlKey) {
|
|
1028
1047
|
if (shiftKey) {
|
|
@@ -1032,6 +1051,8 @@ function isCut(keyCode, shiftKey, metaKey, ctrlKey) {
|
|
|
1032
1051
|
if (keyCode === 88) {
|
|
1033
1052
|
return IS_APPLE ? metaKey : ctrlKey;
|
|
1034
1053
|
}
|
|
1054
|
+
|
|
1055
|
+
return false;
|
|
1035
1056
|
}
|
|
1036
1057
|
|
|
1037
1058
|
function isArrowLeft(keyCode) {
|
|
@@ -1137,8 +1158,7 @@ function setMutatedNode(mutatedNodes, registeredNodes, mutationListeners, node,
|
|
|
1137
1158
|
}
|
|
1138
1159
|
function $nodesOfType(klass) {
|
|
1139
1160
|
const editorState = getActiveEditorState();
|
|
1140
|
-
const readOnly = editorState._readOnly;
|
|
1141
|
-
|
|
1161
|
+
const readOnly = editorState._readOnly;
|
|
1142
1162
|
const klassType = klass.getType();
|
|
1143
1163
|
const nodes = editorState._nodeMap;
|
|
1144
1164
|
const nodesOfType = [];
|
|
@@ -1658,10 +1678,9 @@ function reconcileBlockDirection(element, dom) {
|
|
|
1658
1678
|
|
|
1659
1679
|
if (previousDirectionTheme !== undefined) {
|
|
1660
1680
|
if (typeof previousDirectionTheme === 'string') {
|
|
1661
|
-
const classNamesArr = previousDirectionTheme.split(' ');
|
|
1662
|
-
|
|
1681
|
+
const classNamesArr = previousDirectionTheme.split(' ');
|
|
1663
1682
|
previousDirectionTheme = theme[previousDirection] = classNamesArr;
|
|
1664
|
-
} // @ts-
|
|
1683
|
+
} // @ts-ignore: intentional
|
|
1665
1684
|
|
|
1666
1685
|
|
|
1667
1686
|
classList.remove(...previousDirectionTheme);
|
|
@@ -1679,7 +1698,9 @@ function reconcileBlockDirection(element, dom) {
|
|
|
1679
1698
|
nextDirectionTheme = theme[direction] = classNamesArr;
|
|
1680
1699
|
}
|
|
1681
1700
|
|
|
1682
|
-
|
|
1701
|
+
if (nextDirectionTheme !== undefined) {
|
|
1702
|
+
classList.add(...nextDirectionTheme);
|
|
1703
|
+
}
|
|
1683
1704
|
} // Update direction
|
|
1684
1705
|
|
|
1685
1706
|
|
|
@@ -2001,20 +2022,30 @@ function reconcileRoot(prevEditorState, nextEditorState, editor, dirtyType, dirt
|
|
|
2001
2022
|
// so instead we make it seem that these values are always set.
|
|
2002
2023
|
// We also want to make sure we clear them down, otherwise we
|
|
2003
2024
|
// can leak memory.
|
|
2025
|
+
// @ts-ignore
|
|
2026
|
+
|
|
2027
|
+
activeEditor$1 = undefined; // @ts-ignore
|
|
2028
|
+
|
|
2029
|
+
activeEditorNodes = undefined; // @ts-ignore
|
|
2030
|
+
|
|
2031
|
+
activeDirtyElements = undefined; // @ts-ignore
|
|
2032
|
+
|
|
2033
|
+
activeDirtyLeaves = undefined; // @ts-ignore
|
|
2034
|
+
|
|
2035
|
+
activePrevNodeMap = undefined; // @ts-ignore
|
|
2036
|
+
|
|
2037
|
+
activeNextNodeMap = undefined; // @ts-ignore
|
|
2038
|
+
|
|
2039
|
+
activeEditorConfig = undefined; // @ts-ignore
|
|
2040
|
+
|
|
2041
|
+
activePrevKeyToDOMMap = undefined; // @ts-ignore
|
|
2004
2042
|
|
|
2005
|
-
activeEditor$1 = undefined;
|
|
2006
|
-
activeEditorNodes = undefined;
|
|
2007
|
-
activeDirtyElements = undefined;
|
|
2008
|
-
activeDirtyLeaves = undefined;
|
|
2009
|
-
activePrevNodeMap = undefined;
|
|
2010
|
-
activeNextNodeMap = undefined;
|
|
2011
|
-
activeEditorConfig = undefined;
|
|
2012
|
-
activePrevKeyToDOMMap = undefined;
|
|
2013
2043
|
mutatedNodes = undefined;
|
|
2014
2044
|
return currentMutatedNodes;
|
|
2015
2045
|
}
|
|
2016
2046
|
function storeDOMWithKey(key, dom, editor) {
|
|
2017
|
-
const keyToDOMMap = editor._keyToDOMMap;
|
|
2047
|
+
const keyToDOMMap = editor._keyToDOMMap; // @ts-ignore We intentionally add this to the Node.
|
|
2048
|
+
|
|
2018
2049
|
dom['__lexicalKey_' + editor._key] = key;
|
|
2019
2050
|
keyToDOMMap.set(key, dom);
|
|
2020
2051
|
}
|
|
@@ -2040,10 +2071,10 @@ function getPrevElementByKeyOrThrow(key) {
|
|
|
2040
2071
|
*/
|
|
2041
2072
|
const PASS_THROUGH_COMMAND = Object.freeze({});
|
|
2042
2073
|
const ANDROID_COMPOSITION_LATENCY = 30;
|
|
2043
|
-
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], ['paste', PASS_THROUGH_COMMAND], ['focus', PASS_THROUGH_COMMAND], ['blur', PASS_THROUGH_COMMAND], ['drop', PASS_THROUGH_COMMAND]];
|
|
2074
|
+
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]];
|
|
2044
2075
|
|
|
2045
2076
|
if (CAN_USE_BEFORE_INPUT) {
|
|
2046
|
-
rootElementEvents.push(['beforeinput', onBeforeInput]);
|
|
2077
|
+
rootElementEvents.push(['beforeinput', (event, editor) => onBeforeInput(event, editor)]);
|
|
2047
2078
|
}
|
|
2048
2079
|
|
|
2049
2080
|
let lastKeyDownTimeStamp = 0;
|
|
@@ -2055,7 +2086,7 @@ let isFirefoxEndingComposition = false;
|
|
|
2055
2086
|
let collapsedSelectionFormat = [0, 0, 'root', 0];
|
|
2056
2087
|
|
|
2057
2088
|
function shouldSkipSelectionChange(domNode, offset) {
|
|
2058
|
-
return domNode !== null && domNode.nodeType === DOM_TEXT_TYPE && offset !== 0 && offset !== domNode.nodeValue.length;
|
|
2089
|
+
return domNode !== null && domNode.nodeValue !== null && domNode.nodeType === DOM_TEXT_TYPE && offset !== 0 && offset !== domNode.nodeValue.length;
|
|
2059
2090
|
}
|
|
2060
2091
|
|
|
2061
2092
|
function onSelectionChange(domSelection, editor, isActive) {
|
|
@@ -2157,15 +2188,19 @@ function onClick(event, editor) {
|
|
|
2157
2188
|
const anchor = selection.anchor;
|
|
2158
2189
|
const anchorNode = anchor.getNode();
|
|
2159
2190
|
|
|
2160
|
-
if (anchor.type === 'element' && anchor.offset === 0 && selection.isCollapsed() && !$isRootNode(anchorNode) && $getRoot().getChildrenSize() === 1 && anchorNode.getTopLevelElementOrThrow().isEmpty() && lastSelection !== null && selection.is(lastSelection)) {
|
|
2191
|
+
if (domSelection && anchor.type === 'element' && anchor.offset === 0 && selection.isCollapsed() && !$isRootNode(anchorNode) && $getRoot().getChildrenSize() === 1 && anchorNode.getTopLevelElementOrThrow().isEmpty() && lastSelection !== null && selection.is(lastSelection)) {
|
|
2161
2192
|
domSelection.removeAllRanges();
|
|
2162
2193
|
selection.dirty = true;
|
|
2163
2194
|
}
|
|
2164
|
-
} else if ($isNodeSelection(selection) && domSelection.isCollapsed) {
|
|
2195
|
+
} else if (domSelection && $isNodeSelection(selection) && domSelection.isCollapsed) {
|
|
2165
2196
|
const domAnchor = domSelection.anchorNode; // If the user is attempting to click selection back onto text, then
|
|
2166
2197
|
// we should attempt create a range selection.
|
|
2198
|
+
// When we click on an empty paragraph node or the end of a paragraph that ends
|
|
2199
|
+
// with an image/poll, the nodeType will be ELEMENT_NODE
|
|
2167
2200
|
|
|
2168
|
-
|
|
2201
|
+
const allowedNodeType = [DOM_ELEMENT_TYPE, DOM_TEXT_TYPE];
|
|
2202
|
+
|
|
2203
|
+
if (domAnchor !== null && allowedNodeType.includes(domAnchor.nodeType)) {
|
|
2169
2204
|
const newSelection = internalCreateRangeSelection(lastSelection, domSelection, editor);
|
|
2170
2205
|
$setSelection(newSelection);
|
|
2171
2206
|
}
|
|
@@ -2476,7 +2511,14 @@ function onInput(event, editor) {
|
|
|
2476
2511
|
isFirefoxEndingComposition = false;
|
|
2477
2512
|
}
|
|
2478
2513
|
|
|
2479
|
-
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
|
|
2514
|
+
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
|
|
2515
|
+
const textLength = data.length; // Another hack for FF, as it's possible that the IME is still
|
|
2516
|
+
// open, even though compositionend has already fired (sigh).
|
|
2517
|
+
|
|
2518
|
+
if (IS_FIREFOX && textLength > 1 && event.inputType === 'insertCompositionText' && !editor.isComposing()) {
|
|
2519
|
+
selection.anchor.offset -= textLength;
|
|
2520
|
+
} // This ensures consistency on Android.
|
|
2521
|
+
|
|
2480
2522
|
|
|
2481
2523
|
if (!IS_SAFARI && !IS_IOS && editor.isComposing()) {
|
|
2482
2524
|
lastKeyDownTimeStamp = 0;
|
|
@@ -2486,7 +2528,7 @@ function onInput(event, editor) {
|
|
|
2486
2528
|
$updateSelectedTextFromDOM(editor, false); // onInput always fires after onCompositionEnd for FF.
|
|
2487
2529
|
|
|
2488
2530
|
if (isFirefoxEndingComposition) {
|
|
2489
|
-
onCompositionEndImpl(editor, data);
|
|
2531
|
+
onCompositionEndImpl(editor, data || undefined);
|
|
2490
2532
|
isFirefoxEndingComposition = false;
|
|
2491
2533
|
}
|
|
2492
2534
|
} // Also flush any other mutations that might have occurred
|
|
@@ -2532,7 +2574,7 @@ function onCompositionEndImpl(editor, data) {
|
|
|
2532
2574
|
const node = $getNodeByKey(compositionKey);
|
|
2533
2575
|
const textNode = getDOMTextNode(editor.getElementByKey(compositionKey));
|
|
2534
2576
|
|
|
2535
|
-
if (textNode !== null && $isTextNode(node)) {
|
|
2577
|
+
if (textNode !== null && textNode.nodeValue !== null && $isTextNode(node)) {
|
|
2536
2578
|
$updateTextNodeFromDOMContent(node, textNode.nodeValue, null, null, true);
|
|
2537
2579
|
}
|
|
2538
2580
|
|
|
@@ -2696,6 +2738,11 @@ const activeNestedEditorsMap = new Map();
|
|
|
2696
2738
|
|
|
2697
2739
|
function onDocumentSelectionChange(event) {
|
|
2698
2740
|
const selection = getDOMSelection();
|
|
2741
|
+
|
|
2742
|
+
if (!selection) {
|
|
2743
|
+
return;
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2699
2746
|
const nextActiveEditor = getNearestEditorFromDOMNode(selection.anchorNode);
|
|
2700
2747
|
|
|
2701
2748
|
if (nextActiveEditor === null) {
|
|
@@ -2795,7 +2842,7 @@ function removeRootElementEvents(rootElement) {
|
|
|
2795
2842
|
|
|
2796
2843
|
const editor = rootElement.__lexicalEditor;
|
|
2797
2844
|
|
|
2798
|
-
if (editor !== null
|
|
2845
|
+
if (editor !== null && editor !== undefined) {
|
|
2799
2846
|
cleanActiveNestedEditorsMap(editor); // @ts-expect-error: internal field
|
|
2800
2847
|
|
|
2801
2848
|
rootElement.__lexicalEditor = null;
|
|
@@ -3472,7 +3519,7 @@ class RangeSelection {
|
|
|
3472
3519
|
const lastIndex = selectedNodesLength - 1;
|
|
3473
3520
|
let lastNode = selectedNodes[lastIndex];
|
|
3474
3521
|
|
|
3475
|
-
if (this.isCollapsed() && startOffset === firstNodeTextLength && (firstNode.isSegmented() || firstNode.isToken() || !firstNode.canInsertTextAfter() || !firstNodeParent.canInsertTextAfter())) {
|
|
3522
|
+
if (this.isCollapsed() && startOffset === firstNodeTextLength && (firstNode.isSegmented() || firstNode.isToken() || !firstNode.canInsertTextAfter() || !firstNodeParent.canInsertTextAfter() && firstNode.getNextSibling() === null)) {
|
|
3476
3523
|
let nextSibling = firstNode.getNextSibling();
|
|
3477
3524
|
|
|
3478
3525
|
if (!$isTextNode(nextSibling) || $isTokenOrInertOrSegmented(nextSibling)) {
|
|
@@ -3493,7 +3540,7 @@ class RangeSelection {
|
|
|
3493
3540
|
this.insertText(text);
|
|
3494
3541
|
return;
|
|
3495
3542
|
}
|
|
3496
|
-
} else if (this.isCollapsed() && startOffset === 0 && (firstNode.isSegmented() || firstNode.isToken() || !firstNode.canInsertTextBefore() || !firstNodeParent.canInsertTextBefore())) {
|
|
3543
|
+
} else if (this.isCollapsed() && startOffset === 0 && (firstNode.isSegmented() || firstNode.isToken() || !firstNode.canInsertTextBefore() || !firstNodeParent.canInsertTextBefore() && firstNode.getPreviousSibling() === null)) {
|
|
3497
3544
|
let prevSibling = firstNode.getPreviousSibling();
|
|
3498
3545
|
|
|
3499
3546
|
if (!$isTextNode(prevSibling) || $isTokenOrInertOrSegmented(prevSibling)) {
|
|
@@ -3717,10 +3764,19 @@ class RangeSelection {
|
|
|
3717
3764
|
formatText(formatType) {
|
|
3718
3765
|
// TODO I wonder if this methods use selection.extract() instead?
|
|
3719
3766
|
const selectedNodes = this.getNodes();
|
|
3720
|
-
const
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3767
|
+
const selectedTextNodes = [];
|
|
3768
|
+
|
|
3769
|
+
for (const selectedNode of selectedNodes) {
|
|
3770
|
+
if ($isTextNode(selectedNode)) {
|
|
3771
|
+
selectedTextNodes.push(selectedNode);
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
|
|
3775
|
+
const selectedTextNodesLength = selectedTextNodes.length;
|
|
3776
|
+
let firstIndex = 0;
|
|
3777
|
+
const lastIndex = selectedTextNodesLength - 1;
|
|
3778
|
+
let firstNode = selectedTextNodes[0];
|
|
3779
|
+
let lastNode = selectedTextNodes[lastIndex];
|
|
3724
3780
|
|
|
3725
3781
|
if (this.isCollapsed()) {
|
|
3726
3782
|
this.toggleFormat(formatType); // When changing format, we should stop composition
|
|
@@ -3731,42 +3787,22 @@ class RangeSelection {
|
|
|
3731
3787
|
|
|
3732
3788
|
const anchor = this.anchor;
|
|
3733
3789
|
const focus = this.focus;
|
|
3790
|
+
const anchorOffset = anchor.offset;
|
|
3734
3791
|
const focusOffset = focus.offset;
|
|
3735
|
-
let firstNextFormat =
|
|
3792
|
+
let firstNextFormat = firstNode.getFormatFlags(formatType, null);
|
|
3736
3793
|
let firstNodeTextLength = firstNode.getTextContent().length;
|
|
3737
|
-
|
|
3738
|
-
for (let i = 0; i < selectedNodes.length; i++) {
|
|
3739
|
-
const selectedNode = selectedNodes[i];
|
|
3740
|
-
|
|
3741
|
-
if ($isTextNode(selectedNode)) {
|
|
3742
|
-
firstNextFormat = selectedNode.getFormatFlags(formatType, null);
|
|
3743
|
-
break;
|
|
3744
|
-
}
|
|
3745
|
-
}
|
|
3746
|
-
|
|
3747
|
-
let anchorOffset = anchor.offset;
|
|
3748
|
-
let startOffset;
|
|
3749
|
-
let endOffset;
|
|
3750
3794
|
const isBefore = anchor.isBefore(focus);
|
|
3751
|
-
|
|
3752
|
-
|
|
3795
|
+
const endOffset = isBefore ? focusOffset : anchorOffset;
|
|
3796
|
+
let startOffset = isBefore ? anchorOffset : focusOffset; // This is the case where the user only selected the very end of the
|
|
3753
3797
|
// first node so we don't want to include it in the formatting change.
|
|
3754
3798
|
|
|
3755
|
-
if (startOffset === firstNode.getTextContentSize()) {
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
if ($isTextNode(nextSibling)) {
|
|
3763
|
-
// we basically make the second node the firstNode, changing offsets accordingly
|
|
3764
|
-
anchorOffset = 0;
|
|
3765
|
-
startOffset = 0;
|
|
3766
|
-
firstNode = nextSibling;
|
|
3767
|
-
firstNodeTextLength = nextSibling.getTextContent().length;
|
|
3768
|
-
firstNextFormat = firstNode.getFormatFlags(formatType, null);
|
|
3769
|
-
}
|
|
3799
|
+
if (startOffset === firstNode.getTextContentSize() && selectedTextNodes.length > 1) {
|
|
3800
|
+
const nextNode = selectedTextNodes[1];
|
|
3801
|
+
startOffset = 0;
|
|
3802
|
+
firstIndex = 1;
|
|
3803
|
+
firstNode = nextNode;
|
|
3804
|
+
firstNodeTextLength = nextNode.getTextContentSize();
|
|
3805
|
+
firstNextFormat = nextNode.getFormatFlags(formatType, null);
|
|
3770
3806
|
} // This is the case where we only selected a single node
|
|
3771
3807
|
|
|
3772
3808
|
|
|
@@ -3777,10 +3813,8 @@ class RangeSelection {
|
|
|
3777
3813
|
firstNode.select(startOffset, endOffset);
|
|
3778
3814
|
this.format = firstNextFormat;
|
|
3779
3815
|
return;
|
|
3780
|
-
}
|
|
3816
|
+
} // No actual text is selected, so do nothing.
|
|
3781
3817
|
|
|
3782
|
-
startOffset = anchorOffset > focusOffset ? focusOffset : anchorOffset;
|
|
3783
|
-
endOffset = anchorOffset > focusOffset ? anchorOffset : focusOffset; // No actual text is selected, so do nothing.
|
|
3784
3818
|
|
|
3785
3819
|
if (startOffset === endOffset) {
|
|
3786
3820
|
return;
|
|
@@ -3803,7 +3837,9 @@ class RangeSelection {
|
|
|
3803
3837
|
} // multiple nodes selected.
|
|
3804
3838
|
|
|
3805
3839
|
} else {
|
|
3806
|
-
|
|
3840
|
+
// Note: startOffset !== firstNodeTextLength should only occur within rare programatic
|
|
3841
|
+
// update functions; transforms normalization ensure there's no empty text nodes.
|
|
3842
|
+
if ($isTextNode(firstNode) && startOffset !== firstNodeTextLength) {
|
|
3807
3843
|
if (startOffset !== 0) {
|
|
3808
3844
|
// the entire first node isn't selected, so split it
|
|
3809
3845
|
[, firstNode] = firstNode.splitText(startOffset);
|
|
@@ -3829,11 +3865,12 @@ class RangeSelection {
|
|
|
3829
3865
|
|
|
3830
3866
|
lastNode.setFormat(lastNextFormat);
|
|
3831
3867
|
}
|
|
3832
|
-
}
|
|
3868
|
+
}
|
|
3833
3869
|
|
|
3870
|
+
this.format = firstNextFormat | lastNextFormat; // deal with all the nodes in between
|
|
3834
3871
|
|
|
3835
|
-
for (let i = 1; i < lastIndex; i++) {
|
|
3836
|
-
const selectedNode =
|
|
3872
|
+
for (let i = firstIndex + 1; i < lastIndex; i++) {
|
|
3873
|
+
const selectedNode = selectedTextNodes[i];
|
|
3837
3874
|
const selectedNodeKey = selectedNode.__key;
|
|
3838
3875
|
|
|
3839
3876
|
if ($isTextNode(selectedNode) && selectedNodeKey !== firstNode.__key && selectedNodeKey !== lastNode.__key && !selectedNode.isToken()) {
|
|
@@ -3905,7 +3942,7 @@ class RangeSelection {
|
|
|
3905
3942
|
siblings.push(...nextSiblings);
|
|
3906
3943
|
const firstNode = nodes[0];
|
|
3907
3944
|
let didReplaceOrMerge = false;
|
|
3908
|
-
let
|
|
3945
|
+
let lastNode = null; // Time to insert the nodes!
|
|
3909
3946
|
|
|
3910
3947
|
for (let i = 0; i < nodes.length; i++) {
|
|
3911
3948
|
const node = nodes[i];
|
|
@@ -3968,18 +4005,17 @@ class RangeSelection {
|
|
|
3968
4005
|
|
|
3969
4006
|
if ($isElementNode(target)) {
|
|
3970
4007
|
for (let s = 0; s < childrenLength; s++) {
|
|
3971
|
-
|
|
3972
|
-
target.append(lastNodeInserted);
|
|
4008
|
+
target.append(children[s]);
|
|
3973
4009
|
}
|
|
3974
4010
|
} else {
|
|
3975
4011
|
for (let s = childrenLength - 1; s >= 0; s--) {
|
|
3976
|
-
|
|
3977
|
-
target.insertAfter(lastNodeInserted);
|
|
4012
|
+
target.insertAfter(children[s]);
|
|
3978
4013
|
}
|
|
3979
4014
|
|
|
3980
4015
|
target = target.getParentOrThrow();
|
|
3981
4016
|
}
|
|
3982
4017
|
|
|
4018
|
+
lastNode = children[childrenLength - 1];
|
|
3983
4019
|
element.remove();
|
|
3984
4020
|
didReplaceOrMerge = true;
|
|
3985
4021
|
|
|
@@ -4007,7 +4043,7 @@ class RangeSelection {
|
|
|
4007
4043
|
didReplaceOrMerge = false;
|
|
4008
4044
|
|
|
4009
4045
|
if ($isElementNode(target) && !target.isInline()) {
|
|
4010
|
-
|
|
4046
|
+
lastNode = node;
|
|
4011
4047
|
|
|
4012
4048
|
if ($isDecoratorNode(node) && node.isTopLevel()) {
|
|
4013
4049
|
target = target.insertAfter(node);
|
|
@@ -4040,8 +4076,8 @@ class RangeSelection {
|
|
|
4040
4076
|
target = target.insertAfter(node);
|
|
4041
4077
|
}
|
|
4042
4078
|
}
|
|
4043
|
-
} else if (!$isElementNode(node) || $isElementNode(node) && node.isInline() || $isDecoratorNode(target) && target.isTopLevel()) {
|
|
4044
|
-
|
|
4079
|
+
} else if (!$isElementNode(node) || $isElementNode(node) && node.isInline() || $isDecoratorNode(target) && target.isTopLevel() || $isLineBreakNode(target)) {
|
|
4080
|
+
lastNode = node;
|
|
4045
4081
|
target = target.insertAfter(node);
|
|
4046
4082
|
} else {
|
|
4047
4083
|
target = node.getParentOrThrow(); // Re-try again with the target being the parent
|
|
@@ -4070,7 +4106,7 @@ class RangeSelection {
|
|
|
4070
4106
|
if ($isElementNode(target)) {
|
|
4071
4107
|
// If the last node to be inserted was a text node,
|
|
4072
4108
|
// then we should attempt to move selection to that.
|
|
4073
|
-
const lastChild = $isTextNode(
|
|
4109
|
+
const lastChild = $isTextNode(lastNode) ? lastNode : target.getLastDescendant();
|
|
4074
4110
|
|
|
4075
4111
|
if (!selectStart) {
|
|
4076
4112
|
// Handle moving selection to end for elements
|
|
@@ -4084,18 +4120,26 @@ class RangeSelection {
|
|
|
4084
4120
|
}
|
|
4085
4121
|
|
|
4086
4122
|
if (siblings.length !== 0) {
|
|
4123
|
+
const originalTarget = target;
|
|
4124
|
+
|
|
4087
4125
|
for (let i = siblings.length - 1; i >= 0; i--) {
|
|
4088
4126
|
const sibling = siblings[i];
|
|
4089
4127
|
const prevParent = sibling.getParentOrThrow();
|
|
4090
4128
|
|
|
4091
|
-
if ($isElementNode(target) && !$
|
|
4092
|
-
target
|
|
4129
|
+
if ($isElementNode(target) && !$isBlockElementNode(sibling)) {
|
|
4130
|
+
if (originalTarget === target) {
|
|
4131
|
+
target.append(sibling);
|
|
4132
|
+
} else {
|
|
4133
|
+
target.insertBefore(sibling);
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4093
4136
|
target = sibling;
|
|
4094
|
-
} else if (!$isElementNode(target) && !$
|
|
4137
|
+
} else if (!$isElementNode(target) && !$isBlockElementNode(sibling)) {
|
|
4095
4138
|
target.insertBefore(sibling);
|
|
4096
4139
|
target = sibling;
|
|
4097
4140
|
} else {
|
|
4098
4141
|
if ($isElementNode(sibling) && !sibling.canInsertAfter(target)) {
|
|
4142
|
+
// @ts-ignore The clone method does exist on the constructor.
|
|
4099
4143
|
const prevParentClone = prevParent.constructor.clone(prevParent);
|
|
4100
4144
|
|
|
4101
4145
|
if (!$isElementNode(prevParentClone)) {
|
|
@@ -4378,13 +4422,18 @@ class RangeSelection {
|
|
|
4378
4422
|
}
|
|
4379
4423
|
}
|
|
4380
4424
|
|
|
4381
|
-
const domSelection = getDOMSelection();
|
|
4425
|
+
const domSelection = getDOMSelection();
|
|
4426
|
+
|
|
4427
|
+
if (!domSelection) {
|
|
4428
|
+
return;
|
|
4429
|
+
} // We use the DOM selection.modify API here to "tell" us what the selection
|
|
4382
4430
|
// will be. We then use it to update the Lexical selection accordingly. This
|
|
4383
4431
|
// is much more reliable than waiting for a beforeinput and using the ranges
|
|
4384
4432
|
// from getTargetRanges(), and is also better than trying to do it ourselves
|
|
4385
4433
|
// using Intl.Segmenter or other workarounds that struggle with word segments
|
|
4386
4434
|
// and line segments (especially with word wrapping and non-Roman languages).
|
|
4387
4435
|
|
|
4436
|
+
|
|
4388
4437
|
$moveNativeSelection(domSelection, alter, isBackward ? 'backward' : 'forward', granularity); // Guard against no ranges
|
|
4389
4438
|
|
|
4390
4439
|
if (domSelection.rangeCount > 0) {
|
|
@@ -4408,7 +4457,7 @@ class RangeSelection {
|
|
|
4408
4457
|
let anchorNode = anchor.getNode();
|
|
4409
4458
|
|
|
4410
4459
|
if (!isBackward && ( // Delete forward handle case
|
|
4411
|
-
anchor.type === 'element' && anchor.offset === anchorNode.getChildrenSize() || anchor.type === 'text' && anchor.offset === anchorNode.getTextContentSize())) {
|
|
4460
|
+
anchor.type === 'element' && $isElementNode(anchorNode) && anchor.offset === anchorNode.getChildrenSize() || anchor.type === 'text' && anchor.offset === anchorNode.getTextContentSize())) {
|
|
4412
4461
|
const nextSibling = anchorNode.getNextSibling() || anchorNode.getParentOrThrow().getNextSibling();
|
|
4413
4462
|
|
|
4414
4463
|
if ($isElementNode(nextSibling) && !nextSibling.canExtractContents()) {
|
|
@@ -4509,6 +4558,8 @@ function $swapPoints(selection) {
|
|
|
4509
4558
|
}
|
|
4510
4559
|
|
|
4511
4560
|
function $moveNativeSelection(domSelection, alter, direction, granularity) {
|
|
4561
|
+
// @ts-expect-error Selection.modify() method applies a change to the current selection or cursor position,
|
|
4562
|
+
// but is still non-standard in some browsers.
|
|
4512
4563
|
domSelection.modify(alter, direction, granularity);
|
|
4513
4564
|
}
|
|
4514
4565
|
|
|
@@ -4768,6 +4819,10 @@ function internalResolveSelectionPoints(anchorDOM, anchorOffset, focusDOM, focus
|
|
|
4768
4819
|
|
|
4769
4820
|
normalizeSelectionPointsForBoundaries(resolvedAnchorPoint, resolvedFocusPoint, lastSelection);
|
|
4770
4821
|
return [resolvedAnchorPoint, resolvedFocusPoint];
|
|
4822
|
+
}
|
|
4823
|
+
|
|
4824
|
+
function $isBlockElementNode(node) {
|
|
4825
|
+
return $isElementNode(node) && !node.isInline();
|
|
4771
4826
|
} // This is used to make a selection when the existing
|
|
4772
4827
|
// selection is null, i.e. forcing selection on the editor
|
|
4773
4828
|
// when it current exists outside the editor.
|
|
@@ -5116,7 +5171,7 @@ function updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
|
|
|
5116
5171
|
if (anchorOffset === nextAnchorOffset && focusOffset === nextFocusOffset && anchorDOMNode === nextAnchorNode && focusDOMNode === nextFocusNode && // Badly interpreted range selection when collapsed - #1482
|
|
5117
5172
|
!(domSelection.type === 'Range' && isCollapsed)) {
|
|
5118
5173
|
// If the root element does not have focus, ensure it has focus
|
|
5119
|
-
if (activeElement === null || !rootElement.contains(activeElement)) {
|
|
5174
|
+
if (rootElement !== null && (activeElement === null || !rootElement.contains(activeElement))) {
|
|
5120
5175
|
rootElement.focus({
|
|
5121
5176
|
preventScroll: true
|
|
5122
5177
|
});
|
|
@@ -5135,7 +5190,7 @@ function updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
|
|
|
5135
5190
|
try {
|
|
5136
5191
|
domSelection.setBaseAndExtent(nextAnchorNode, nextAnchorOffset, nextFocusNode, nextFocusOffset);
|
|
5137
5192
|
|
|
5138
|
-
if (nextSelection.isCollapsed() && rootElement === activeElement) {
|
|
5193
|
+
if (nextSelection.isCollapsed() && rootElement !== null && rootElement === activeElement) {
|
|
5139
5194
|
scrollIntoViewIfNeeded(editor, anchor, rootElement, tags);
|
|
5140
5195
|
}
|
|
5141
5196
|
|
|
@@ -5317,7 +5372,6 @@ function $applyAllTransforms(editorState, editor) {
|
|
|
5317
5372
|
}
|
|
5318
5373
|
|
|
5319
5374
|
function $parseSerializedNode(serializedNode) {
|
|
5320
|
-
// $FlowFixMe: intentional cast to our internal type
|
|
5321
5375
|
const internalSerializedNode = serializedNode;
|
|
5322
5376
|
return $parseSerializedNodeImpl(internalSerializedNode, getActiveEditor()._nodes);
|
|
5323
5377
|
}
|
|
@@ -5332,14 +5386,13 @@ function $parseSerializedNodeImpl(serializedNode, registeredNodes) {
|
|
|
5332
5386
|
}
|
|
5333
5387
|
}
|
|
5334
5388
|
|
|
5335
|
-
const nodeClass = registeredNode.klass;
|
|
5389
|
+
const nodeClass = registeredNode.klass;
|
|
5336
5390
|
|
|
5337
5391
|
if (serializedNode.type !== nodeClass.getType()) {
|
|
5338
5392
|
{
|
|
5339
5393
|
throw Error(`LexicalNode: Node ${nodeClass.name} does not implement .importJSON().`);
|
|
5340
5394
|
}
|
|
5341
|
-
}
|
|
5342
|
-
|
|
5395
|
+
}
|
|
5343
5396
|
|
|
5344
5397
|
const node = nodeClass.importJSON(serializedNode);
|
|
5345
5398
|
const children = serializedNode.children;
|
|
@@ -5477,7 +5530,9 @@ function commitPendingUpdates(editor) {
|
|
|
5477
5530
|
mutatedNodes = reconcileRoot(currentEditorState, pendingEditorState, editor, dirtyType, dirtyElements, dirtyLeaves);
|
|
5478
5531
|
} catch (error) {
|
|
5479
5532
|
// Report errors
|
|
5480
|
-
|
|
5533
|
+
if (error instanceof Error) {
|
|
5534
|
+
editor._onError(error);
|
|
5535
|
+
} // Reset editor and restore incoming editor state to the DOM
|
|
5481
5536
|
|
|
5482
5537
|
|
|
5483
5538
|
if (!isAttemptingToRecoverFromReconcilerError) {
|
|
@@ -5590,16 +5645,17 @@ function triggerTextContentListeners(editor, currentEditorState, pendingEditorSt
|
|
|
5590
5645
|
}
|
|
5591
5646
|
|
|
5592
5647
|
function triggerMutationListeners(editor, currentEditorState, pendingEditorState, mutatedNodes) {
|
|
5593
|
-
const listeners = editor._listeners.mutation;
|
|
5594
|
-
|
|
5648
|
+
const listeners = Array.from(editor._listeners.mutation);
|
|
5649
|
+
const listenersLength = listeners.length;
|
|
5650
|
+
|
|
5651
|
+
for (let i = 0; i < listenersLength; i++) {
|
|
5652
|
+
const [listener, klass] = listeners[i];
|
|
5595
5653
|
const mutatedNodesByType = mutatedNodes.get(klass);
|
|
5596
5654
|
|
|
5597
|
-
if (mutatedNodesByType
|
|
5598
|
-
|
|
5655
|
+
if (mutatedNodesByType !== undefined) {
|
|
5656
|
+
listener(mutatedNodesByType);
|
|
5599
5657
|
}
|
|
5600
|
-
|
|
5601
|
-
listener(mutatedNodesByType);
|
|
5602
|
-
});
|
|
5658
|
+
}
|
|
5603
5659
|
}
|
|
5604
5660
|
|
|
5605
5661
|
function triggerListeners(type, editor, isCurrentlyEnqueuingUpdates, ...payload) {
|
|
@@ -5610,6 +5666,7 @@ function triggerListeners(type, editor, isCurrentlyEnqueuingUpdates, ...payload)
|
|
|
5610
5666
|
const listeners = Array.from(editor._listeners[type]);
|
|
5611
5667
|
|
|
5612
5668
|
for (let i = 0; i < listeners.length; i++) {
|
|
5669
|
+
// @ts-ignore
|
|
5613
5670
|
listeners[i].apply(null, payload);
|
|
5614
5671
|
}
|
|
5615
5672
|
} finally {
|
|
@@ -5634,11 +5691,14 @@ function triggerCommandListeners(editor, type, payload) {
|
|
|
5634
5691
|
const listenerInPriorityOrder = commandListeners.get(type);
|
|
5635
5692
|
|
|
5636
5693
|
if (listenerInPriorityOrder !== undefined) {
|
|
5637
|
-
const
|
|
5694
|
+
const listenersSet = listenerInPriorityOrder[i];
|
|
5638
5695
|
|
|
5639
|
-
if (
|
|
5640
|
-
|
|
5641
|
-
|
|
5696
|
+
if (listenersSet !== undefined) {
|
|
5697
|
+
const listeners = Array.from(listenersSet);
|
|
5698
|
+
const listenersLength = listeners.length;
|
|
5699
|
+
|
|
5700
|
+
for (let j = 0; j < listenersLength; j++) {
|
|
5701
|
+
if (listeners[j](payload, editor) === true) {
|
|
5642
5702
|
return true;
|
|
5643
5703
|
}
|
|
5644
5704
|
}
|
|
@@ -5654,8 +5714,12 @@ function triggerEnqueuedUpdates(editor) {
|
|
|
5654
5714
|
const queuedUpdates = editor._updates;
|
|
5655
5715
|
|
|
5656
5716
|
if (queuedUpdates.length !== 0) {
|
|
5657
|
-
const
|
|
5658
|
-
|
|
5717
|
+
const queuedUpdate = queuedUpdates.shift();
|
|
5718
|
+
|
|
5719
|
+
if (queuedUpdate) {
|
|
5720
|
+
const [updateFn, options] = queuedUpdate;
|
|
5721
|
+
beginUpdate(editor, updateFn, options);
|
|
5722
|
+
}
|
|
5659
5723
|
}
|
|
5660
5724
|
}
|
|
5661
5725
|
|
|
@@ -5683,28 +5747,32 @@ function processNestedUpdates(editor, initialSkipTransforms) {
|
|
|
5683
5747
|
// empty.
|
|
5684
5748
|
|
|
5685
5749
|
while (queuedUpdates.length !== 0) {
|
|
5686
|
-
const
|
|
5687
|
-
let onUpdate;
|
|
5688
|
-
let tag;
|
|
5750
|
+
const queuedUpdate = queuedUpdates.shift();
|
|
5689
5751
|
|
|
5690
|
-
if (
|
|
5691
|
-
|
|
5692
|
-
|
|
5752
|
+
if (queuedUpdate) {
|
|
5753
|
+
const [nextUpdateFn, options] = queuedUpdate;
|
|
5754
|
+
let onUpdate;
|
|
5755
|
+
let tag;
|
|
5693
5756
|
|
|
5694
|
-
if (options
|
|
5695
|
-
|
|
5696
|
-
|
|
5757
|
+
if (options !== undefined) {
|
|
5758
|
+
onUpdate = options.onUpdate;
|
|
5759
|
+
tag = options.tag;
|
|
5697
5760
|
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5761
|
+
if (options.skipTransforms) {
|
|
5762
|
+
skipTransforms = true;
|
|
5763
|
+
}
|
|
5764
|
+
|
|
5765
|
+
if (onUpdate) {
|
|
5766
|
+
editor._deferred.push(onUpdate);
|
|
5767
|
+
}
|
|
5701
5768
|
|
|
5702
|
-
|
|
5703
|
-
|
|
5769
|
+
if (tag) {
|
|
5770
|
+
editor._updateTags.add(tag);
|
|
5771
|
+
}
|
|
5704
5772
|
}
|
|
5705
|
-
}
|
|
5706
5773
|
|
|
5707
|
-
|
|
5774
|
+
nextUpdateFn();
|
|
5775
|
+
}
|
|
5708
5776
|
}
|
|
5709
5777
|
|
|
5710
5778
|
return skipTransforms;
|
|
@@ -5724,7 +5792,7 @@ function beginUpdate(editor, updateFn, options) {
|
|
|
5724
5792
|
updateTags.add(tag);
|
|
5725
5793
|
}
|
|
5726
5794
|
|
|
5727
|
-
skipTransforms = options.skipTransforms;
|
|
5795
|
+
skipTransforms = options.skipTransforms || false;
|
|
5728
5796
|
}
|
|
5729
5797
|
|
|
5730
5798
|
if (onUpdate) {
|
|
@@ -5796,7 +5864,9 @@ function beginUpdate(editor, updateFn, options) {
|
|
|
5796
5864
|
}
|
|
5797
5865
|
} catch (error) {
|
|
5798
5866
|
// Report errors
|
|
5799
|
-
|
|
5867
|
+
if (error instanceof Error) {
|
|
5868
|
+
editor._onError(error);
|
|
5869
|
+
} // Restore existing editor state to the DOM
|
|
5800
5870
|
|
|
5801
5871
|
|
|
5802
5872
|
editor._pendingEditorState = currentEditorState;
|
|
@@ -6158,7 +6228,6 @@ class LexicalNode {
|
|
|
6158
6228
|
const b = node.getParents();
|
|
6159
6229
|
|
|
6160
6230
|
if ($isElementNode(this)) {
|
|
6161
|
-
// @ts-expect-error
|
|
6162
6231
|
a.unshift(this);
|
|
6163
6232
|
}
|
|
6164
6233
|
|
|
@@ -6338,7 +6407,7 @@ class LexicalNode {
|
|
|
6338
6407
|
|
|
6339
6408
|
if (latest === null) {
|
|
6340
6409
|
{
|
|
6341
|
-
throw Error(`Lexical node does not exist in active
|
|
6410
|
+
throw Error(`Lexical node does not exist in active editor state. Avoid using the same node references between nested closures from editor.read/editor.update.`);
|
|
6342
6411
|
}
|
|
6343
6412
|
}
|
|
6344
6413
|
|
|
@@ -6871,8 +6940,12 @@ class ElementNode extends LexicalNode {
|
|
|
6871
6940
|
}
|
|
6872
6941
|
|
|
6873
6942
|
hasFormat(type) {
|
|
6874
|
-
|
|
6875
|
-
|
|
6943
|
+
if (type !== '') {
|
|
6944
|
+
const formatFlag = ELEMENT_TYPE_TO_FORMAT[type];
|
|
6945
|
+
return (this.getFormat() & formatFlag) !== 0;
|
|
6946
|
+
}
|
|
6947
|
+
|
|
6948
|
+
return false;
|
|
6876
6949
|
} // Mutators
|
|
6877
6950
|
|
|
6878
6951
|
|
|
@@ -6957,7 +7030,7 @@ class ElementNode extends LexicalNode {
|
|
|
6957
7030
|
setFormat(type) {
|
|
6958
7031
|
errorOnReadOnly();
|
|
6959
7032
|
const self = this.getWritable();
|
|
6960
|
-
self.__format = ELEMENT_TYPE_TO_FORMAT[type]
|
|
7033
|
+
self.__format = type !== '' ? ELEMENT_TYPE_TO_FORMAT[type] : 0;
|
|
6961
7034
|
return this;
|
|
6962
7035
|
}
|
|
6963
7036
|
|
|
@@ -7550,20 +7623,24 @@ function setTextContent(nextText, dom, node) {
|
|
|
7550
7623
|
dom.textContent = text;
|
|
7551
7624
|
} else {
|
|
7552
7625
|
const nodeValue = firstChild.nodeValue;
|
|
7553
|
-
if (nodeValue !== text) if (isComposing || IS_FIREFOX) {
|
|
7554
|
-
// We also use the diff composed text for general text in FF to avoid
|
|
7555
|
-
// the spellcheck red line from flickering.
|
|
7556
|
-
const [index, remove, insert] = diffComposedText(nodeValue, text);
|
|
7557
7626
|
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7627
|
+
if (nodeValue !== text) {
|
|
7628
|
+
if (isComposing || IS_FIREFOX) {
|
|
7629
|
+
// We also use the diff composed text for general text in FF to avoid
|
|
7630
|
+
// We also use the diff composed text for general text in FF to avoid
|
|
7631
|
+
// the spellcheck red line from flickering.
|
|
7632
|
+
const [index, remove, insert] = diffComposedText(nodeValue, text);
|
|
7562
7633
|
|
|
7634
|
+
if (remove !== 0) {
|
|
7635
|
+
// @ts-expect-error
|
|
7636
|
+
firstChild.deleteData(index, remove);
|
|
7637
|
+
} // @ts-expect-error
|
|
7563
7638
|
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7639
|
+
|
|
7640
|
+
firstChild.insertData(index, insert);
|
|
7641
|
+
} else {
|
|
7642
|
+
firstChild.nodeValue = text;
|
|
7643
|
+
}
|
|
7567
7644
|
}
|
|
7568
7645
|
}
|
|
7569
7646
|
}
|
|
@@ -7816,19 +7893,21 @@ class TextNode extends LexicalNode {
|
|
|
7816
7893
|
|
|
7817
7894
|
selectionTransform(prevSelection, nextSelection) {
|
|
7818
7895
|
return;
|
|
7819
|
-
}
|
|
7896
|
+
} // TODO 0.4 This should just be a `string`.
|
|
7897
|
+
|
|
7820
7898
|
|
|
7821
7899
|
setFormat(format) {
|
|
7822
7900
|
errorOnReadOnly();
|
|
7823
7901
|
const self = this.getWritable();
|
|
7824
|
-
self.__format = format;
|
|
7902
|
+
self.__format = typeof format === 'string' ? TEXT_TYPE_TO_FORMAT[format] : format;
|
|
7825
7903
|
return self;
|
|
7826
|
-
}
|
|
7904
|
+
} // TODO 0.4 This should just be a `string`.
|
|
7905
|
+
|
|
7827
7906
|
|
|
7828
7907
|
setDetail(detail) {
|
|
7829
7908
|
errorOnReadOnly();
|
|
7830
7909
|
const self = this.getWritable();
|
|
7831
|
-
self.__detail = detail;
|
|
7910
|
+
self.__detail = typeof detail === 'string' ? DETAIL_TYPE_TO_DETAIL[detail] : detail;
|
|
7832
7911
|
return self;
|
|
7833
7912
|
}
|
|
7834
7913
|
|
|
@@ -8118,25 +8197,63 @@ function convertSpanElement(domNode) {
|
|
|
8118
8197
|
// domNode is a <span> since we matched it by nodeName
|
|
8119
8198
|
const span = domNode; // Google Docs uses span tags + font-weight for bold text
|
|
8120
8199
|
|
|
8121
|
-
const hasBoldFontWeight = span.style.fontWeight === '700'; // Google Docs uses span tags + text-decoration for strikethrough text
|
|
8200
|
+
const hasBoldFontWeight = span.style.fontWeight === '700'; // Google Docs uses span tags + text-decoration: line-through for strikethrough text
|
|
8122
8201
|
|
|
8123
8202
|
const hasLinethroughTextDecoration = span.style.textDecoration === 'line-through'; // Google Docs uses span tags + font-style for italic text
|
|
8124
8203
|
|
|
8125
|
-
const hasItalicFontStyle = span.style.fontStyle === 'italic';
|
|
8204
|
+
const hasItalicFontStyle = span.style.fontStyle === 'italic'; // Google Docs uses span tags + text-decoration: underline for underline text
|
|
8205
|
+
|
|
8206
|
+
const hasUnderlineTextDecoration = span.style.textDecoration === 'underline'; // Google Docs uses span tags + vertical-align to specify subscript and superscript
|
|
8207
|
+
|
|
8208
|
+
const verticalAlign = span.style.verticalAlign; // Google Docs uses span tags + color, background-color for coloring
|
|
8209
|
+
|
|
8210
|
+
const backgroundColor = span.style.backgroundColor;
|
|
8211
|
+
const textColor = span.style.color; //TODO: font-size and coloring of subscript & superscript
|
|
8212
|
+
|
|
8126
8213
|
return {
|
|
8127
8214
|
forChild: lexicalNode => {
|
|
8128
|
-
if (
|
|
8215
|
+
if (!$isTextNode(lexicalNode)) {
|
|
8216
|
+
return lexicalNode;
|
|
8217
|
+
}
|
|
8218
|
+
|
|
8219
|
+
if (hasBoldFontWeight) {
|
|
8129
8220
|
lexicalNode.toggleFormat('bold');
|
|
8130
8221
|
}
|
|
8131
8222
|
|
|
8132
|
-
if (
|
|
8223
|
+
if (hasLinethroughTextDecoration) {
|
|
8133
8224
|
lexicalNode.toggleFormat('strikethrough');
|
|
8134
8225
|
}
|
|
8135
8226
|
|
|
8136
|
-
if (
|
|
8227
|
+
if (hasItalicFontStyle) {
|
|
8137
8228
|
lexicalNode.toggleFormat('italic');
|
|
8138
8229
|
}
|
|
8139
8230
|
|
|
8231
|
+
if (hasUnderlineTextDecoration) {
|
|
8232
|
+
lexicalNode.toggleFormat('underline');
|
|
8233
|
+
}
|
|
8234
|
+
|
|
8235
|
+
if (verticalAlign === 'sub') {
|
|
8236
|
+
lexicalNode.toggleFormat('subscript');
|
|
8237
|
+
}
|
|
8238
|
+
|
|
8239
|
+
if (verticalAlign === 'super') {
|
|
8240
|
+
lexicalNode.toggleFormat('superscript');
|
|
8241
|
+
}
|
|
8242
|
+
|
|
8243
|
+
let cssString = '';
|
|
8244
|
+
|
|
8245
|
+
if (textColor && textColor !== 'rgb(0, 0, 0)') {
|
|
8246
|
+
cssString += `color: ${textColor};`;
|
|
8247
|
+
}
|
|
8248
|
+
|
|
8249
|
+
if (backgroundColor && backgroundColor !== 'transparent') {
|
|
8250
|
+
cssString += `background-color: ${backgroundColor};`;
|
|
8251
|
+
}
|
|
8252
|
+
|
|
8253
|
+
if (cssString !== '') {
|
|
8254
|
+
lexicalNode.setStyle(cssString);
|
|
8255
|
+
}
|
|
8256
|
+
|
|
8140
8257
|
return lexicalNode;
|
|
8141
8258
|
},
|
|
8142
8259
|
node: null
|
|
@@ -8162,9 +8279,9 @@ function convertBringAttentionToElement(domNode) {
|
|
|
8162
8279
|
|
|
8163
8280
|
function convertTextDOMNode(domNode) {
|
|
8164
8281
|
const {
|
|
8165
|
-
parentElement
|
|
8166
|
-
textContent
|
|
8282
|
+
parentElement
|
|
8167
8283
|
} = domNode;
|
|
8284
|
+
const textContent = domNode.textContent || '';
|
|
8168
8285
|
const textContentTrim = textContent.trim();
|
|
8169
8286
|
const isPre = parentElement != null && parentElement.tagName.toLowerCase() === 'pre';
|
|
8170
8287
|
|
|
@@ -8261,10 +8378,8 @@ class ParagraphNode extends ElementNode {
|
|
|
8261
8378
|
element
|
|
8262
8379
|
} = super.exportDOM(editor);
|
|
8263
8380
|
|
|
8264
|
-
if (element) {
|
|
8265
|
-
|
|
8266
|
-
element.append(document.createElement('br'));
|
|
8267
|
-
}
|
|
8381
|
+
if (element && this.isEmpty()) {
|
|
8382
|
+
element.append(document.createElement('br'));
|
|
8268
8383
|
}
|
|
8269
8384
|
|
|
8270
8385
|
return {
|
|
@@ -8388,9 +8503,7 @@ function initializeConversionCache(nodes) {
|
|
|
8388
8503
|
const conversionCache = new Map();
|
|
8389
8504
|
const handledConversions = new Set();
|
|
8390
8505
|
nodes.forEach(node => {
|
|
8391
|
-
const importDOM =
|
|
8392
|
-
node.klass.importDOM != null ? // @ts-expect-error TODO Replace Class utility type with InstanceType
|
|
8393
|
-
node.klass.importDOM.bind(node.klass) : null;
|
|
8506
|
+
const importDOM = node.klass.importDOM != null ? node.klass.importDOM.bind(node.klass) : null;
|
|
8394
8507
|
|
|
8395
8508
|
if (importDOM == null || handledConversions.has(importDOM)) {
|
|
8396
8509
|
return;
|
|
@@ -8458,7 +8571,7 @@ function createEditor(editorConfig) {
|
|
|
8458
8571
|
if (proto instanceof DecoratorNode) {
|
|
8459
8572
|
// eslint-disable-next-line no-prototype-builtins
|
|
8460
8573
|
if (!proto.hasOwnProperty('decorate')) {
|
|
8461
|
-
console.warn(`${
|
|
8574
|
+
console.warn(`${proto.constructor.name} must implement "decorate" method`);
|
|
8462
8575
|
}
|
|
8463
8576
|
}
|
|
8464
8577
|
|
|
@@ -8472,8 +8585,7 @@ function createEditor(editorConfig) {
|
|
|
8472
8585
|
console.warn(`${name} should implement "exportJSON" method to ensure JSON and default HTML serialization works as expected`);
|
|
8473
8586
|
}
|
|
8474
8587
|
}
|
|
8475
|
-
}
|
|
8476
|
-
|
|
8588
|
+
}
|
|
8477
8589
|
|
|
8478
8590
|
const type = klass.getType();
|
|
8479
8591
|
registeredNodes.set(type, {
|
|
@@ -8487,7 +8599,7 @@ function createEditor(editorConfig) {
|
|
|
8487
8599
|
disableEvents,
|
|
8488
8600
|
namespace,
|
|
8489
8601
|
theme
|
|
8490
|
-
}, onError, initializeConversionCache(registeredNodes), isReadOnly);
|
|
8602
|
+
}, onError ? onError : console.error, initializeConversionCache(registeredNodes), isReadOnly);
|
|
8491
8603
|
|
|
8492
8604
|
if (initialEditorState !== undefined) {
|
|
8493
8605
|
editor._pendingEditorState = initialEditorState;
|
|
@@ -8626,7 +8738,6 @@ class LexicalEditor {
|
|
|
8626
8738
|
}
|
|
8627
8739
|
|
|
8628
8740
|
registerMutationListener(klass, listener) {
|
|
8629
|
-
// @ts-expect-error TODO Replace Class utility type with InstanceType
|
|
8630
8741
|
const registeredNode = this._nodes.get(klass.getType());
|
|
8631
8742
|
|
|
8632
8743
|
if (registeredNode === undefined) {
|
|
@@ -8643,7 +8754,6 @@ class LexicalEditor {
|
|
|
8643
8754
|
}
|
|
8644
8755
|
|
|
8645
8756
|
registerNodeTransform(klass, listener) {
|
|
8646
|
-
// @ts-expect-error TODO Replace Class utility type with InstanceType
|
|
8647
8757
|
const type = klass.getType();
|
|
8648
8758
|
|
|
8649
8759
|
const registeredNode = this._nodes.get(type);
|
|
@@ -8664,8 +8774,7 @@ class LexicalEditor {
|
|
|
8664
8774
|
|
|
8665
8775
|
hasNodes(nodes) {
|
|
8666
8776
|
for (let i = 0; i < nodes.length; i++) {
|
|
8667
|
-
const klass = nodes[i];
|
|
8668
|
-
|
|
8777
|
+
const klass = nodes[i];
|
|
8669
8778
|
const type = klass.getType();
|
|
8670
8779
|
|
|
8671
8780
|
if (!this._nodes.has(type)) {
|
|
@@ -8780,7 +8889,7 @@ class LexicalEditor {
|
|
|
8780
8889
|
updateEditor(this, updateFn, options);
|
|
8781
8890
|
}
|
|
8782
8891
|
|
|
8783
|
-
focus(callbackFn) {
|
|
8892
|
+
focus(callbackFn, options = {}) {
|
|
8784
8893
|
const rootElement = this._rootElement;
|
|
8785
8894
|
|
|
8786
8895
|
if (rootElement !== null) {
|
|
@@ -8794,7 +8903,11 @@ class LexicalEditor {
|
|
|
8794
8903
|
// Marking the selection dirty will force the selection back to it
|
|
8795
8904
|
selection.dirty = true;
|
|
8796
8905
|
} else if (root.getChildrenSize() !== 0) {
|
|
8797
|
-
|
|
8906
|
+
if (options.defaultSelection === 'rootStart') {
|
|
8907
|
+
root.selectStart();
|
|
8908
|
+
} else {
|
|
8909
|
+
root.selectEnd();
|
|
8910
|
+
}
|
|
8798
8911
|
}
|
|
8799
8912
|
}, {
|
|
8800
8913
|
onUpdate: () => {
|
|
@@ -8827,8 +8940,10 @@ class LexicalEditor {
|
|
|
8827
8940
|
}
|
|
8828
8941
|
|
|
8829
8942
|
setReadOnly(readOnly) {
|
|
8830
|
-
this._readOnly
|
|
8831
|
-
|
|
8943
|
+
if (this._readOnly !== readOnly) {
|
|
8944
|
+
this._readOnly = readOnly;
|
|
8945
|
+
triggerListeners('readonly', this, true, readOnly);
|
|
8946
|
+
}
|
|
8832
8947
|
}
|
|
8833
8948
|
|
|
8834
8949
|
toJSON() {
|
|
@@ -8846,7 +8961,7 @@ class LexicalEditor {
|
|
|
8846
8961
|
* LICENSE file in the root directory of this source tree.
|
|
8847
8962
|
*
|
|
8848
8963
|
*/
|
|
8849
|
-
const VERSION = '0.3.
|
|
8964
|
+
const VERSION = '0.3.7';
|
|
8850
8965
|
|
|
8851
8966
|
/**
|
|
8852
8967
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|