lexical 0.3.3 → 0.3.6
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 +378 -293
- package/Lexical.js.flow +15 -10
- package/Lexical.prod.js +174 -171
- package/LexicalCommands.d.ts +50 -0
- package/LexicalConstants.d.ts +43 -0
- package/LexicalEditor.d.ts +199 -0
- package/LexicalEditorState.d.ts +28 -0
- package/LexicalEvents.d.ts +14 -0
- package/LexicalGC.d.ts +12 -0
- package/LexicalMutations.d.ts +12 -0
- package/LexicalNode.d.ts +82 -0
- package/LexicalNormalization.d.ts +9 -0
- package/LexicalReconciler.d.ts +12 -0
- package/LexicalSelection.d.ts +147 -0
- package/LexicalUpdates.d.ts +25 -0
- package/LexicalUtils.d.ts +94 -0
- package/LexicalVersion.d.ts +8 -0
- package/index.d.ts +31 -0
- package/nodes/LexicalDecoratorNode.d.ts +18 -0
- package/nodes/LexicalElementNode.d.ts +71 -0
- package/nodes/LexicalGridCellNode.d.ts +18 -0
- package/nodes/LexicalGridNode.d.ts +12 -0
- package/nodes/LexicalGridRowNode.d.ts +12 -0
- package/nodes/LexicalLineBreakNode.d.ts +26 -0
- package/nodes/LexicalParagraphNode.d.ts +30 -0
- package/nodes/LexicalRootNode.d.ts +29 -0
- package/nodes/LexicalTextNode.d.ts +75 -0
- package/package.json +1 -5
- package/Lexical.d.ts +0 -853
package/Lexical.dev.js
CHANGED
|
@@ -6,6 +6,58 @@
|
|
|
6
6
|
*/
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
11
|
+
*
|
|
12
|
+
* This source code is licensed under the MIT license found in the
|
|
13
|
+
* LICENSE file in the root directory of this source tree.
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
function createCommand() {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
const SELECTION_CHANGE_COMMAND = createCommand();
|
|
20
|
+
const CLICK_COMMAND = createCommand();
|
|
21
|
+
const DELETE_CHARACTER_COMMAND = createCommand();
|
|
22
|
+
const INSERT_LINE_BREAK_COMMAND = createCommand();
|
|
23
|
+
const INSERT_PARAGRAPH_COMMAND = createCommand();
|
|
24
|
+
const CONTROLLED_TEXT_INSERTION_COMMAND = createCommand();
|
|
25
|
+
const PASTE_COMMAND = createCommand();
|
|
26
|
+
const REMOVE_TEXT_COMMAND = createCommand();
|
|
27
|
+
const DELETE_WORD_COMMAND = createCommand();
|
|
28
|
+
const DELETE_LINE_COMMAND = createCommand();
|
|
29
|
+
const FORMAT_TEXT_COMMAND = createCommand();
|
|
30
|
+
const UNDO_COMMAND = createCommand();
|
|
31
|
+
const REDO_COMMAND = createCommand();
|
|
32
|
+
const KEY_ARROW_RIGHT_COMMAND = createCommand();
|
|
33
|
+
const MOVE_TO_END = createCommand();
|
|
34
|
+
const KEY_ARROW_LEFT_COMMAND = createCommand();
|
|
35
|
+
const MOVE_TO_START = createCommand();
|
|
36
|
+
const KEY_ARROW_UP_COMMAND = createCommand();
|
|
37
|
+
const KEY_ARROW_DOWN_COMMAND = createCommand();
|
|
38
|
+
const KEY_ENTER_COMMAND = createCommand();
|
|
39
|
+
const KEY_SPACE_COMMAND = createCommand();
|
|
40
|
+
const KEY_BACKSPACE_COMMAND = createCommand();
|
|
41
|
+
const KEY_ESCAPE_COMMAND = createCommand();
|
|
42
|
+
const KEY_DELETE_COMMAND = createCommand();
|
|
43
|
+
const KEY_TAB_COMMAND = createCommand();
|
|
44
|
+
const INDENT_CONTENT_COMMAND = createCommand();
|
|
45
|
+
const OUTDENT_CONTENT_COMMAND = createCommand();
|
|
46
|
+
const DROP_COMMAND = createCommand();
|
|
47
|
+
const FORMAT_ELEMENT_COMMAND = createCommand();
|
|
48
|
+
const DRAGSTART_COMMAND = createCommand();
|
|
49
|
+
const DRAGOVER_COMMAND = createCommand();
|
|
50
|
+
const DRAGEND_COMMAND = createCommand();
|
|
51
|
+
const COPY_COMMAND = createCommand();
|
|
52
|
+
const CUT_COMMAND = createCommand();
|
|
53
|
+
const CLEAR_EDITOR_COMMAND = createCommand();
|
|
54
|
+
const CLEAR_HISTORY_COMMAND = createCommand();
|
|
55
|
+
const CAN_REDO_COMMAND = createCommand();
|
|
56
|
+
const CAN_UNDO_COMMAND = createCommand();
|
|
57
|
+
const FOCUS_COMMAND = createCommand();
|
|
58
|
+
const BLUR_COMMAND = createCommand();
|
|
59
|
+
const KEY_MODIFIER_COMMAND = createCommand();
|
|
60
|
+
|
|
9
61
|
/**
|
|
10
62
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
11
63
|
*
|
|
@@ -96,6 +148,10 @@ const TEXT_TYPE_TO_FORMAT = {
|
|
|
96
148
|
superscript: IS_SUPERSCRIPT,
|
|
97
149
|
underline: IS_UNDERLINE
|
|
98
150
|
};
|
|
151
|
+
const DETAIL_TYPE_TO_DETAIL = {
|
|
152
|
+
directionless: IS_DIRECTIONLESS,
|
|
153
|
+
unmergeable: IS_UNMERGEABLE
|
|
154
|
+
};
|
|
99
155
|
const ELEMENT_TYPE_TO_FORMAT = {
|
|
100
156
|
center: IS_ALIGN_CENTER,
|
|
101
157
|
justify: IS_ALIGN_JUSTIFY,
|
|
@@ -148,7 +204,8 @@ function initTextEntryListener() {
|
|
|
148
204
|
|
|
149
205
|
function isManagedLineBreak(dom, target, editor) {
|
|
150
206
|
return (// @ts-expect-error: internal field
|
|
151
|
-
target.__lexicalLineBreak === dom ||
|
|
207
|
+
target.__lexicalLineBreak === dom || // @ts-ignore We intentionally add this to the Node.
|
|
208
|
+
dom[`__lexicalKey_${editor._key}`] !== undefined
|
|
152
209
|
);
|
|
153
210
|
}
|
|
154
211
|
|
|
@@ -170,7 +227,22 @@ function handleTextMutation(target, node, editor) {
|
|
|
170
227
|
}
|
|
171
228
|
|
|
172
229
|
const text = target.nodeValue;
|
|
173
|
-
|
|
230
|
+
|
|
231
|
+
if (text !== null) {
|
|
232
|
+
$updateTextNodeFromDOMContent(node, text, anchorOffset, focusOffset, false);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function shouldUpdateTextNodeFromMutation(selection, targetDOM, targetNode) {
|
|
237
|
+
if ($isRangeSelection(selection)) {
|
|
238
|
+
const anchorNode = selection.anchor.getNode();
|
|
239
|
+
|
|
240
|
+
if (anchorNode.is(targetNode) && selection.format !== anchorNode.getFormat()) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return targetDOM.nodeType === DOM_TEXT_TYPE && targetNode.isAttached();
|
|
174
246
|
}
|
|
175
247
|
|
|
176
248
|
function $flushMutations$1(editor, mutations, observer) {
|
|
@@ -179,6 +251,7 @@ function $flushMutations$1(editor, mutations, observer) {
|
|
|
179
251
|
|
|
180
252
|
try {
|
|
181
253
|
updateEditor(editor, () => {
|
|
254
|
+
const selection = $getSelection() || getLastSelection(editor);
|
|
182
255
|
const badDOMTargets = new Map();
|
|
183
256
|
const rootElement = editor.getRootElement(); // We use the current edtior state, as that reflects what is
|
|
184
257
|
// actually "on screen".
|
|
@@ -200,7 +273,7 @@ function $flushMutations$1(editor, mutations, observer) {
|
|
|
200
273
|
if (type === 'characterData') {
|
|
201
274
|
// Text mutations are deferred and passed to mutation listeners to be
|
|
202
275
|
// processed outside of the Lexical engine.
|
|
203
|
-
if (shouldFlushTextMutations &&
|
|
276
|
+
if (shouldFlushTextMutations && $isTextNode(targetNode) && shouldUpdateTextNodeFromMutation(selection, targetDOM, targetNode)) {
|
|
204
277
|
handleTextMutation( // nodeType === DOM_TEXT_TYPE is a Text DOM node
|
|
205
278
|
targetDOM, targetNode);
|
|
206
279
|
}
|
|
@@ -315,8 +388,6 @@ function $flushMutations$1(editor, mutations, observer) {
|
|
|
315
388
|
observer.takeRecords();
|
|
316
389
|
}
|
|
317
390
|
|
|
318
|
-
const selection = $getSelection() || getLastSelection(editor);
|
|
319
|
-
|
|
320
391
|
if (selection !== null) {
|
|
321
392
|
if (shouldRevertSelection) {
|
|
322
393
|
selection.dirty = true;
|
|
@@ -598,8 +669,9 @@ function $getNodeByKey(key, _editorState) {
|
|
|
598
669
|
return node;
|
|
599
670
|
}
|
|
600
671
|
function getNodeFromDOMNode(dom, editorState) {
|
|
601
|
-
const editor = getActiveEditor();
|
|
602
|
-
|
|
672
|
+
const editor = getActiveEditor(); // @ts-ignore We intentionally add this to the Node.
|
|
673
|
+
|
|
674
|
+
const key = dom[`__lexicalKey_${editor._key}`];
|
|
603
675
|
|
|
604
676
|
if (key !== undefined) {
|
|
605
677
|
return $getNodeByKey(key, editorState);
|
|
@@ -664,7 +736,6 @@ function $setSelection(selection) {
|
|
|
664
736
|
const editorState = getActiveEditorState();
|
|
665
737
|
|
|
666
738
|
if (selection !== null) {
|
|
667
|
-
// @ts-ignore
|
|
668
739
|
{
|
|
669
740
|
if (Object.isFrozen(selection)) {
|
|
670
741
|
{
|
|
@@ -709,7 +780,8 @@ dom, editor) {
|
|
|
709
780
|
let node = dom;
|
|
710
781
|
|
|
711
782
|
while (node != null) {
|
|
712
|
-
|
|
783
|
+
// @ts-ignore We intentionally add this to the Node.
|
|
784
|
+
const key = node[`__lexicalKey_${editor._key}`];
|
|
713
785
|
|
|
714
786
|
if (key !== undefined) {
|
|
715
787
|
return key;
|
|
@@ -765,7 +837,9 @@ function $updateSelectedTextFromDOM(editor, isCompositionEnd, data) {
|
|
|
765
837
|
focusOffset = offset;
|
|
766
838
|
}
|
|
767
839
|
|
|
768
|
-
|
|
840
|
+
if (textContent !== null) {
|
|
841
|
+
$updateTextNodeFromDOMContent(node, textContent, anchorOffset, focusOffset, isCompositionEnd);
|
|
842
|
+
}
|
|
769
843
|
}
|
|
770
844
|
}
|
|
771
845
|
}
|
|
@@ -835,6 +909,11 @@ function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focu
|
|
|
835
909
|
}
|
|
836
910
|
}
|
|
837
911
|
|
|
912
|
+
function $previousSiblingDoesNotAcceptText(node) {
|
|
913
|
+
const previousSibling = node.getPreviousSibling();
|
|
914
|
+
return ($isTextNode(previousSibling) || $isElementNode(previousSibling) && previousSibling.isInline()) && !previousSibling.canInsertTextAfter();
|
|
915
|
+
}
|
|
916
|
+
|
|
838
917
|
function $shouldInsertTextAfterOrBeforeTextNode(selection, node) {
|
|
839
918
|
if (node.isSegmented()) {
|
|
840
919
|
return true;
|
|
@@ -847,7 +926,7 @@ function $shouldInsertTextAfterOrBeforeTextNode(selection, node) {
|
|
|
847
926
|
const offset = selection.anchor.offset;
|
|
848
927
|
const parent = node.getParentOrThrow();
|
|
849
928
|
const isToken = node.isToken();
|
|
850
|
-
const shouldInsertTextBefore = offset === 0 && (!node.canInsertTextBefore() || !parent.canInsertTextBefore() || isToken);
|
|
929
|
+
const shouldInsertTextBefore = offset === 0 && (!node.canInsertTextBefore() || !parent.canInsertTextBefore() || isToken || $previousSiblingDoesNotAcceptText(node));
|
|
851
930
|
const shouldInsertTextAfter = node.getTextContentSize() === offset && (!node.canInsertTextBefore() || !parent.canInsertTextBefore() || isToken);
|
|
852
931
|
return shouldInsertTextBefore || shouldInsertTextAfter;
|
|
853
932
|
} // This function is used to determine if Lexical should attempt to override
|
|
@@ -961,6 +1040,8 @@ function isCopy(keyCode, shiftKey, metaKey, ctrlKey) {
|
|
|
961
1040
|
if (keyCode === 67) {
|
|
962
1041
|
return IS_APPLE ? metaKey : ctrlKey;
|
|
963
1042
|
}
|
|
1043
|
+
|
|
1044
|
+
return false;
|
|
964
1045
|
}
|
|
965
1046
|
function isCut(keyCode, shiftKey, metaKey, ctrlKey) {
|
|
966
1047
|
if (shiftKey) {
|
|
@@ -970,6 +1051,8 @@ function isCut(keyCode, shiftKey, metaKey, ctrlKey) {
|
|
|
970
1051
|
if (keyCode === 88) {
|
|
971
1052
|
return IS_APPLE ? metaKey : ctrlKey;
|
|
972
1053
|
}
|
|
1054
|
+
|
|
1055
|
+
return false;
|
|
973
1056
|
}
|
|
974
1057
|
|
|
975
1058
|
function isArrowLeft(keyCode) {
|
|
@@ -1075,8 +1158,7 @@ function setMutatedNode(mutatedNodes, registeredNodes, mutationListeners, node,
|
|
|
1075
1158
|
}
|
|
1076
1159
|
function $nodesOfType(klass) {
|
|
1077
1160
|
const editorState = getActiveEditorState();
|
|
1078
|
-
const readOnly = editorState._readOnly;
|
|
1079
|
-
|
|
1161
|
+
const readOnly = editorState._readOnly;
|
|
1080
1162
|
const klassType = klass.getType();
|
|
1081
1163
|
const nodes = editorState._nodeMap;
|
|
1082
1164
|
const nodesOfType = [];
|
|
@@ -1474,7 +1556,7 @@ function createNode(key, parentDOM, insertDOM) {
|
|
|
1474
1556
|
const text = node.getTextContent();
|
|
1475
1557
|
|
|
1476
1558
|
if ($isDecoratorNode(node)) {
|
|
1477
|
-
const decorator = node.decorate(activeEditor$1);
|
|
1559
|
+
const decorator = node.decorate(activeEditor$1, activeEditorConfig);
|
|
1478
1560
|
|
|
1479
1561
|
if (decorator !== null) {
|
|
1480
1562
|
reconcileDecorator(key, decorator);
|
|
@@ -1514,8 +1596,7 @@ function createNode(key, parentDOM, insertDOM) {
|
|
|
1514
1596
|
parentDOM.appendChild(dom);
|
|
1515
1597
|
}
|
|
1516
1598
|
}
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1599
|
+
}
|
|
1519
1600
|
|
|
1520
1601
|
{
|
|
1521
1602
|
// Freeze the node in DEV to prevent accidental mutations
|
|
@@ -1597,10 +1678,9 @@ function reconcileBlockDirection(element, dom) {
|
|
|
1597
1678
|
|
|
1598
1679
|
if (previousDirectionTheme !== undefined) {
|
|
1599
1680
|
if (typeof previousDirectionTheme === 'string') {
|
|
1600
|
-
const classNamesArr = previousDirectionTheme.split(' ');
|
|
1601
|
-
|
|
1681
|
+
const classNamesArr = previousDirectionTheme.split(' ');
|
|
1602
1682
|
previousDirectionTheme = theme[previousDirection] = classNamesArr;
|
|
1603
|
-
} // @ts-
|
|
1683
|
+
} // @ts-ignore: intentional
|
|
1604
1684
|
|
|
1605
1685
|
|
|
1606
1686
|
classList.remove(...previousDirectionTheme);
|
|
@@ -1618,7 +1698,9 @@ function reconcileBlockDirection(element, dom) {
|
|
|
1618
1698
|
nextDirectionTheme = theme[direction] = classNamesArr;
|
|
1619
1699
|
}
|
|
1620
1700
|
|
|
1621
|
-
|
|
1701
|
+
if (nextDirectionTheme !== undefined) {
|
|
1702
|
+
classList.add(...nextDirectionTheme);
|
|
1703
|
+
}
|
|
1622
1704
|
} // Update direction
|
|
1623
1705
|
|
|
1624
1706
|
|
|
@@ -1790,7 +1872,7 @@ function reconcileNode(key, parentDOM) {
|
|
|
1790
1872
|
const text = nextNode.getTextContent();
|
|
1791
1873
|
|
|
1792
1874
|
if ($isDecoratorNode(nextNode)) {
|
|
1793
|
-
const decorator = nextNode.decorate(activeEditor$1);
|
|
1875
|
+
const decorator = nextNode.decorate(activeEditor$1, activeEditorConfig);
|
|
1794
1876
|
|
|
1795
1877
|
if (decorator !== null) {
|
|
1796
1878
|
reconcileDecorator(key, decorator);
|
|
@@ -1811,8 +1893,7 @@ function reconcileNode(key, parentDOM) {
|
|
|
1811
1893
|
// Cache the latest text content.
|
|
1812
1894
|
nextNode = nextNode.getWritable();
|
|
1813
1895
|
nextNode.__cachedText = editorTextContent;
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1896
|
+
}
|
|
1816
1897
|
|
|
1817
1898
|
{
|
|
1818
1899
|
// Freeze the node in DEV to prevent accidental mutations
|
|
@@ -1941,20 +2022,30 @@ function reconcileRoot(prevEditorState, nextEditorState, editor, dirtyType, dirt
|
|
|
1941
2022
|
// so instead we make it seem that these values are always set.
|
|
1942
2023
|
// We also want to make sure we clear them down, otherwise we
|
|
1943
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
|
|
1944
2042
|
|
|
1945
|
-
activeEditor$1 = undefined;
|
|
1946
|
-
activeEditorNodes = undefined;
|
|
1947
|
-
activeDirtyElements = undefined;
|
|
1948
|
-
activeDirtyLeaves = undefined;
|
|
1949
|
-
activePrevNodeMap = undefined;
|
|
1950
|
-
activeNextNodeMap = undefined;
|
|
1951
|
-
activeEditorConfig = undefined;
|
|
1952
|
-
activePrevKeyToDOMMap = undefined;
|
|
1953
2043
|
mutatedNodes = undefined;
|
|
1954
2044
|
return currentMutatedNodes;
|
|
1955
2045
|
}
|
|
1956
2046
|
function storeDOMWithKey(key, dom, editor) {
|
|
1957
|
-
const keyToDOMMap = editor._keyToDOMMap;
|
|
2047
|
+
const keyToDOMMap = editor._keyToDOMMap; // @ts-ignore We intentionally add this to the Node.
|
|
2048
|
+
|
|
1958
2049
|
dom['__lexicalKey_' + editor._key] = key;
|
|
1959
2050
|
keyToDOMMap.set(key, dom);
|
|
1960
2051
|
}
|
|
@@ -1971,58 +2062,6 @@ function getPrevElementByKeyOrThrow(key) {
|
|
|
1971
2062
|
return element;
|
|
1972
2063
|
}
|
|
1973
2064
|
|
|
1974
|
-
/**
|
|
1975
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
1976
|
-
*
|
|
1977
|
-
* This source code is licensed under the MIT license found in the
|
|
1978
|
-
* LICENSE file in the root directory of this source tree.
|
|
1979
|
-
*
|
|
1980
|
-
*/
|
|
1981
|
-
function createCommand() {
|
|
1982
|
-
return {};
|
|
1983
|
-
}
|
|
1984
|
-
const SELECTION_CHANGE_COMMAND = createCommand();
|
|
1985
|
-
const CLICK_COMMAND = createCommand();
|
|
1986
|
-
const DELETE_CHARACTER_COMMAND = createCommand();
|
|
1987
|
-
const INSERT_LINE_BREAK_COMMAND = createCommand();
|
|
1988
|
-
const INSERT_PARAGRAPH_COMMAND = createCommand();
|
|
1989
|
-
const CONTROLLED_TEXT_INSERTION_COMMAND = createCommand();
|
|
1990
|
-
const PASTE_COMMAND = createCommand();
|
|
1991
|
-
const REMOVE_TEXT_COMMAND = createCommand();
|
|
1992
|
-
const DELETE_WORD_COMMAND = createCommand();
|
|
1993
|
-
const DELETE_LINE_COMMAND = createCommand();
|
|
1994
|
-
const FORMAT_TEXT_COMMAND = createCommand();
|
|
1995
|
-
const UNDO_COMMAND = createCommand();
|
|
1996
|
-
const REDO_COMMAND = createCommand();
|
|
1997
|
-
const KEY_ARROW_RIGHT_COMMAND = createCommand();
|
|
1998
|
-
const MOVE_TO_END = createCommand();
|
|
1999
|
-
const KEY_ARROW_LEFT_COMMAND = createCommand();
|
|
2000
|
-
const MOVE_TO_START = createCommand();
|
|
2001
|
-
const KEY_ARROW_UP_COMMAND = createCommand();
|
|
2002
|
-
const KEY_ARROW_DOWN_COMMAND = createCommand();
|
|
2003
|
-
const KEY_ENTER_COMMAND = createCommand();
|
|
2004
|
-
const KEY_SPACE_COMMAND = createCommand();
|
|
2005
|
-
const KEY_BACKSPACE_COMMAND = createCommand();
|
|
2006
|
-
const KEY_ESCAPE_COMMAND = createCommand();
|
|
2007
|
-
const KEY_DELETE_COMMAND = createCommand();
|
|
2008
|
-
const KEY_TAB_COMMAND = createCommand();
|
|
2009
|
-
const INDENT_CONTENT_COMMAND = createCommand();
|
|
2010
|
-
const OUTDENT_CONTENT_COMMAND = createCommand();
|
|
2011
|
-
const DROP_COMMAND = createCommand();
|
|
2012
|
-
const FORMAT_ELEMENT_COMMAND = createCommand();
|
|
2013
|
-
const DRAGSTART_COMMAND = createCommand();
|
|
2014
|
-
const DRAGOVER_COMMAND = createCommand();
|
|
2015
|
-
const DRAGEND_COMMAND = createCommand();
|
|
2016
|
-
const COPY_COMMAND = createCommand();
|
|
2017
|
-
const CUT_COMMAND = createCommand();
|
|
2018
|
-
const CLEAR_EDITOR_COMMAND = createCommand();
|
|
2019
|
-
const CLEAR_HISTORY_COMMAND = createCommand();
|
|
2020
|
-
const CAN_REDO_COMMAND = createCommand();
|
|
2021
|
-
const CAN_UNDO_COMMAND = createCommand();
|
|
2022
|
-
const FOCUS_COMMAND = createCommand();
|
|
2023
|
-
const BLUR_COMMAND = createCommand();
|
|
2024
|
-
const KEY_MODIFIER_COMMAND = createCommand();
|
|
2025
|
-
|
|
2026
2065
|
/**
|
|
2027
2066
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2028
2067
|
*
|
|
@@ -2035,10 +2074,11 @@ const ANDROID_COMPOSITION_LATENCY = 30;
|
|
|
2035
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], ['paste', PASS_THROUGH_COMMAND], ['focus', PASS_THROUGH_COMMAND], ['blur', PASS_THROUGH_COMMAND], ['drop', PASS_THROUGH_COMMAND]];
|
|
2036
2075
|
|
|
2037
2076
|
if (CAN_USE_BEFORE_INPUT) {
|
|
2038
|
-
rootElementEvents.push(['beforeinput', onBeforeInput]);
|
|
2077
|
+
rootElementEvents.push(['beforeinput', (event, editor) => onBeforeInput(event, editor)]);
|
|
2039
2078
|
}
|
|
2040
2079
|
|
|
2041
2080
|
let lastKeyDownTimeStamp = 0;
|
|
2081
|
+
let lastKeyCode = 0;
|
|
2042
2082
|
let rootElementsRegistered = 0;
|
|
2043
2083
|
let isSelectionChangeFromDOMUpdate = false;
|
|
2044
2084
|
let isInsertLineBreak = false;
|
|
@@ -2046,7 +2086,7 @@ let isFirefoxEndingComposition = false;
|
|
|
2046
2086
|
let collapsedSelectionFormat = [0, 0, 'root', 0];
|
|
2047
2087
|
|
|
2048
2088
|
function shouldSkipSelectionChange(domNode, offset) {
|
|
2049
|
-
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;
|
|
2050
2090
|
}
|
|
2051
2091
|
|
|
2052
2092
|
function onSelectionChange(domSelection, editor, isActive) {
|
|
@@ -2148,15 +2188,19 @@ function onClick(event, editor) {
|
|
|
2148
2188
|
const anchor = selection.anchor;
|
|
2149
2189
|
const anchorNode = anchor.getNode();
|
|
2150
2190
|
|
|
2151
|
-
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)) {
|
|
2152
2192
|
domSelection.removeAllRanges();
|
|
2153
2193
|
selection.dirty = true;
|
|
2154
2194
|
}
|
|
2155
|
-
} else if ($isNodeSelection(selection) && domSelection.isCollapsed) {
|
|
2195
|
+
} else if (domSelection && $isNodeSelection(selection) && domSelection.isCollapsed) {
|
|
2156
2196
|
const domAnchor = domSelection.anchorNode; // If the user is attempting to click selection back onto text, then
|
|
2157
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
|
|
2158
2200
|
|
|
2159
|
-
|
|
2201
|
+
const allowedNodeType = [DOM_ELEMENT_TYPE, DOM_TEXT_TYPE];
|
|
2202
|
+
|
|
2203
|
+
if (domAnchor !== null && allowedNodeType.includes(domAnchor.nodeType)) {
|
|
2160
2204
|
const newSelection = internalCreateRangeSelection(lastSelection, domSelection, editor);
|
|
2161
2205
|
$setSelection(newSelection);
|
|
2162
2206
|
}
|
|
@@ -2180,6 +2224,10 @@ function $canRemoveText(anchorNode, focusNode) {
|
|
|
2180
2224
|
return anchorNode !== focusNode || $isElementNode(anchorNode) || $isElementNode(focusNode) || !$isTokenOrInert(anchorNode) || !$isTokenOrInert(focusNode);
|
|
2181
2225
|
}
|
|
2182
2226
|
|
|
2227
|
+
function isPossiblyAndroidKeyPress(timeStamp) {
|
|
2228
|
+
return lastKeyCode === 229 && timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY;
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2183
2231
|
function onBeforeInput(event, editor) {
|
|
2184
2232
|
const inputType = event.inputType; // We let the browser do its own thing for composition.
|
|
2185
2233
|
|
|
@@ -2196,7 +2244,9 @@ function onBeforeInput(event, editor) {
|
|
|
2196
2244
|
// during composition and see if the previous text contains
|
|
2197
2245
|
// part of the composed text to work out the actual text that
|
|
2198
2246
|
// we need to insert.
|
|
2199
|
-
const composedText = event.data;
|
|
2247
|
+
const composedText = event.data; // TODO: evaluate if this is Android only. It doesn't always seem
|
|
2248
|
+
// to have any real impact, so could probably be refactored or removed
|
|
2249
|
+
// for an alternative approach.
|
|
2200
2250
|
|
|
2201
2251
|
if (composedText) {
|
|
2202
2252
|
updateEditor(editor, () => {
|
|
@@ -2240,20 +2290,32 @@ function onBeforeInput(event, editor) {
|
|
|
2240
2290
|
}
|
|
2241
2291
|
|
|
2242
2292
|
$setSelection(prevSelection.clone());
|
|
2243
|
-
}
|
|
2293
|
+
}
|
|
2244
2294
|
|
|
2295
|
+
if ($isRangeSelection(selection)) {
|
|
2296
|
+
// Used for handling backspace in Android.
|
|
2297
|
+
if (isPossiblyAndroidKeyPress(event.timeStamp) && selection.anchor.key === selection.focus.key) {
|
|
2298
|
+
$setCompositionKey(null);
|
|
2299
|
+
lastKeyDownTimeStamp = 0; // Fixes an Android bug where selection flickers when backspacing
|
|
2245
2300
|
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2301
|
+
setTimeout(() => {
|
|
2302
|
+
updateEditor(editor, () => {
|
|
2303
|
+
$setCompositionKey(null);
|
|
2304
|
+
});
|
|
2305
|
+
}, ANDROID_COMPOSITION_LATENCY);
|
|
2250
2306
|
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2307
|
+
if ($isRangeSelection(selection)) {
|
|
2308
|
+
const anchorNode = selection.anchor.getNode();
|
|
2309
|
+
anchorNode.markDirty();
|
|
2310
|
+
selection.format = anchorNode.getFormat();
|
|
2311
|
+
}
|
|
2312
|
+
} else {
|
|
2313
|
+
event.preventDefault();
|
|
2314
|
+
dispatchCommand(editor, DELETE_CHARACTER_COMMAND, false);
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2257
2319
|
}
|
|
2258
2320
|
|
|
2259
2321
|
if (!$isRangeSelection(selection)) {
|
|
@@ -2449,7 +2511,14 @@ function onInput(event, editor) {
|
|
|
2449
2511
|
isFirefoxEndingComposition = false;
|
|
2450
2512
|
}
|
|
2451
2513
|
|
|
2452
|
-
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
|
+
|
|
2453
2522
|
|
|
2454
2523
|
if (!IS_SAFARI && !IS_IOS && editor.isComposing()) {
|
|
2455
2524
|
lastKeyDownTimeStamp = 0;
|
|
@@ -2459,7 +2528,7 @@ function onInput(event, editor) {
|
|
|
2459
2528
|
$updateSelectedTextFromDOM(editor, false); // onInput always fires after onCompositionEnd for FF.
|
|
2460
2529
|
|
|
2461
2530
|
if (isFirefoxEndingComposition) {
|
|
2462
|
-
onCompositionEndImpl(editor, data);
|
|
2531
|
+
onCompositionEndImpl(editor, data || undefined);
|
|
2463
2532
|
isFirefoxEndingComposition = false;
|
|
2464
2533
|
}
|
|
2465
2534
|
} // Also flush any other mutations that might have occurred
|
|
@@ -2505,7 +2574,7 @@ function onCompositionEndImpl(editor, data) {
|
|
|
2505
2574
|
const node = $getNodeByKey(compositionKey);
|
|
2506
2575
|
const textNode = getDOMTextNode(editor.getElementByKey(compositionKey));
|
|
2507
2576
|
|
|
2508
|
-
if (textNode !== null && $isTextNode(node)) {
|
|
2577
|
+
if (textNode !== null && textNode.nodeValue !== null && $isTextNode(node)) {
|
|
2509
2578
|
$updateTextNodeFromDOMContent(node, textNode.nodeValue, null, null, true);
|
|
2510
2579
|
}
|
|
2511
2580
|
|
|
@@ -2548,6 +2617,7 @@ function onCompositionEnd(event, editor) {
|
|
|
2548
2617
|
|
|
2549
2618
|
function onKeyDown(event, editor) {
|
|
2550
2619
|
lastKeyDownTimeStamp = event.timeStamp;
|
|
2620
|
+
lastKeyCode = event.keyCode;
|
|
2551
2621
|
|
|
2552
2622
|
if (editor.isComposing()) {
|
|
2553
2623
|
return;
|
|
@@ -2668,6 +2738,11 @@ const activeNestedEditorsMap = new Map();
|
|
|
2668
2738
|
|
|
2669
2739
|
function onDocumentSelectionChange(event) {
|
|
2670
2740
|
const selection = getDOMSelection();
|
|
2741
|
+
|
|
2742
|
+
if (!selection) {
|
|
2743
|
+
return;
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2671
2746
|
const nextActiveEditor = getNearestEditorFromDOMNode(selection.anchorNode);
|
|
2672
2747
|
|
|
2673
2748
|
if (nextActiveEditor === null) {
|
|
@@ -2767,7 +2842,7 @@ function removeRootElementEvents(rootElement) {
|
|
|
2767
2842
|
|
|
2768
2843
|
const editor = rootElement.__lexicalEditor;
|
|
2769
2844
|
|
|
2770
|
-
if (editor !== null
|
|
2845
|
+
if (editor !== null && editor !== undefined) {
|
|
2771
2846
|
cleanActiveNestedEditorsMap(editor); // @ts-expect-error: internal field
|
|
2772
2847
|
|
|
2773
2848
|
rootElement.__lexicalEditor = null;
|
|
@@ -2813,7 +2888,6 @@ function markCollapsedSelectionFormat(format, offset, key, timeStamp) {
|
|
|
2813
2888
|
* LICENSE file in the root directory of this source tree.
|
|
2814
2889
|
*
|
|
2815
2890
|
*/
|
|
2816
|
-
|
|
2817
2891
|
class Point {
|
|
2818
2892
|
constructor(key, offset, type) {
|
|
2819
2893
|
this._selection = null;
|
|
@@ -3445,11 +3519,12 @@ class RangeSelection {
|
|
|
3445
3519
|
const lastIndex = selectedNodesLength - 1;
|
|
3446
3520
|
let lastNode = selectedNodes[lastIndex];
|
|
3447
3521
|
|
|
3448
|
-
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)) {
|
|
3449
3523
|
let nextSibling = firstNode.getNextSibling();
|
|
3450
3524
|
|
|
3451
3525
|
if (!$isTextNode(nextSibling) || $isTokenOrInertOrSegmented(nextSibling)) {
|
|
3452
3526
|
nextSibling = $createTextNode();
|
|
3527
|
+
nextSibling.setFormat(format);
|
|
3453
3528
|
|
|
3454
3529
|
if (!firstNodeParent.canInsertTextAfter()) {
|
|
3455
3530
|
firstNodeParent.insertAfter(nextSibling);
|
|
@@ -3465,11 +3540,12 @@ class RangeSelection {
|
|
|
3465
3540
|
this.insertText(text);
|
|
3466
3541
|
return;
|
|
3467
3542
|
}
|
|
3468
|
-
} 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)) {
|
|
3469
3544
|
let prevSibling = firstNode.getPreviousSibling();
|
|
3470
3545
|
|
|
3471
3546
|
if (!$isTextNode(prevSibling) || $isTokenOrInertOrSegmented(prevSibling)) {
|
|
3472
3547
|
prevSibling = $createTextNode();
|
|
3548
|
+
prevSibling.setFormat(format);
|
|
3473
3549
|
|
|
3474
3550
|
if (!firstNodeParent.canInsertTextBefore()) {
|
|
3475
3551
|
firstNodeParent.insertBefore(prevSibling);
|
|
@@ -3487,6 +3563,7 @@ class RangeSelection {
|
|
|
3487
3563
|
}
|
|
3488
3564
|
} else if (firstNode.isSegmented() && startOffset !== firstNodeTextLength) {
|
|
3489
3565
|
const textNode = $createTextNode(firstNode.getTextContent());
|
|
3566
|
+
textNode.setFormat(format);
|
|
3490
3567
|
firstNode.replace(textNode);
|
|
3491
3568
|
firstNode = textNode;
|
|
3492
3569
|
} else if (!this.isCollapsed() && text !== '') {
|
|
@@ -3544,10 +3621,14 @@ class RangeSelection {
|
|
|
3544
3621
|
|
|
3545
3622
|
if (firstNode.getTextContent() === '') {
|
|
3546
3623
|
firstNode.remove();
|
|
3547
|
-
} else if (
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3624
|
+
} else if (this.anchor.type === 'text') {
|
|
3625
|
+
if (firstNode.isComposing()) {
|
|
3626
|
+
// When composing, we need to adjust the anchor offset so that
|
|
3627
|
+
// we correctly replace that right range.
|
|
3628
|
+
this.anchor.offset -= text.length;
|
|
3629
|
+
} else {
|
|
3630
|
+
this.format = firstNodeFormat;
|
|
3631
|
+
}
|
|
3551
3632
|
}
|
|
3552
3633
|
} else {
|
|
3553
3634
|
const markedNodeKeysForKeep = new Set([...firstNode.getParentKeys(), ...lastNode.getParentKeys()]); // We have to get the parent elements before the next section,
|
|
@@ -3683,10 +3764,19 @@ class RangeSelection {
|
|
|
3683
3764
|
formatText(formatType) {
|
|
3684
3765
|
// TODO I wonder if this methods use selection.extract() instead?
|
|
3685
3766
|
const selectedNodes = this.getNodes();
|
|
3686
|
-
const
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
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];
|
|
3690
3780
|
|
|
3691
3781
|
if (this.isCollapsed()) {
|
|
3692
3782
|
this.toggleFormat(formatType); // When changing format, we should stop composition
|
|
@@ -3697,42 +3787,22 @@ class RangeSelection {
|
|
|
3697
3787
|
|
|
3698
3788
|
const anchor = this.anchor;
|
|
3699
3789
|
const focus = this.focus;
|
|
3790
|
+
const anchorOffset = anchor.offset;
|
|
3700
3791
|
const focusOffset = focus.offset;
|
|
3701
|
-
let firstNextFormat =
|
|
3792
|
+
let firstNextFormat = firstNode.getFormatFlags(formatType, null);
|
|
3702
3793
|
let firstNodeTextLength = firstNode.getTextContent().length;
|
|
3703
|
-
|
|
3704
|
-
for (let i = 0; i < selectedNodes.length; i++) {
|
|
3705
|
-
const selectedNode = selectedNodes[i];
|
|
3706
|
-
|
|
3707
|
-
if ($isTextNode(selectedNode)) {
|
|
3708
|
-
firstNextFormat = selectedNode.getFormatFlags(formatType, null);
|
|
3709
|
-
break;
|
|
3710
|
-
}
|
|
3711
|
-
}
|
|
3712
|
-
|
|
3713
|
-
let anchorOffset = anchor.offset;
|
|
3714
|
-
let startOffset;
|
|
3715
|
-
let endOffset;
|
|
3716
3794
|
const isBefore = anchor.isBefore(focus);
|
|
3717
|
-
|
|
3718
|
-
|
|
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
|
|
3719
3797
|
// first node so we don't want to include it in the formatting change.
|
|
3720
3798
|
|
|
3721
|
-
if (startOffset === firstNode.getTextContentSize()) {
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
if ($isTextNode(nextSibling)) {
|
|
3729
|
-
// we basically make the second node the firstNode, changing offsets accordingly
|
|
3730
|
-
anchorOffset = 0;
|
|
3731
|
-
startOffset = 0;
|
|
3732
|
-
firstNode = nextSibling;
|
|
3733
|
-
firstNodeTextLength = nextSibling.getTextContent().length;
|
|
3734
|
-
firstNextFormat = firstNode.getFormatFlags(formatType, null);
|
|
3735
|
-
}
|
|
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);
|
|
3736
3806
|
} // This is the case where we only selected a single node
|
|
3737
3807
|
|
|
3738
3808
|
|
|
@@ -3743,10 +3813,8 @@ class RangeSelection {
|
|
|
3743
3813
|
firstNode.select(startOffset, endOffset);
|
|
3744
3814
|
this.format = firstNextFormat;
|
|
3745
3815
|
return;
|
|
3746
|
-
}
|
|
3816
|
+
} // No actual text is selected, so do nothing.
|
|
3747
3817
|
|
|
3748
|
-
startOffset = anchorOffset > focusOffset ? focusOffset : anchorOffset;
|
|
3749
|
-
endOffset = anchorOffset > focusOffset ? anchorOffset : focusOffset; // No actual text is selected, so do nothing.
|
|
3750
3818
|
|
|
3751
3819
|
if (startOffset === endOffset) {
|
|
3752
3820
|
return;
|
|
@@ -3769,7 +3837,9 @@ class RangeSelection {
|
|
|
3769
3837
|
} // multiple nodes selected.
|
|
3770
3838
|
|
|
3771
3839
|
} else {
|
|
3772
|
-
|
|
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) {
|
|
3773
3843
|
if (startOffset !== 0) {
|
|
3774
3844
|
// the entire first node isn't selected, so split it
|
|
3775
3845
|
[, firstNode] = firstNode.splitText(startOffset);
|
|
@@ -3795,11 +3865,12 @@ class RangeSelection {
|
|
|
3795
3865
|
|
|
3796
3866
|
lastNode.setFormat(lastNextFormat);
|
|
3797
3867
|
}
|
|
3798
|
-
}
|
|
3868
|
+
}
|
|
3799
3869
|
|
|
3870
|
+
this.format = firstNextFormat | lastNextFormat; // deal with all the nodes in between
|
|
3800
3871
|
|
|
3801
|
-
for (let i = 1; i < lastIndex; i++) {
|
|
3802
|
-
const selectedNode =
|
|
3872
|
+
for (let i = firstIndex + 1; i < lastIndex; i++) {
|
|
3873
|
+
const selectedNode = selectedTextNodes[i];
|
|
3803
3874
|
const selectedNodeKey = selectedNode.__key;
|
|
3804
3875
|
|
|
3805
3876
|
if ($isTextNode(selectedNode) && selectedNodeKey !== firstNode.__key && selectedNodeKey !== lastNode.__key && !selectedNode.isToken()) {
|
|
@@ -3871,7 +3942,7 @@ class RangeSelection {
|
|
|
3871
3942
|
siblings.push(...nextSiblings);
|
|
3872
3943
|
const firstNode = nodes[0];
|
|
3873
3944
|
let didReplaceOrMerge = false;
|
|
3874
|
-
let
|
|
3945
|
+
let lastNode = null; // Time to insert the nodes!
|
|
3875
3946
|
|
|
3876
3947
|
for (let i = 0; i < nodes.length; i++) {
|
|
3877
3948
|
const node = nodes[i];
|
|
@@ -3934,18 +4005,17 @@ class RangeSelection {
|
|
|
3934
4005
|
|
|
3935
4006
|
if ($isElementNode(target)) {
|
|
3936
4007
|
for (let s = 0; s < childrenLength; s++) {
|
|
3937
|
-
|
|
3938
|
-
target.append(lastNodeInserted);
|
|
4008
|
+
target.append(children[s]);
|
|
3939
4009
|
}
|
|
3940
4010
|
} else {
|
|
3941
4011
|
for (let s = childrenLength - 1; s >= 0; s--) {
|
|
3942
|
-
|
|
3943
|
-
target.insertAfter(lastNodeInserted);
|
|
4012
|
+
target.insertAfter(children[s]);
|
|
3944
4013
|
}
|
|
3945
4014
|
|
|
3946
4015
|
target = target.getParentOrThrow();
|
|
3947
4016
|
}
|
|
3948
4017
|
|
|
4018
|
+
lastNode = children[childrenLength - 1];
|
|
3949
4019
|
element.remove();
|
|
3950
4020
|
didReplaceOrMerge = true;
|
|
3951
4021
|
|
|
@@ -3973,7 +4043,7 @@ class RangeSelection {
|
|
|
3973
4043
|
didReplaceOrMerge = false;
|
|
3974
4044
|
|
|
3975
4045
|
if ($isElementNode(target) && !target.isInline()) {
|
|
3976
|
-
|
|
4046
|
+
lastNode = node;
|
|
3977
4047
|
|
|
3978
4048
|
if ($isDecoratorNode(node) && node.isTopLevel()) {
|
|
3979
4049
|
target = target.insertAfter(node);
|
|
@@ -4006,8 +4076,8 @@ class RangeSelection {
|
|
|
4006
4076
|
target = target.insertAfter(node);
|
|
4007
4077
|
}
|
|
4008
4078
|
}
|
|
4009
|
-
} else if (!$isElementNode(node) || $isElementNode(node) && node.isInline() || $isDecoratorNode(target) && target.isTopLevel()) {
|
|
4010
|
-
|
|
4079
|
+
} else if (!$isElementNode(node) || $isElementNode(node) && node.isInline() || $isDecoratorNode(target) && target.isTopLevel() || $isLineBreakNode(target)) {
|
|
4080
|
+
lastNode = node;
|
|
4011
4081
|
target = target.insertAfter(node);
|
|
4012
4082
|
} else {
|
|
4013
4083
|
target = node.getParentOrThrow(); // Re-try again with the target being the parent
|
|
@@ -4036,7 +4106,7 @@ class RangeSelection {
|
|
|
4036
4106
|
if ($isElementNode(target)) {
|
|
4037
4107
|
// If the last node to be inserted was a text node,
|
|
4038
4108
|
// then we should attempt to move selection to that.
|
|
4039
|
-
const lastChild = $isTextNode(
|
|
4109
|
+
const lastChild = $isTextNode(lastNode) ? lastNode : target.getLastDescendant();
|
|
4040
4110
|
|
|
4041
4111
|
if (!selectStart) {
|
|
4042
4112
|
// Handle moving selection to end for elements
|
|
@@ -4050,18 +4120,26 @@ class RangeSelection {
|
|
|
4050
4120
|
}
|
|
4051
4121
|
|
|
4052
4122
|
if (siblings.length !== 0) {
|
|
4123
|
+
const originalTarget = target;
|
|
4124
|
+
|
|
4053
4125
|
for (let i = siblings.length - 1; i >= 0; i--) {
|
|
4054
4126
|
const sibling = siblings[i];
|
|
4055
4127
|
const prevParent = sibling.getParentOrThrow();
|
|
4056
4128
|
|
|
4057
|
-
if ($isElementNode(target) && !$
|
|
4058
|
-
target
|
|
4129
|
+
if ($isElementNode(target) && !$isBlockElementNode(sibling)) {
|
|
4130
|
+
if (originalTarget === target) {
|
|
4131
|
+
target.append(sibling);
|
|
4132
|
+
} else {
|
|
4133
|
+
target.insertBefore(sibling);
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4059
4136
|
target = sibling;
|
|
4060
|
-
} else if (!$isElementNode(target) && !$
|
|
4137
|
+
} else if (!$isElementNode(target) && !$isBlockElementNode(sibling)) {
|
|
4061
4138
|
target.insertBefore(sibling);
|
|
4062
4139
|
target = sibling;
|
|
4063
4140
|
} else {
|
|
4064
4141
|
if ($isElementNode(sibling) && !sibling.canInsertAfter(target)) {
|
|
4142
|
+
// @ts-ignore The clone method does exist on the constructor.
|
|
4065
4143
|
const prevParentClone = prevParent.constructor.clone(prevParent);
|
|
4066
4144
|
|
|
4067
4145
|
if (!$isElementNode(prevParentClone)) {
|
|
@@ -4344,13 +4422,18 @@ class RangeSelection {
|
|
|
4344
4422
|
}
|
|
4345
4423
|
}
|
|
4346
4424
|
|
|
4347
|
-
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
|
|
4348
4430
|
// will be. We then use it to update the Lexical selection accordingly. This
|
|
4349
4431
|
// is much more reliable than waiting for a beforeinput and using the ranges
|
|
4350
4432
|
// from getTargetRanges(), and is also better than trying to do it ourselves
|
|
4351
4433
|
// using Intl.Segmenter or other workarounds that struggle with word segments
|
|
4352
4434
|
// and line segments (especially with word wrapping and non-Roman languages).
|
|
4353
4435
|
|
|
4436
|
+
|
|
4354
4437
|
$moveNativeSelection(domSelection, alter, isBackward ? 'backward' : 'forward', granularity); // Guard against no ranges
|
|
4355
4438
|
|
|
4356
4439
|
if (domSelection.rangeCount > 0) {
|
|
@@ -4374,7 +4457,7 @@ class RangeSelection {
|
|
|
4374
4457
|
let anchorNode = anchor.getNode();
|
|
4375
4458
|
|
|
4376
4459
|
if (!isBackward && ( // Delete forward handle case
|
|
4377
|
-
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())) {
|
|
4378
4461
|
const nextSibling = anchorNode.getNextSibling() || anchorNode.getParentOrThrow().getNextSibling();
|
|
4379
4462
|
|
|
4380
4463
|
if ($isElementNode(nextSibling) && !nextSibling.canExtractContents()) {
|
|
@@ -4475,6 +4558,8 @@ function $swapPoints(selection) {
|
|
|
4475
4558
|
}
|
|
4476
4559
|
|
|
4477
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.
|
|
4478
4563
|
domSelection.modify(alter, direction, granularity);
|
|
4479
4564
|
}
|
|
4480
4565
|
|
|
@@ -4734,6 +4819,10 @@ function internalResolveSelectionPoints(anchorDOM, anchorOffset, focusDOM, focus
|
|
|
4734
4819
|
|
|
4735
4820
|
normalizeSelectionPointsForBoundaries(resolvedAnchorPoint, resolvedFocusPoint, lastSelection);
|
|
4736
4821
|
return [resolvedAnchorPoint, resolvedFocusPoint];
|
|
4822
|
+
}
|
|
4823
|
+
|
|
4824
|
+
function $isBlockElementNode(node) {
|
|
4825
|
+
return $isElementNode(node) && !node.isInline();
|
|
4737
4826
|
} // This is used to make a selection when the existing
|
|
4738
4827
|
// selection is null, i.e. forcing selection on the editor
|
|
4739
4828
|
// when it current exists outside the editor.
|
|
@@ -4746,15 +4835,15 @@ function internalMakeRangeSelection(anchorKey, anchorOffset, focusKey, focusOffs
|
|
|
4746
4835
|
editorState._selection = selection;
|
|
4747
4836
|
return selection;
|
|
4748
4837
|
}
|
|
4749
|
-
function $
|
|
4838
|
+
function $createRangeSelection() {
|
|
4750
4839
|
const anchor = $createPoint('root', 0, 'element');
|
|
4751
4840
|
const focus = $createPoint('root', 0, 'element');
|
|
4752
4841
|
return new RangeSelection(anchor, focus, 0);
|
|
4753
4842
|
}
|
|
4754
|
-
function $
|
|
4843
|
+
function $createNodeSelection() {
|
|
4755
4844
|
return new NodeSelection(new Set());
|
|
4756
4845
|
}
|
|
4757
|
-
function $
|
|
4846
|
+
function $createGridSelection() {
|
|
4758
4847
|
const anchor = $createPoint('root', 0, 'element');
|
|
4759
4848
|
const focus = $createPoint('root', 0, 'element');
|
|
4760
4849
|
return new GridSelection('root', anchor, focus);
|
|
@@ -5082,7 +5171,7 @@ function updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
|
|
|
5082
5171
|
if (anchorOffset === nextAnchorOffset && focusOffset === nextFocusOffset && anchorDOMNode === nextAnchorNode && focusDOMNode === nextFocusNode && // Badly interpreted range selection when collapsed - #1482
|
|
5083
5172
|
!(domSelection.type === 'Range' && isCollapsed)) {
|
|
5084
5173
|
// If the root element does not have focus, ensure it has focus
|
|
5085
|
-
if (activeElement === null || !rootElement.contains(activeElement)) {
|
|
5174
|
+
if (rootElement !== null && (activeElement === null || !rootElement.contains(activeElement))) {
|
|
5086
5175
|
rootElement.focus({
|
|
5087
5176
|
preventScroll: true
|
|
5088
5177
|
});
|
|
@@ -5101,7 +5190,7 @@ function updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
|
|
|
5101
5190
|
try {
|
|
5102
5191
|
domSelection.setBaseAndExtent(nextAnchorNode, nextAnchorOffset, nextFocusNode, nextFocusOffset);
|
|
5103
5192
|
|
|
5104
|
-
if (nextSelection.isCollapsed() && rootElement === activeElement) {
|
|
5193
|
+
if (nextSelection.isCollapsed() && rootElement !== null && rootElement === activeElement) {
|
|
5105
5194
|
scrollIntoViewIfNeeded(editor, anchor, rootElement, tags);
|
|
5106
5195
|
}
|
|
5107
5196
|
|
|
@@ -5283,7 +5372,6 @@ function $applyAllTransforms(editorState, editor) {
|
|
|
5283
5372
|
}
|
|
5284
5373
|
|
|
5285
5374
|
function $parseSerializedNode(serializedNode) {
|
|
5286
|
-
// $FlowFixMe: intentional cast to our internal type
|
|
5287
5375
|
const internalSerializedNode = serializedNode;
|
|
5288
5376
|
return $parseSerializedNodeImpl(internalSerializedNode, getActiveEditor()._nodes);
|
|
5289
5377
|
}
|
|
@@ -5298,14 +5386,13 @@ function $parseSerializedNodeImpl(serializedNode, registeredNodes) {
|
|
|
5298
5386
|
}
|
|
5299
5387
|
}
|
|
5300
5388
|
|
|
5301
|
-
const nodeClass = registeredNode.klass;
|
|
5389
|
+
const nodeClass = registeredNode.klass;
|
|
5302
5390
|
|
|
5303
5391
|
if (serializedNode.type !== nodeClass.getType()) {
|
|
5304
5392
|
{
|
|
5305
5393
|
throw Error(`LexicalNode: Node ${nodeClass.name} does not implement .importJSON().`);
|
|
5306
5394
|
}
|
|
5307
|
-
}
|
|
5308
|
-
|
|
5395
|
+
}
|
|
5309
5396
|
|
|
5310
5397
|
const node = nodeClass.importJSON(serializedNode);
|
|
5311
5398
|
const children = serializedNode.children;
|
|
@@ -5348,7 +5435,7 @@ function parseEditorState(serializedEditorState, editor, updateFn) {
|
|
|
5348
5435
|
} // Make the editorState immutable
|
|
5349
5436
|
|
|
5350
5437
|
|
|
5351
|
-
editorState._readOnly = true;
|
|
5438
|
+
editorState._readOnly = true;
|
|
5352
5439
|
|
|
5353
5440
|
{
|
|
5354
5441
|
handleDEVOnlyPendingUpdateGuarantees(editorState);
|
|
@@ -5443,7 +5530,9 @@ function commitPendingUpdates(editor) {
|
|
|
5443
5530
|
mutatedNodes = reconcileRoot(currentEditorState, pendingEditorState, editor, dirtyType, dirtyElements, dirtyLeaves);
|
|
5444
5531
|
} catch (error) {
|
|
5445
5532
|
// Report errors
|
|
5446
|
-
|
|
5533
|
+
if (error instanceof Error) {
|
|
5534
|
+
editor._onError(error);
|
|
5535
|
+
} // Reset editor and restore incoming editor state to the DOM
|
|
5447
5536
|
|
|
5448
5537
|
|
|
5449
5538
|
if (!isAttemptingToRecoverFromReconcilerError) {
|
|
@@ -5472,7 +5561,7 @@ function commitPendingUpdates(editor) {
|
|
|
5472
5561
|
}
|
|
5473
5562
|
}
|
|
5474
5563
|
|
|
5475
|
-
pendingEditorState._readOnly = true;
|
|
5564
|
+
pendingEditorState._readOnly = true;
|
|
5476
5565
|
|
|
5477
5566
|
{
|
|
5478
5567
|
handleDEVOnlyPendingUpdateGuarantees(pendingEditorState);
|
|
@@ -5576,6 +5665,7 @@ function triggerListeners(type, editor, isCurrentlyEnqueuingUpdates, ...payload)
|
|
|
5576
5665
|
const listeners = Array.from(editor._listeners[type]);
|
|
5577
5666
|
|
|
5578
5667
|
for (let i = 0; i < listeners.length; i++) {
|
|
5668
|
+
// @ts-ignore
|
|
5579
5669
|
listeners[i].apply(null, payload);
|
|
5580
5670
|
}
|
|
5581
5671
|
} finally {
|
|
@@ -5620,8 +5710,12 @@ function triggerEnqueuedUpdates(editor) {
|
|
|
5620
5710
|
const queuedUpdates = editor._updates;
|
|
5621
5711
|
|
|
5622
5712
|
if (queuedUpdates.length !== 0) {
|
|
5623
|
-
const
|
|
5624
|
-
|
|
5713
|
+
const queuedUpdate = queuedUpdates.shift();
|
|
5714
|
+
|
|
5715
|
+
if (queuedUpdate) {
|
|
5716
|
+
const [updateFn, options] = queuedUpdate;
|
|
5717
|
+
beginUpdate(editor, updateFn, options);
|
|
5718
|
+
}
|
|
5625
5719
|
}
|
|
5626
5720
|
}
|
|
5627
5721
|
|
|
@@ -5649,28 +5743,32 @@ function processNestedUpdates(editor, initialSkipTransforms) {
|
|
|
5649
5743
|
// empty.
|
|
5650
5744
|
|
|
5651
5745
|
while (queuedUpdates.length !== 0) {
|
|
5652
|
-
const
|
|
5653
|
-
let onUpdate;
|
|
5654
|
-
let tag;
|
|
5746
|
+
const queuedUpdate = queuedUpdates.shift();
|
|
5655
5747
|
|
|
5656
|
-
if (
|
|
5657
|
-
|
|
5658
|
-
|
|
5748
|
+
if (queuedUpdate) {
|
|
5749
|
+
const [nextUpdateFn, options] = queuedUpdate;
|
|
5750
|
+
let onUpdate;
|
|
5751
|
+
let tag;
|
|
5659
5752
|
|
|
5660
|
-
if (options
|
|
5661
|
-
|
|
5662
|
-
|
|
5753
|
+
if (options !== undefined) {
|
|
5754
|
+
onUpdate = options.onUpdate;
|
|
5755
|
+
tag = options.tag;
|
|
5663
5756
|
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5757
|
+
if (options.skipTransforms) {
|
|
5758
|
+
skipTransforms = true;
|
|
5759
|
+
}
|
|
5667
5760
|
|
|
5668
|
-
|
|
5669
|
-
|
|
5761
|
+
if (onUpdate) {
|
|
5762
|
+
editor._deferred.push(onUpdate);
|
|
5763
|
+
}
|
|
5764
|
+
|
|
5765
|
+
if (tag) {
|
|
5766
|
+
editor._updateTags.add(tag);
|
|
5767
|
+
}
|
|
5670
5768
|
}
|
|
5671
|
-
}
|
|
5672
5769
|
|
|
5673
|
-
|
|
5770
|
+
nextUpdateFn();
|
|
5771
|
+
}
|
|
5674
5772
|
}
|
|
5675
5773
|
|
|
5676
5774
|
return skipTransforms;
|
|
@@ -5690,7 +5788,7 @@ function beginUpdate(editor, updateFn, options) {
|
|
|
5690
5788
|
updateTags.add(tag);
|
|
5691
5789
|
}
|
|
5692
5790
|
|
|
5693
|
-
skipTransforms = options.skipTransforms;
|
|
5791
|
+
skipTransforms = options.skipTransforms || false;
|
|
5694
5792
|
}
|
|
5695
5793
|
|
|
5696
5794
|
if (onUpdate) {
|
|
@@ -5762,7 +5860,9 @@ function beginUpdate(editor, updateFn, options) {
|
|
|
5762
5860
|
}
|
|
5763
5861
|
} catch (error) {
|
|
5764
5862
|
// Report errors
|
|
5765
|
-
|
|
5863
|
+
if (error instanceof Error) {
|
|
5864
|
+
editor._onError(error);
|
|
5865
|
+
} // Restore existing editor state to the DOM
|
|
5766
5866
|
|
|
5767
5867
|
|
|
5768
5868
|
editor._pendingEditorState = currentEditorState;
|
|
@@ -5817,7 +5917,13 @@ function internalGetActiveEditor() {
|
|
|
5817
5917
|
return activeEditor;
|
|
5818
5918
|
}
|
|
5819
5919
|
|
|
5820
|
-
|
|
5920
|
+
/**
|
|
5921
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
5922
|
+
*
|
|
5923
|
+
* This source code is licensed under the MIT license found in the
|
|
5924
|
+
* LICENSE file in the root directory of this source tree.
|
|
5925
|
+
*
|
|
5926
|
+
*/
|
|
5821
5927
|
function removeNode(nodeToRemove, restoreSelection, preserveEmptyParent) {
|
|
5822
5928
|
errorOnReadOnly();
|
|
5823
5929
|
const key = nodeToRemove.__key;
|
|
@@ -5906,7 +6012,7 @@ class LexicalNode {
|
|
|
5906
6012
|
// @ts-expect-error
|
|
5907
6013
|
this.__type = this.constructor.getType();
|
|
5908
6014
|
this.__parent = null;
|
|
5909
|
-
$setNodeKey(this, key);
|
|
6015
|
+
$setNodeKey(this, key);
|
|
5910
6016
|
|
|
5911
6017
|
{
|
|
5912
6018
|
if (this.__type !== 'root') {
|
|
@@ -6118,7 +6224,6 @@ class LexicalNode {
|
|
|
6118
6224
|
const b = node.getParents();
|
|
6119
6225
|
|
|
6120
6226
|
if ($isElementNode(this)) {
|
|
6121
|
-
// @ts-expect-error
|
|
6122
6227
|
a.unshift(this);
|
|
6123
6228
|
}
|
|
6124
6229
|
|
|
@@ -6375,52 +6480,11 @@ class LexicalNode {
|
|
|
6375
6480
|
|
|
6376
6481
|
exportDOM(editor) {
|
|
6377
6482
|
const element = this.createDOM(editor._config, editor);
|
|
6378
|
-
const serializedNode = this.exportJSON();
|
|
6379
|
-
element.setAttribute('data-lexical-node-type', this.__type);
|
|
6380
|
-
element.setAttribute('data-lexical-node-json', JSON.stringify(serializedNode));
|
|
6381
|
-
element.setAttribute('data-lexical-editor-key', editor._key);
|
|
6382
6483
|
return {
|
|
6383
6484
|
element
|
|
6384
6485
|
};
|
|
6385
6486
|
}
|
|
6386
6487
|
|
|
6387
|
-
static importDOM() {
|
|
6388
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6389
|
-
const proto = this.prototype.constructor;
|
|
6390
|
-
return {
|
|
6391
|
-
// Catch-all key because we don't know the nodeName of the element returned by exportDOM.
|
|
6392
|
-
'*': domNode => {
|
|
6393
|
-
if (!(domNode instanceof HTMLElement)) return null;
|
|
6394
|
-
const editorKey = domNode.getAttribute('data-lexical-editor-key');
|
|
6395
|
-
const nodeType = domNode.getAttribute('data-lexical-node-type');
|
|
6396
|
-
if (editorKey == null || nodeType == null) return null;
|
|
6397
|
-
const editor = getActiveEditor();
|
|
6398
|
-
|
|
6399
|
-
if (editorKey === editor.getKey() && nodeType === proto.getType()) {
|
|
6400
|
-
try {
|
|
6401
|
-
const json = domNode.getAttribute('data-lexical-node-json');
|
|
6402
|
-
|
|
6403
|
-
if (json != null) {
|
|
6404
|
-
const serializedNode = JSON.parse(json);
|
|
6405
|
-
const node = proto.importJSON(serializedNode);
|
|
6406
|
-
return {
|
|
6407
|
-
conversion: () => ({
|
|
6408
|
-
node
|
|
6409
|
-
}),
|
|
6410
|
-
// Max priority because of the 'data-lexical-node-type' attribute
|
|
6411
|
-
// matching the one on node klass guarantees a match.
|
|
6412
|
-
priority: 4
|
|
6413
|
-
}; // eslint-disable-next-line no-empty
|
|
6414
|
-
} // eslint-disable-next-line no-empty
|
|
6415
|
-
|
|
6416
|
-
} catch {}
|
|
6417
|
-
}
|
|
6418
|
-
|
|
6419
|
-
return null;
|
|
6420
|
-
}
|
|
6421
|
-
};
|
|
6422
|
-
}
|
|
6423
|
-
|
|
6424
6488
|
exportJSON() {
|
|
6425
6489
|
{
|
|
6426
6490
|
throw Error(`exportJSON: base method not extended`);
|
|
@@ -6639,7 +6703,7 @@ class DecoratorNode extends LexicalNode {
|
|
|
6639
6703
|
super(key);
|
|
6640
6704
|
}
|
|
6641
6705
|
|
|
6642
|
-
decorate(editor) {
|
|
6706
|
+
decorate(editor, config) {
|
|
6643
6707
|
{
|
|
6644
6708
|
throw Error(`decorate: base method not extended`);
|
|
6645
6709
|
}
|
|
@@ -6872,8 +6936,12 @@ class ElementNode extends LexicalNode {
|
|
|
6872
6936
|
}
|
|
6873
6937
|
|
|
6874
6938
|
hasFormat(type) {
|
|
6875
|
-
|
|
6876
|
-
|
|
6939
|
+
if (type !== '') {
|
|
6940
|
+
const formatFlag = ELEMENT_TYPE_TO_FORMAT[type];
|
|
6941
|
+
return (this.getFormat() & formatFlag) !== 0;
|
|
6942
|
+
}
|
|
6943
|
+
|
|
6944
|
+
return false;
|
|
6877
6945
|
} // Mutators
|
|
6878
6946
|
|
|
6879
6947
|
|
|
@@ -6958,7 +7026,7 @@ class ElementNode extends LexicalNode {
|
|
|
6958
7026
|
setFormat(type) {
|
|
6959
7027
|
errorOnReadOnly();
|
|
6960
7028
|
const self = this.getWritable();
|
|
6961
|
-
self.__format = ELEMENT_TYPE_TO_FORMAT[type]
|
|
7029
|
+
self.__format = type !== '' ? ELEMENT_TYPE_TO_FORMAT[type] : 0;
|
|
6962
7030
|
return this;
|
|
6963
7031
|
}
|
|
6964
7032
|
|
|
@@ -7108,7 +7176,7 @@ class ElementNode extends LexicalNode {
|
|
|
7108
7176
|
return false;
|
|
7109
7177
|
}
|
|
7110
7178
|
|
|
7111
|
-
excludeFromCopy() {
|
|
7179
|
+
excludeFromCopy(destination) {
|
|
7112
7180
|
return false;
|
|
7113
7181
|
}
|
|
7114
7182
|
|
|
@@ -7551,20 +7619,24 @@ function setTextContent(nextText, dom, node) {
|
|
|
7551
7619
|
dom.textContent = text;
|
|
7552
7620
|
} else {
|
|
7553
7621
|
const nodeValue = firstChild.nodeValue;
|
|
7554
|
-
if (nodeValue !== text) if (isComposing || IS_FIREFOX) {
|
|
7555
|
-
// We also use the diff composed text for general text in FF to avoid
|
|
7556
|
-
// the spellcheck red line from flickering.
|
|
7557
|
-
const [index, remove, insert] = diffComposedText(nodeValue, text);
|
|
7558
7622
|
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7623
|
+
if (nodeValue !== text) {
|
|
7624
|
+
if (isComposing || IS_FIREFOX) {
|
|
7625
|
+
// We also use the diff composed text for general text in FF to avoid
|
|
7626
|
+
// We also use the diff composed text for general text in FF to avoid
|
|
7627
|
+
// the spellcheck red line from flickering.
|
|
7628
|
+
const [index, remove, insert] = diffComposedText(nodeValue, text);
|
|
7563
7629
|
|
|
7630
|
+
if (remove !== 0) {
|
|
7631
|
+
// @ts-expect-error
|
|
7632
|
+
firstChild.deleteData(index, remove);
|
|
7633
|
+
} // @ts-expect-error
|
|
7564
7634
|
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7635
|
+
|
|
7636
|
+
firstChild.insertData(index, insert);
|
|
7637
|
+
} else {
|
|
7638
|
+
firstChild.nodeValue = text;
|
|
7639
|
+
}
|
|
7568
7640
|
}
|
|
7569
7641
|
}
|
|
7570
7642
|
}
|
|
@@ -7817,19 +7889,21 @@ class TextNode extends LexicalNode {
|
|
|
7817
7889
|
|
|
7818
7890
|
selectionTransform(prevSelection, nextSelection) {
|
|
7819
7891
|
return;
|
|
7820
|
-
}
|
|
7892
|
+
} // TODO 0.4 This should just be a `string`.
|
|
7893
|
+
|
|
7821
7894
|
|
|
7822
7895
|
setFormat(format) {
|
|
7823
7896
|
errorOnReadOnly();
|
|
7824
7897
|
const self = this.getWritable();
|
|
7825
|
-
self.__format = format;
|
|
7898
|
+
self.__format = typeof format === 'string' ? TEXT_TYPE_TO_FORMAT[format] : format;
|
|
7826
7899
|
return self;
|
|
7827
|
-
}
|
|
7900
|
+
} // TODO 0.4 This should just be a `string`.
|
|
7901
|
+
|
|
7828
7902
|
|
|
7829
7903
|
setDetail(detail) {
|
|
7830
7904
|
errorOnReadOnly();
|
|
7831
7905
|
const self = this.getWritable();
|
|
7832
|
-
self.__detail = detail;
|
|
7906
|
+
self.__detail = typeof detail === 'string' ? DETAIL_TYPE_TO_DETAIL[detail] : detail;
|
|
7833
7907
|
return self;
|
|
7834
7908
|
}
|
|
7835
7909
|
|
|
@@ -8119,11 +8193,13 @@ function convertSpanElement(domNode) {
|
|
|
8119
8193
|
// domNode is a <span> since we matched it by nodeName
|
|
8120
8194
|
const span = domNode; // Google Docs uses span tags + font-weight for bold text
|
|
8121
8195
|
|
|
8122
|
-
const hasBoldFontWeight = span.style.fontWeight === '700'; // Google Docs uses span tags + text-decoration for strikethrough text
|
|
8196
|
+
const hasBoldFontWeight = span.style.fontWeight === '700'; // Google Docs uses span tags + text-decoration: line-through for strikethrough text
|
|
8123
8197
|
|
|
8124
8198
|
const hasLinethroughTextDecoration = span.style.textDecoration === 'line-through'; // Google Docs uses span tags + font-style for italic text
|
|
8125
8199
|
|
|
8126
|
-
const hasItalicFontStyle = span.style.fontStyle === 'italic';
|
|
8200
|
+
const hasItalicFontStyle = span.style.fontStyle === 'italic'; // Google Docs uses span tags + text-decoration: underline for underline text
|
|
8201
|
+
|
|
8202
|
+
const hasUnderlineTextDecoration = span.style.textDecoration === 'underline';
|
|
8127
8203
|
return {
|
|
8128
8204
|
forChild: lexicalNode => {
|
|
8129
8205
|
if ($isTextNode(lexicalNode) && hasBoldFontWeight) {
|
|
@@ -8138,6 +8214,10 @@ function convertSpanElement(domNode) {
|
|
|
8138
8214
|
lexicalNode.toggleFormat('italic');
|
|
8139
8215
|
}
|
|
8140
8216
|
|
|
8217
|
+
if ($isTextNode(lexicalNode) && hasUnderlineTextDecoration) {
|
|
8218
|
+
lexicalNode.toggleFormat('underline');
|
|
8219
|
+
}
|
|
8220
|
+
|
|
8141
8221
|
return lexicalNode;
|
|
8142
8222
|
},
|
|
8143
8223
|
node: null
|
|
@@ -8162,8 +8242,21 @@ function convertBringAttentionToElement(domNode) {
|
|
|
8162
8242
|
}
|
|
8163
8243
|
|
|
8164
8244
|
function convertTextDOMNode(domNode) {
|
|
8245
|
+
const {
|
|
8246
|
+
parentElement
|
|
8247
|
+
} = domNode;
|
|
8248
|
+
const textContent = domNode.textContent || '';
|
|
8249
|
+
const textContentTrim = textContent.trim();
|
|
8250
|
+
const isPre = parentElement != null && parentElement.tagName.toLowerCase() === 'pre';
|
|
8251
|
+
|
|
8252
|
+
if (!isPre && textContentTrim.length === 0 && textContent.includes('\n')) {
|
|
8253
|
+
return {
|
|
8254
|
+
node: null
|
|
8255
|
+
};
|
|
8256
|
+
}
|
|
8257
|
+
|
|
8165
8258
|
return {
|
|
8166
|
-
node: $createTextNode(
|
|
8259
|
+
node: $createTextNode(textContent)
|
|
8167
8260
|
};
|
|
8168
8261
|
}
|
|
8169
8262
|
|
|
@@ -8249,10 +8342,8 @@ class ParagraphNode extends ElementNode {
|
|
|
8249
8342
|
element
|
|
8250
8343
|
} = super.exportDOM(editor);
|
|
8251
8344
|
|
|
8252
|
-
if (element) {
|
|
8253
|
-
|
|
8254
|
-
element.append(document.createElement('br'));
|
|
8255
|
-
}
|
|
8345
|
+
if (element && this.isEmpty()) {
|
|
8346
|
+
element.append(document.createElement('br'));
|
|
8256
8347
|
}
|
|
8257
8348
|
|
|
8258
8349
|
return {
|
|
@@ -8376,10 +8467,9 @@ function initializeConversionCache(nodes) {
|
|
|
8376
8467
|
const conversionCache = new Map();
|
|
8377
8468
|
const handledConversions = new Set();
|
|
8378
8469
|
nodes.forEach(node => {
|
|
8379
|
-
|
|
8380
|
-
const importDOM = node.klass.importDOM.bind(node.klass); // debugger;
|
|
8470
|
+
const importDOM = node.klass.importDOM != null ? node.klass.importDOM.bind(node.klass) : null;
|
|
8381
8471
|
|
|
8382
|
-
if (handledConversions.has(importDOM)) {
|
|
8472
|
+
if (importDOM == null || handledConversions.has(importDOM)) {
|
|
8383
8473
|
return;
|
|
8384
8474
|
}
|
|
8385
8475
|
|
|
@@ -8423,7 +8513,6 @@ function createEditor(editorConfig) {
|
|
|
8423
8513
|
|
|
8424
8514
|
for (let i = 0; i < nodes.length; i++) {
|
|
8425
8515
|
const klass = nodes[i]; // Ensure custom nodes implement required methods.
|
|
8426
|
-
// @ts-ignore
|
|
8427
8516
|
|
|
8428
8517
|
{
|
|
8429
8518
|
const name = klass.name;
|
|
@@ -8446,7 +8535,7 @@ function createEditor(editorConfig) {
|
|
|
8446
8535
|
if (proto instanceof DecoratorNode) {
|
|
8447
8536
|
// eslint-disable-next-line no-prototype-builtins
|
|
8448
8537
|
if (!proto.hasOwnProperty('decorate')) {
|
|
8449
|
-
console.warn(`${
|
|
8538
|
+
console.warn(`${proto.constructor.name} must implement "decorate" method`);
|
|
8450
8539
|
}
|
|
8451
8540
|
}
|
|
8452
8541
|
|
|
@@ -8460,8 +8549,7 @@ function createEditor(editorConfig) {
|
|
|
8460
8549
|
console.warn(`${name} should implement "exportJSON" method to ensure JSON and default HTML serialization works as expected`);
|
|
8461
8550
|
}
|
|
8462
8551
|
}
|
|
8463
|
-
}
|
|
8464
|
-
|
|
8552
|
+
}
|
|
8465
8553
|
|
|
8466
8554
|
const type = klass.getType();
|
|
8467
8555
|
registeredNodes.set(type, {
|
|
@@ -8475,7 +8563,7 @@ function createEditor(editorConfig) {
|
|
|
8475
8563
|
disableEvents,
|
|
8476
8564
|
namespace,
|
|
8477
8565
|
theme
|
|
8478
|
-
}, onError, initializeConversionCache(registeredNodes), isReadOnly);
|
|
8566
|
+
}, onError ? onError : console.error, initializeConversionCache(registeredNodes), isReadOnly);
|
|
8479
8567
|
|
|
8480
8568
|
if (initialEditorState !== undefined) {
|
|
8481
8569
|
editor._pendingEditorState = initialEditorState;
|
|
@@ -8614,7 +8702,6 @@ class LexicalEditor {
|
|
|
8614
8702
|
}
|
|
8615
8703
|
|
|
8616
8704
|
registerMutationListener(klass, listener) {
|
|
8617
|
-
// @ts-expect-error TODO Replace Class utility type with InstanceType
|
|
8618
8705
|
const registeredNode = this._nodes.get(klass.getType());
|
|
8619
8706
|
|
|
8620
8707
|
if (registeredNode === undefined) {
|
|
@@ -8630,10 +8717,7 @@ class LexicalEditor {
|
|
|
8630
8717
|
};
|
|
8631
8718
|
}
|
|
8632
8719
|
|
|
8633
|
-
registerNodeTransform(
|
|
8634
|
-
// declaration below guarantees these are LexicalNodes.
|
|
8635
|
-
klass, listener) {
|
|
8636
|
-
// @ts-expect-error TODO Replace Class utility type with InstanceType
|
|
8720
|
+
registerNodeTransform(klass, listener) {
|
|
8637
8721
|
const type = klass.getType();
|
|
8638
8722
|
|
|
8639
8723
|
const registeredNode = this._nodes.get(type);
|
|
@@ -8654,8 +8738,7 @@ class LexicalEditor {
|
|
|
8654
8738
|
|
|
8655
8739
|
hasNodes(nodes) {
|
|
8656
8740
|
for (let i = 0; i < nodes.length; i++) {
|
|
8657
|
-
const klass = nodes[i];
|
|
8658
|
-
|
|
8741
|
+
const klass = nodes[i];
|
|
8659
8742
|
const type = klass.getType();
|
|
8660
8743
|
|
|
8661
8744
|
if (!this._nodes.has(type)) {
|
|
@@ -8817,13 +8900,15 @@ class LexicalEditor {
|
|
|
8817
8900
|
}
|
|
8818
8901
|
|
|
8819
8902
|
setReadOnly(readOnly) {
|
|
8820
|
-
this._readOnly
|
|
8821
|
-
|
|
8903
|
+
if (this._readOnly !== readOnly) {
|
|
8904
|
+
this._readOnly = readOnly;
|
|
8905
|
+
triggerListeners('readonly', this, true, readOnly);
|
|
8906
|
+
}
|
|
8822
8907
|
}
|
|
8823
8908
|
|
|
8824
8909
|
toJSON() {
|
|
8825
8910
|
return {
|
|
8826
|
-
editorState: this._editorState
|
|
8911
|
+
editorState: this._editorState.toJSON()
|
|
8827
8912
|
};
|
|
8828
8913
|
}
|
|
8829
8914
|
|
|
@@ -8836,7 +8921,7 @@ class LexicalEditor {
|
|
|
8836
8921
|
* LICENSE file in the root directory of this source tree.
|
|
8837
8922
|
*
|
|
8838
8923
|
*/
|
|
8839
|
-
const VERSION = '0.3.
|
|
8924
|
+
const VERSION = '0.3.6';
|
|
8840
8925
|
|
|
8841
8926
|
/**
|
|
8842
8927
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -8886,11 +8971,11 @@ function $isGridRowNode(node) {
|
|
|
8886
8971
|
return node instanceof GridRowNode;
|
|
8887
8972
|
}
|
|
8888
8973
|
|
|
8889
|
-
exports.$createGridSelection = $
|
|
8974
|
+
exports.$createGridSelection = $createGridSelection;
|
|
8890
8975
|
exports.$createLineBreakNode = $createLineBreakNode;
|
|
8891
|
-
exports.$createNodeSelection = $
|
|
8976
|
+
exports.$createNodeSelection = $createNodeSelection;
|
|
8892
8977
|
exports.$createParagraphNode = $createParagraphNode;
|
|
8893
|
-
exports.$createRangeSelection = $
|
|
8978
|
+
exports.$createRangeSelection = $createRangeSelection;
|
|
8894
8979
|
exports.$createTextNode = $createTextNode;
|
|
8895
8980
|
exports.$getDecoratorNode = $getDecoratorNode;
|
|
8896
8981
|
exports.$getNearestNodeFromDOMNode = $getNearestNodeFromDOMNode;
|