lexical 0.3.6 → 0.3.9
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 +232 -183
- package/Lexical.js.flow +8 -2
- package/Lexical.prod.js +163 -160
- package/LexicalCommands.d.ts +4 -4
- package/LexicalConstants.d.ts +1 -0
- package/LexicalEditor.d.ts +31 -4
- package/LexicalUtils.d.ts +8 -7
- package/LexicalVersion.d.ts +1 -1
- package/README.md +1 -1
- package/index.d.ts +1 -1
- package/package.json +1 -1
package/Lexical.dev.js
CHANGED
|
@@ -77,14 +77,12 @@ var getDOMSelection = getSelection;
|
|
|
77
77
|
*
|
|
78
78
|
*/
|
|
79
79
|
const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
|
|
80
|
-
const documentMode =
|
|
81
|
-
CAN_USE_DOM && 'documentMode' in document ? document.documentMode : null;
|
|
80
|
+
const documentMode = CAN_USE_DOM && 'documentMode' in document ? document.documentMode : null;
|
|
82
81
|
const IS_APPLE = CAN_USE_DOM && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
|
|
83
82
|
const IS_FIREFOX = CAN_USE_DOM && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
|
|
84
83
|
const CAN_USE_BEFORE_INPUT = CAN_USE_DOM && 'InputEvent' in window && !documentMode ? 'getTargetRanges' in new window.InputEvent('input') : false;
|
|
85
84
|
const IS_SAFARI = CAN_USE_DOM && /Version\/[\d.]+.*Safari/.test(navigator.userAgent);
|
|
86
|
-
const IS_IOS = CAN_USE_DOM && /iPad|iPhone|iPod/.test(navigator.userAgent) && //
|
|
87
|
-
!window.MSStream; // Keep these in case we need to use them in the future.
|
|
85
|
+
const IS_IOS = CAN_USE_DOM && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; // Keep these in case we need to use them in the future.
|
|
88
86
|
// export const IS_WINDOWS: boolean = CAN_USE_DOM && /Win/.test(navigator.platform);
|
|
89
87
|
// export const IS_CHROME: boolean = CAN_USE_DOM && /^(?=.*Chrome).*/i.test(navigator.userAgent);
|
|
90
88
|
// export const canUseTextInputEvent: boolean = CAN_USE_DOM && 'TextEvent' in window && !documentMode;
|
|
@@ -115,7 +113,8 @@ const IS_STRIKETHROUGH = 1 << 2;
|
|
|
115
113
|
const IS_UNDERLINE = 1 << 3;
|
|
116
114
|
const IS_CODE = 1 << 4;
|
|
117
115
|
const IS_SUBSCRIPT = 1 << 5;
|
|
118
|
-
const IS_SUPERSCRIPT = 1 << 6;
|
|
116
|
+
const IS_SUPERSCRIPT = 1 << 6;
|
|
117
|
+
const IS_ALL_FORMATTING = IS_BOLD | IS_ITALIC | IS_STRIKETHROUGH | IS_UNDERLINE | IS_CODE | IS_SUBSCRIPT | IS_SUPERSCRIPT; // Text node details
|
|
119
118
|
|
|
120
119
|
const IS_DIRECTIONLESS = 1;
|
|
121
120
|
const IS_UNMERGEABLE = 1 << 1; // Element node formatting
|
|
@@ -125,10 +124,11 @@ const IS_ALIGN_CENTER = 2;
|
|
|
125
124
|
const IS_ALIGN_RIGHT = 3;
|
|
126
125
|
const IS_ALIGN_JUSTIFY = 4; // Reconciliation
|
|
127
126
|
|
|
128
|
-
const NON_BREAKING_SPACE = '\u00A0';
|
|
127
|
+
const NON_BREAKING_SPACE = '\u00A0';
|
|
128
|
+
const ZERO_WIDTH_SPACE = '\u200b'; // For iOS/Safari we use a non breaking space, otherwise the cursor appears
|
|
129
129
|
// overlapping the composed text.
|
|
130
130
|
|
|
131
|
-
const COMPOSITION_SUFFIX = IS_SAFARI || IS_IOS ? NON_BREAKING_SPACE :
|
|
131
|
+
const COMPOSITION_SUFFIX = IS_SAFARI || IS_IOS ? NON_BREAKING_SPACE : ZERO_WIDTH_SPACE;
|
|
132
132
|
const DOUBLE_LINE_BREAK = '\n\n'; // For FF, we need to use a non-breaking space, or it gets composition
|
|
133
133
|
// in a stuck state.
|
|
134
134
|
|
|
@@ -253,7 +253,7 @@ function $flushMutations$1(editor, mutations, observer) {
|
|
|
253
253
|
updateEditor(editor, () => {
|
|
254
254
|
const selection = $getSelection() || getLastSelection(editor);
|
|
255
255
|
const badDOMTargets = new Map();
|
|
256
|
-
const rootElement = editor.getRootElement(); // We use the current
|
|
256
|
+
const rootElement = editor.getRootElement(); // We use the current editor state, as that reflects what is
|
|
257
257
|
// actually "on screen".
|
|
258
258
|
|
|
259
259
|
const currentEditorState = editor._editorState;
|
|
@@ -494,12 +494,16 @@ function $isTokenOrInertOrSegmented(node) {
|
|
|
494
494
|
function $isTokenOrInert(node) {
|
|
495
495
|
return node.isToken() || node.isInert();
|
|
496
496
|
}
|
|
497
|
+
|
|
498
|
+
function isDOMNodeLexicalTextNode(node) {
|
|
499
|
+
return node.nodeType === DOM_TEXT_TYPE;
|
|
500
|
+
}
|
|
501
|
+
|
|
497
502
|
function getDOMTextNode(element) {
|
|
498
503
|
let node = element;
|
|
499
504
|
|
|
500
505
|
while (node != null) {
|
|
501
|
-
if (node
|
|
502
|
-
// @ts-expect-error: this is a Text
|
|
506
|
+
if (isDOMNodeLexicalTextNode(node)) {
|
|
503
507
|
return node;
|
|
504
508
|
}
|
|
505
509
|
|
|
@@ -810,7 +814,7 @@ function getEditorsToPropagate(editor) {
|
|
|
810
814
|
function createUID() {
|
|
811
815
|
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
|
|
812
816
|
}
|
|
813
|
-
function $updateSelectedTextFromDOM(
|
|
817
|
+
function $updateSelectedTextFromDOM(isCompositionEnd, data) {
|
|
814
818
|
// Update the text content with the latest composition text
|
|
815
819
|
const domSelection = getDOMSelection();
|
|
816
820
|
|
|
@@ -881,7 +885,7 @@ function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focu
|
|
|
881
885
|
const prevSelection = $getPreviousSelection();
|
|
882
886
|
|
|
883
887
|
if ($isTokenOrInert(node) || $getCompositionKey() !== null && !isComposing || // Check if character was added at the start, and we need
|
|
884
|
-
// to clear this input from
|
|
888
|
+
// to clear this input from occurring as that action wasn't
|
|
885
889
|
// permitted.
|
|
886
890
|
parent !== null && $isRangeSelection(prevSelection) && !parent.canInsertTextBefore() && prevSelection.anchor.offset === 0) {
|
|
887
891
|
node.markDirty();
|
|
@@ -1071,22 +1075,22 @@ function isArrowDown(keyCode) {
|
|
|
1071
1075
|
return keyCode === 40;
|
|
1072
1076
|
}
|
|
1073
1077
|
|
|
1074
|
-
function isMoveBackward(keyCode, ctrlKey,
|
|
1078
|
+
function isMoveBackward(keyCode, ctrlKey, altKey, metaKey) {
|
|
1075
1079
|
return isArrowLeft(keyCode) && !ctrlKey && !metaKey && !altKey;
|
|
1076
1080
|
}
|
|
1077
1081
|
function isMoveToStart(keyCode, ctrlKey, shiftKey, altKey, metaKey) {
|
|
1078
1082
|
return isArrowLeft(keyCode) && !altKey && !shiftKey && (ctrlKey || metaKey);
|
|
1079
1083
|
}
|
|
1080
|
-
function isMoveForward(keyCode, ctrlKey,
|
|
1084
|
+
function isMoveForward(keyCode, ctrlKey, altKey, metaKey) {
|
|
1081
1085
|
return isArrowRight(keyCode) && !ctrlKey && !metaKey && !altKey;
|
|
1082
1086
|
}
|
|
1083
1087
|
function isMoveToEnd(keyCode, ctrlKey, shiftKey, altKey, metaKey) {
|
|
1084
1088
|
return isArrowRight(keyCode) && !altKey && !shiftKey && (ctrlKey || metaKey);
|
|
1085
1089
|
}
|
|
1086
|
-
function isMoveUp(keyCode, ctrlKey,
|
|
1090
|
+
function isMoveUp(keyCode, ctrlKey, metaKey) {
|
|
1087
1091
|
return isArrowUp(keyCode) && !ctrlKey && !metaKey;
|
|
1088
1092
|
}
|
|
1089
|
-
function isMoveDown(keyCode, ctrlKey,
|
|
1093
|
+
function isMoveDown(keyCode, ctrlKey, metaKey) {
|
|
1090
1094
|
return isArrowDown(keyCode) && !ctrlKey && !metaKey;
|
|
1091
1095
|
}
|
|
1092
1096
|
function isModifier(ctrlKey, shiftKey, altKey, metaKey) {
|
|
@@ -1228,7 +1232,7 @@ function getElementByKeyOrThrow(editor, key) {
|
|
|
1228
1232
|
|
|
1229
1233
|
if (element === undefined) {
|
|
1230
1234
|
{
|
|
1231
|
-
throw Error(`Reconciliation: could not find DOM element for node key
|
|
1235
|
+
throw Error(`Reconciliation: could not find DOM element for node key ${key}`);
|
|
1232
1236
|
}
|
|
1233
1237
|
}
|
|
1234
1238
|
|
|
@@ -1268,6 +1272,50 @@ function scrollIntoViewIfNeeded(editor, anchor, rootElement, tags) {
|
|
|
1268
1272
|
tags.add('scroll-into-view');
|
|
1269
1273
|
}
|
|
1270
1274
|
}
|
|
1275
|
+
function $maybeMoveChildrenSelectionToParent(parentNode, offset = 0) {
|
|
1276
|
+
if (offset !== 0) {
|
|
1277
|
+
{
|
|
1278
|
+
throw Error(`TODO`);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
const selection = $getSelection();
|
|
1283
|
+
|
|
1284
|
+
if (!$isRangeSelection(selection) || !$isElementNode(parentNode)) {
|
|
1285
|
+
return selection;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
const {
|
|
1289
|
+
anchor,
|
|
1290
|
+
focus
|
|
1291
|
+
} = selection;
|
|
1292
|
+
const anchorNode = anchor.getNode();
|
|
1293
|
+
const focusNode = focus.getNode();
|
|
1294
|
+
|
|
1295
|
+
if ($hasAncestor(anchorNode, parentNode)) {
|
|
1296
|
+
anchor.set(parentNode.__key, 0, 'element');
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
if ($hasAncestor(focusNode, parentNode)) {
|
|
1300
|
+
focus.set(parentNode.__key, 0, 'element');
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
return selection;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
function $hasAncestor(child, targetNode) {
|
|
1307
|
+
let parent = child.getParent();
|
|
1308
|
+
|
|
1309
|
+
while (parent !== null) {
|
|
1310
|
+
if (parent.is(targetNode)) {
|
|
1311
|
+
return true;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
parent = parent.getParent();
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
return false;
|
|
1318
|
+
}
|
|
1271
1319
|
|
|
1272
1320
|
/**
|
|
1273
1321
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -1633,7 +1681,7 @@ function isLastChildLineBreakOrDecorator(children, nodeMap) {
|
|
|
1633
1681
|
const childKey = children[children.length - 1];
|
|
1634
1682
|
const node = nodeMap.get(childKey);
|
|
1635
1683
|
return $isLineBreakNode(node) || $isDecoratorNode(node);
|
|
1636
|
-
} // If we end an element with a LinkBreakNode, then we need to add an
|
|
1684
|
+
} // If we end an element with a LinkBreakNode, then we need to add an additional <br>
|
|
1637
1685
|
|
|
1638
1686
|
|
|
1639
1687
|
function reconcileElementTerminatingLineBreak(prevChildren, nextChildren, dom) {
|
|
@@ -2055,7 +2103,7 @@ function getPrevElementByKeyOrThrow(key) {
|
|
|
2055
2103
|
|
|
2056
2104
|
if (element === undefined) {
|
|
2057
2105
|
{
|
|
2058
|
-
throw Error(`Reconciliation: could not find DOM element for node key
|
|
2106
|
+
throw Error(`Reconciliation: could not find DOM element for node key ${key}`);
|
|
2059
2107
|
}
|
|
2060
2108
|
}
|
|
2061
2109
|
|
|
@@ -2071,7 +2119,7 @@ function getPrevElementByKeyOrThrow(key) {
|
|
|
2071
2119
|
*/
|
|
2072
2120
|
const PASS_THROUGH_COMMAND = Object.freeze({});
|
|
2073
2121
|
const ANDROID_COMPOSITION_LATENCY = 30;
|
|
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]];
|
|
2122
|
+
const rootElementEvents = [['keydown', onKeyDown], ['compositionstart', onCompositionStart], ['compositionend', onCompositionEnd], ['input', onInput], ['click', onClick], ['cut', PASS_THROUGH_COMMAND], ['copy', PASS_THROUGH_COMMAND], ['dragstart', PASS_THROUGH_COMMAND], ['dragover', PASS_THROUGH_COMMAND], ['dragend', PASS_THROUGH_COMMAND], ['paste', PASS_THROUGH_COMMAND], ['focus', PASS_THROUGH_COMMAND], ['blur', PASS_THROUGH_COMMAND], ['drop', PASS_THROUGH_COMMAND]];
|
|
2075
2123
|
|
|
2076
2124
|
if (CAN_USE_BEFORE_INPUT) {
|
|
2077
2125
|
rootElementEvents.push(['beforeinput', (event, editor) => onBeforeInput(event, editor)]);
|
|
@@ -2153,19 +2201,25 @@ function onSelectionChange(domSelection, editor, isActive) {
|
|
|
2153
2201
|
}
|
|
2154
2202
|
}
|
|
2155
2203
|
} else {
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2204
|
+
let combinedFormat = IS_ALL_FORMATTING;
|
|
2205
|
+
let hasTextNodes = false;
|
|
2206
|
+
const nodes = selection.getNodes();
|
|
2207
|
+
const nodesLength = nodes.length;
|
|
2159
2208
|
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
}
|
|
2209
|
+
for (let i = 0; i < nodesLength; i++) {
|
|
2210
|
+
const node = nodes[i];
|
|
2163
2211
|
|
|
2164
|
-
|
|
2165
|
-
|
|
2212
|
+
if ($isTextNode(node)) {
|
|
2213
|
+
hasTextNodes = true;
|
|
2214
|
+
combinedFormat &= node.getFormat();
|
|
2215
|
+
|
|
2216
|
+
if (combinedFormat === 0) {
|
|
2217
|
+
break;
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2166
2220
|
}
|
|
2167
2221
|
|
|
2168
|
-
selection.format = combinedFormat;
|
|
2222
|
+
selection.format = hasTextNodes ? combinedFormat : 0;
|
|
2169
2223
|
}
|
|
2170
2224
|
}
|
|
2171
2225
|
|
|
@@ -2192,7 +2246,7 @@ function onClick(event, editor) {
|
|
|
2192
2246
|
domSelection.removeAllRanges();
|
|
2193
2247
|
selection.dirty = true;
|
|
2194
2248
|
}
|
|
2195
|
-
} else if (domSelection && $isNodeSelection(selection)
|
|
2249
|
+
} else if (domSelection && $isNodeSelection(selection)) {
|
|
2196
2250
|
const domAnchor = domSelection.anchorNode; // If the user is attempting to click selection back onto text, then
|
|
2197
2251
|
// we should attempt create a range selection.
|
|
2198
2252
|
// When we click on an empty paragraph node or the end of a paragraph that ends
|
|
@@ -2239,41 +2293,6 @@ function onBeforeInput(event, editor) {
|
|
|
2239
2293
|
IS_FIREFOX && isFirefoxClipboardEvents()) {
|
|
2240
2294
|
return;
|
|
2241
2295
|
} else if (inputType === 'insertCompositionText') {
|
|
2242
|
-
// This logic handles insertion of text between different
|
|
2243
|
-
// format text types. We have to detect a change in type
|
|
2244
|
-
// during composition and see if the previous text contains
|
|
2245
|
-
// part of the composed text to work out the actual text that
|
|
2246
|
-
// we need to insert.
|
|
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.
|
|
2250
|
-
|
|
2251
|
-
if (composedText) {
|
|
2252
|
-
updateEditor(editor, () => {
|
|
2253
|
-
const selection = $getSelection();
|
|
2254
|
-
|
|
2255
|
-
if ($isRangeSelection(selection)) {
|
|
2256
|
-
const anchor = selection.anchor;
|
|
2257
|
-
const node = anchor.getNode();
|
|
2258
|
-
const prevNode = node.getPreviousSibling();
|
|
2259
|
-
|
|
2260
|
-
if (anchor.offset === 0 && $isTextNode(node) && $isTextNode(prevNode) && node.getTextContent() === COMPOSITION_START_CHAR && prevNode.getFormat() !== selection.format) {
|
|
2261
|
-
const prevTextContent = prevNode.getTextContent();
|
|
2262
|
-
|
|
2263
|
-
if (composedText.indexOf(prevTextContent) === 0) {
|
|
2264
|
-
const insertedText = composedText.slice(prevTextContent.length);
|
|
2265
|
-
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, insertedText);
|
|
2266
|
-
setTimeout(() => {
|
|
2267
|
-
updateEditor(editor, () => {
|
|
2268
|
-
node.select();
|
|
2269
|
-
});
|
|
2270
|
-
}, ANDROID_COMPOSITION_LATENCY);
|
|
2271
|
-
}
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
});
|
|
2275
|
-
}
|
|
2276
|
-
|
|
2277
2296
|
return;
|
|
2278
2297
|
}
|
|
2279
2298
|
|
|
@@ -2333,10 +2352,10 @@ function onBeforeInput(event, editor) {
|
|
|
2333
2352
|
const anchorNode = anchor.getNode();
|
|
2334
2353
|
const focusNode = focus.getNode();
|
|
2335
2354
|
|
|
2336
|
-
if (inputType === 'insertText') {
|
|
2355
|
+
if (inputType === 'insertText' || inputType === 'insertTranspose') {
|
|
2337
2356
|
if (data === '\n') {
|
|
2338
2357
|
event.preventDefault();
|
|
2339
|
-
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND,
|
|
2358
|
+
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
|
|
2340
2359
|
} else if (data === DOUBLE_LINE_BREAK) {
|
|
2341
2360
|
event.preventDefault();
|
|
2342
2361
|
dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND, undefined);
|
|
@@ -2379,7 +2398,7 @@ function onBeforeInput(event, editor) {
|
|
|
2379
2398
|
{
|
|
2380
2399
|
// Used for Android
|
|
2381
2400
|
$setCompositionKey(null);
|
|
2382
|
-
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND,
|
|
2401
|
+
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
|
|
2383
2402
|
break;
|
|
2384
2403
|
}
|
|
2385
2404
|
|
|
@@ -2391,7 +2410,7 @@ function onBeforeInput(event, editor) {
|
|
|
2391
2410
|
|
|
2392
2411
|
if (isInsertLineBreak) {
|
|
2393
2412
|
isInsertLineBreak = false;
|
|
2394
|
-
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND,
|
|
2413
|
+
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
|
|
2395
2414
|
} else {
|
|
2396
2415
|
dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND, undefined);
|
|
2397
2416
|
}
|
|
@@ -2525,7 +2544,7 @@ function onInput(event, editor) {
|
|
|
2525
2544
|
$setCompositionKey(null);
|
|
2526
2545
|
}
|
|
2527
2546
|
} else {
|
|
2528
|
-
$updateSelectedTextFromDOM(
|
|
2547
|
+
$updateSelectedTextFromDOM(false); // onInput always fires after onCompositionEnd for FF.
|
|
2529
2548
|
|
|
2530
2549
|
if (isFirefoxEndingComposition) {
|
|
2531
2550
|
onCompositionEndImpl(editor, data || undefined);
|
|
@@ -2597,7 +2616,7 @@ function onCompositionEndImpl(editor, data) {
|
|
|
2597
2616
|
}
|
|
2598
2617
|
}
|
|
2599
2618
|
|
|
2600
|
-
$updateSelectedTextFromDOM(
|
|
2619
|
+
$updateSelectedTextFromDOM(true, data);
|
|
2601
2620
|
}
|
|
2602
2621
|
|
|
2603
2622
|
function onCompositionEnd(event, editor) {
|
|
@@ -2631,17 +2650,17 @@ function onKeyDown(event, editor) {
|
|
|
2631
2650
|
altKey
|
|
2632
2651
|
} = event;
|
|
2633
2652
|
|
|
2634
|
-
if (isMoveForward(keyCode, ctrlKey,
|
|
2653
|
+
if (isMoveForward(keyCode, ctrlKey, altKey, metaKey)) {
|
|
2635
2654
|
dispatchCommand(editor, KEY_ARROW_RIGHT_COMMAND, event);
|
|
2636
2655
|
} else if (isMoveToEnd(keyCode, ctrlKey, shiftKey, altKey, metaKey)) {
|
|
2637
2656
|
dispatchCommand(editor, MOVE_TO_END, event);
|
|
2638
|
-
} else if (isMoveBackward(keyCode, ctrlKey,
|
|
2657
|
+
} else if (isMoveBackward(keyCode, ctrlKey, altKey, metaKey)) {
|
|
2639
2658
|
dispatchCommand(editor, KEY_ARROW_LEFT_COMMAND, event);
|
|
2640
2659
|
} else if (isMoveToStart(keyCode, ctrlKey, shiftKey, altKey, metaKey)) {
|
|
2641
2660
|
dispatchCommand(editor, MOVE_TO_START, event);
|
|
2642
|
-
} else if (isMoveUp(keyCode, ctrlKey,
|
|
2661
|
+
} else if (isMoveUp(keyCode, ctrlKey, metaKey)) {
|
|
2643
2662
|
dispatchCommand(editor, KEY_ARROW_UP_COMMAND, event);
|
|
2644
|
-
} else if (isMoveDown(keyCode, ctrlKey,
|
|
2663
|
+
} else if (isMoveDown(keyCode, ctrlKey, metaKey)) {
|
|
2645
2664
|
dispatchCommand(editor, KEY_ARROW_DOWN_COMMAND, event);
|
|
2646
2665
|
} else if (isLineBreak(keyCode, shiftKey)) {
|
|
2647
2666
|
isInsertLineBreak = true;
|
|
@@ -3762,7 +3781,13 @@ class RangeSelection {
|
|
|
3762
3781
|
}
|
|
3763
3782
|
|
|
3764
3783
|
formatText(formatType) {
|
|
3765
|
-
|
|
3784
|
+
if (this.isCollapsed()) {
|
|
3785
|
+
this.toggleFormat(formatType); // When changing format, we should stop composition
|
|
3786
|
+
|
|
3787
|
+
$setCompositionKey(null);
|
|
3788
|
+
return;
|
|
3789
|
+
}
|
|
3790
|
+
|
|
3766
3791
|
const selectedNodes = this.getNodes();
|
|
3767
3792
|
const selectedTextNodes = [];
|
|
3768
3793
|
|
|
@@ -3773,12 +3798,8 @@ class RangeSelection {
|
|
|
3773
3798
|
}
|
|
3774
3799
|
|
|
3775
3800
|
const selectedTextNodesLength = selectedTextNodes.length;
|
|
3776
|
-
let firstIndex = 0;
|
|
3777
|
-
const lastIndex = selectedTextNodesLength - 1;
|
|
3778
|
-
let firstNode = selectedTextNodes[0];
|
|
3779
|
-
let lastNode = selectedTextNodes[lastIndex];
|
|
3780
3801
|
|
|
3781
|
-
if (
|
|
3802
|
+
if (selectedTextNodesLength === 0) {
|
|
3782
3803
|
this.toggleFormat(formatType); // When changing format, we should stop composition
|
|
3783
3804
|
|
|
3784
3805
|
$setCompositionKey(null);
|
|
@@ -3787,98 +3808,96 @@ class RangeSelection {
|
|
|
3787
3808
|
|
|
3788
3809
|
const anchor = this.anchor;
|
|
3789
3810
|
const focus = this.focus;
|
|
3790
|
-
const
|
|
3791
|
-
const
|
|
3792
|
-
|
|
3793
|
-
let
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
let startOffset = isBefore ? anchorOffset : focusOffset; // This is the case where the user only selected the very end of the
|
|
3797
|
-
// first node so we don't want to include it in the formatting change.
|
|
3811
|
+
const isBackward = this.isBackward();
|
|
3812
|
+
const startPoint = isBackward ? focus : anchor;
|
|
3813
|
+
const endPoint = isBackward ? anchor : focus;
|
|
3814
|
+
let firstIndex = 0;
|
|
3815
|
+
let firstNode = selectedTextNodes[0];
|
|
3816
|
+
let startOffset = startPoint.type === 'element' ? 0 : startPoint.offset; // In case selection started at the end of text node use next text node
|
|
3798
3817
|
|
|
3799
|
-
if (startOffset === firstNode.getTextContentSize()
|
|
3800
|
-
const nextNode = selectedTextNodes[1];
|
|
3801
|
-
startOffset = 0;
|
|
3818
|
+
if (startPoint.type === 'text' && startOffset === firstNode.getTextContentSize()) {
|
|
3802
3819
|
firstIndex = 1;
|
|
3803
|
-
firstNode =
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
} // This is the case where we only selected a single node
|
|
3820
|
+
firstNode = selectedTextNodes[1];
|
|
3821
|
+
startOffset = 0;
|
|
3822
|
+
}
|
|
3807
3823
|
|
|
3824
|
+
if (firstNode == null) {
|
|
3825
|
+
return;
|
|
3826
|
+
}
|
|
3827
|
+
|
|
3828
|
+
const firstNextFormat = firstNode.getFormatFlags(formatType, null);
|
|
3829
|
+
const lastIndex = selectedTextNodesLength - 1;
|
|
3830
|
+
let lastNode = selectedTextNodes[lastIndex];
|
|
3831
|
+
const endOffset = endPoint.type === 'text' ? endPoint.offset : lastNode.getTextContentSize(); // Single node selected
|
|
3808
3832
|
|
|
3809
3833
|
if (firstNode.is(lastNode)) {
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
this.format = firstNextFormat;
|
|
3815
|
-
return;
|
|
3816
|
-
} // No actual text is selected, so do nothing.
|
|
3834
|
+
// No actual text is selected, so do nothing.
|
|
3835
|
+
if (startOffset === endOffset) {
|
|
3836
|
+
return;
|
|
3837
|
+
} // The entire node is selected, so just format it
|
|
3817
3838
|
|
|
3818
3839
|
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3840
|
+
if (startOffset === 0 && endOffset === firstNode.getTextContentSize()) {
|
|
3841
|
+
firstNode.setFormat(firstNextFormat);
|
|
3842
|
+
} else {
|
|
3843
|
+
// Node is partially selected, so split it into two nodes
|
|
3844
|
+
// add style the selected one.
|
|
3845
|
+
const splitNodes = firstNode.splitText(startOffset, endOffset);
|
|
3846
|
+
const replacement = startOffset === 0 ? splitNodes[0] : splitNodes[1];
|
|
3847
|
+
replacement.setFormat(firstNextFormat); // Update selection only if starts/ends on text node
|
|
3822
3848
|
|
|
3849
|
+
if (startPoint.type === 'text') {
|
|
3850
|
+
startPoint.set(replacement.__key, 0, 'text');
|
|
3851
|
+
}
|
|
3823
3852
|
|
|
3824
|
-
if (
|
|
3825
|
-
|
|
3826
|
-
firstNode.select(startOffset, endOffset);
|
|
3827
|
-
} else {
|
|
3828
|
-
// ndoe is partially selected, so split it into two nodes
|
|
3829
|
-
// adnd style the selected one.
|
|
3830
|
-
const splitNodes = firstNode.splitText(startOffset, endOffset);
|
|
3831
|
-
const replacement = startOffset === 0 ? splitNodes[0] : splitNodes[1];
|
|
3832
|
-
replacement.setFormat(firstNextFormat);
|
|
3833
|
-
replacement.select(0, endOffset - startOffset);
|
|
3853
|
+
if (endPoint.type === 'text') {
|
|
3854
|
+
endPoint.set(replacement.__key, endOffset - startOffset, 'text');
|
|
3834
3855
|
}
|
|
3856
|
+
}
|
|
3835
3857
|
|
|
3836
|
-
|
|
3837
|
-
|
|
3858
|
+
this.format = firstNextFormat;
|
|
3859
|
+
return;
|
|
3860
|
+
} // Multiple nodes selected
|
|
3861
|
+
// The entire first node isn't selected, so split it
|
|
3838
3862
|
|
|
3839
|
-
} else {
|
|
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) {
|
|
3843
|
-
if (startOffset !== 0) {
|
|
3844
|
-
// the entire first node isn't selected, so split it
|
|
3845
|
-
[, firstNode] = firstNode.splitText(startOffset);
|
|
3846
|
-
startOffset = 0;
|
|
3847
|
-
}
|
|
3848
3863
|
|
|
3849
|
-
|
|
3864
|
+
if (startOffset !== 0) {
|
|
3865
|
+
[, firstNode] = firstNode.splitText(startOffset);
|
|
3866
|
+
startOffset = 0;
|
|
3867
|
+
}
|
|
3868
|
+
|
|
3869
|
+
firstNode.setFormat(firstNextFormat);
|
|
3870
|
+
const lastNextFormat = lastNode.getFormatFlags(formatType, firstNextFormat); // If the offset is 0, it means no actual characters are selected,
|
|
3871
|
+
// so we skip formatting the last node altogether.
|
|
3872
|
+
|
|
3873
|
+
if (endOffset > 0) {
|
|
3874
|
+
if (endOffset !== lastNode.getTextContentSize()) {
|
|
3875
|
+
[lastNode] = lastNode.splitText(endOffset);
|
|
3850
3876
|
}
|
|
3851
3877
|
|
|
3852
|
-
|
|
3878
|
+
lastNode.setFormat(lastNextFormat);
|
|
3879
|
+
} // Process all text nodes in between
|
|
3853
3880
|
|
|
3854
|
-
if ($isTextNode(lastNode)) {
|
|
3855
|
-
lastNextFormat = lastNode.getFormatFlags(formatType, firstNextFormat);
|
|
3856
|
-
const lastNodeText = lastNode.getTextContent();
|
|
3857
|
-
const lastNodeTextLength = lastNodeText.length; // if the offset is 0, it means no actual characters are selected,
|
|
3858
|
-
// so we skip formatting the last node altogether.
|
|
3859
3881
|
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
if (endOffset !== lastNodeTextLength) {
|
|
3863
|
-
[lastNode] = lastNode.splitText(endOffset);
|
|
3864
|
-
}
|
|
3882
|
+
for (let i = firstIndex + 1; i < lastIndex; i++) {
|
|
3883
|
+
const textNode = selectedTextNodes[i];
|
|
3865
3884
|
|
|
3866
|
-
|
|
3867
|
-
|
|
3885
|
+
if (!textNode.isToken()) {
|
|
3886
|
+
const nextFormat = textNode.getFormatFlags(formatType, lastNextFormat);
|
|
3887
|
+
textNode.setFormat(nextFormat);
|
|
3868
3888
|
}
|
|
3889
|
+
} // Update selection only if starts/ends on text node
|
|
3869
3890
|
|
|
3870
|
-
this.format = firstNextFormat | lastNextFormat; // deal with all the nodes in between
|
|
3871
3891
|
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3892
|
+
if (startPoint.type === 'text') {
|
|
3893
|
+
startPoint.set(firstNode.__key, startOffset, 'text');
|
|
3894
|
+
}
|
|
3875
3895
|
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
selectedNode.setFormat(selectedNextFormat);
|
|
3879
|
-
}
|
|
3880
|
-
}
|
|
3896
|
+
if (endPoint.type === 'text') {
|
|
3897
|
+
endPoint.set(lastNode.__key, endOffset, 'text');
|
|
3881
3898
|
}
|
|
3899
|
+
|
|
3900
|
+
this.format = firstNextFormat | lastNextFormat;
|
|
3882
3901
|
}
|
|
3883
3902
|
|
|
3884
3903
|
insertNodes(nodes, selectStart) {
|
|
@@ -3949,7 +3968,7 @@ class RangeSelection {
|
|
|
3949
3968
|
|
|
3950
3969
|
if ($isElementNode(node) && !node.isInline()) {
|
|
3951
3970
|
// -----
|
|
3952
|
-
// Heuristics for the
|
|
3971
|
+
// Heuristics for the replacement or merging of elements
|
|
3953
3972
|
// -----
|
|
3954
3973
|
// If we have an incoming element node as the first node, then we'll need
|
|
3955
3974
|
// see if we can merge any descendant leaf nodes into our existing target.
|
|
@@ -4126,7 +4145,7 @@ class RangeSelection {
|
|
|
4126
4145
|
const sibling = siblings[i];
|
|
4127
4146
|
const prevParent = sibling.getParentOrThrow();
|
|
4128
4147
|
|
|
4129
|
-
if ($isElementNode(target) && !$isBlockElementNode(sibling)) {
|
|
4148
|
+
if ($isElementNode(target) && !$isBlockElementNode(sibling) && !($isDecoratorNode(sibling) && sibling.isTopLevel())) {
|
|
4130
4149
|
if (originalTarget === target) {
|
|
4131
4150
|
target.append(sibling);
|
|
4132
4151
|
} else {
|
|
@@ -4335,7 +4354,7 @@ class RangeSelection {
|
|
|
4335
4354
|
if (selectedNodesLength === 0) {
|
|
4336
4355
|
return [];
|
|
4337
4356
|
} else if (selectedNodesLength === 1) {
|
|
4338
|
-
if ($isTextNode(firstNode)) {
|
|
4357
|
+
if ($isTextNode(firstNode) && !this.isCollapsed()) {
|
|
4339
4358
|
const startOffset = anchorOffset > focusOffset ? focusOffset : anchorOffset;
|
|
4340
4359
|
const endOffset = anchorOffset > focusOffset ? anchorOffset : focusOffset;
|
|
4341
4360
|
const splitNodes = firstNode.splitText(startOffset, endOffset);
|
|
@@ -5293,7 +5312,7 @@ function $normalizeAllDirtyTextNodes(editorState, editor) {
|
|
|
5293
5312
|
* 2. We transform elements. If element transforms generate additional dirty nodes we repeat step 1.
|
|
5294
5313
|
* If element transforms only generate additional dirty elements we only repeat step 2.
|
|
5295
5314
|
*
|
|
5296
|
-
* Note that to keep track of newly dirty nodes and
|
|
5315
|
+
* Note that to keep track of newly dirty nodes and subtrees we leverage the editor._dirtyNodes and
|
|
5297
5316
|
* editor._subtrees which we reset in every loop.
|
|
5298
5317
|
*/
|
|
5299
5318
|
|
|
@@ -5498,7 +5517,7 @@ function commitPendingUpdates(editor) {
|
|
|
5498
5517
|
if (rootElement === null && !headless || pendingEditorState === null) {
|
|
5499
5518
|
return;
|
|
5500
5519
|
} // ======
|
|
5501
|
-
//
|
|
5520
|
+
// Reconciliation has started.
|
|
5502
5521
|
// ======
|
|
5503
5522
|
|
|
5504
5523
|
|
|
@@ -5518,7 +5537,7 @@ function commitPendingUpdates(editor) {
|
|
|
5518
5537
|
if (!headless && needsUpdate && observer !== null) {
|
|
5519
5538
|
activeEditor = editor;
|
|
5520
5539
|
activeEditorState = pendingEditorState;
|
|
5521
|
-
isReadOnlyMode = false; // We don't want updates to sync block the
|
|
5540
|
+
isReadOnlyMode = false; // We don't want updates to sync block the reconciliation.
|
|
5522
5541
|
|
|
5523
5542
|
editor._updating = true;
|
|
5524
5543
|
|
|
@@ -5593,7 +5612,7 @@ function commitPendingUpdates(editor) {
|
|
|
5593
5612
|
}
|
|
5594
5613
|
|
|
5595
5614
|
$garbageCollectDetachedDecorators(editor, pendingEditorState); // ======
|
|
5596
|
-
//
|
|
5615
|
+
// Reconciliation has finished. Now update selection and trigger listeners.
|
|
5597
5616
|
// ======
|
|
5598
5617
|
|
|
5599
5618
|
const domSelection = headless ? null : getDOMSelection(); // Attempt to update the DOM selection, including focusing of the root element,
|
|
@@ -5613,7 +5632,7 @@ function commitPendingUpdates(editor) {
|
|
|
5613
5632
|
}
|
|
5614
5633
|
|
|
5615
5634
|
if (mutatedNodes !== null) {
|
|
5616
|
-
triggerMutationListeners(editor, currentEditorState, pendingEditorState, mutatedNodes);
|
|
5635
|
+
triggerMutationListeners(editor, currentEditorState, pendingEditorState, mutatedNodes, tags, dirtyLeaves);
|
|
5617
5636
|
}
|
|
5618
5637
|
|
|
5619
5638
|
if (pendingDecorators !== null) {
|
|
@@ -5644,17 +5663,21 @@ function triggerTextContentListeners(editor, currentEditorState, pendingEditorSt
|
|
|
5644
5663
|
}
|
|
5645
5664
|
}
|
|
5646
5665
|
|
|
5647
|
-
function triggerMutationListeners(editor, currentEditorState, pendingEditorState, mutatedNodes) {
|
|
5648
|
-
const listeners = editor._listeners.mutation;
|
|
5649
|
-
|
|
5666
|
+
function triggerMutationListeners(editor, currentEditorState, pendingEditorState, mutatedNodes, updateTags, dirtyLeaves) {
|
|
5667
|
+
const listeners = Array.from(editor._listeners.mutation);
|
|
5668
|
+
const listenersLength = listeners.length;
|
|
5669
|
+
|
|
5670
|
+
for (let i = 0; i < listenersLength; i++) {
|
|
5671
|
+
const [listener, klass] = listeners[i];
|
|
5650
5672
|
const mutatedNodesByType = mutatedNodes.get(klass);
|
|
5651
5673
|
|
|
5652
|
-
if (mutatedNodesByType
|
|
5653
|
-
|
|
5674
|
+
if (mutatedNodesByType !== undefined) {
|
|
5675
|
+
listener(mutatedNodesByType, {
|
|
5676
|
+
dirtyLeaves,
|
|
5677
|
+
updateTags
|
|
5678
|
+
});
|
|
5654
5679
|
}
|
|
5655
|
-
|
|
5656
|
-
listener(mutatedNodesByType);
|
|
5657
|
-
});
|
|
5680
|
+
}
|
|
5658
5681
|
}
|
|
5659
5682
|
|
|
5660
5683
|
function triggerListeners(type, editor, isCurrentlyEnqueuingUpdates, ...payload) {
|
|
@@ -5690,11 +5713,14 @@ function triggerCommandListeners(editor, type, payload) {
|
|
|
5690
5713
|
const listenerInPriorityOrder = commandListeners.get(type);
|
|
5691
5714
|
|
|
5692
5715
|
if (listenerInPriorityOrder !== undefined) {
|
|
5693
|
-
const
|
|
5716
|
+
const listenersSet = listenerInPriorityOrder[i];
|
|
5694
5717
|
|
|
5695
|
-
if (
|
|
5696
|
-
|
|
5697
|
-
|
|
5718
|
+
if (listenersSet !== undefined) {
|
|
5719
|
+
const listeners = Array.from(listenersSet);
|
|
5720
|
+
const listenersLength = listeners.length;
|
|
5721
|
+
|
|
5722
|
+
for (let j = 0; j < listenersLength; j++) {
|
|
5723
|
+
if (listeners[j](payload, editor) === true) {
|
|
5698
5724
|
return true;
|
|
5699
5725
|
}
|
|
5700
5726
|
}
|
|
@@ -5933,7 +5959,7 @@ function removeNode(nodeToRemove, restoreSelection, preserveEmptyParent) {
|
|
|
5933
5959
|
return;
|
|
5934
5960
|
}
|
|
5935
5961
|
|
|
5936
|
-
const selection = $
|
|
5962
|
+
const selection = $maybeMoveChildrenSelectionToParent(nodeToRemove);
|
|
5937
5963
|
let selectionMoved = false;
|
|
5938
5964
|
|
|
5939
5965
|
if ($isRangeSelection(selection) && restoreSelection) {
|
|
@@ -6114,7 +6140,7 @@ class LexicalNode {
|
|
|
6114
6140
|
while (node !== null) {
|
|
6115
6141
|
const parent = node.getParent();
|
|
6116
6142
|
|
|
6117
|
-
if ($isRootNode(parent) && $isElementNode(node)) {
|
|
6143
|
+
if ($isRootNode(parent) && ($isElementNode(node) || $isDecoratorNode(node) && node.isTopLevel())) {
|
|
6118
6144
|
return node;
|
|
6119
6145
|
}
|
|
6120
6146
|
|
|
@@ -6403,7 +6429,7 @@ class LexicalNode {
|
|
|
6403
6429
|
|
|
6404
6430
|
if (latest === null) {
|
|
6405
6431
|
{
|
|
6406
|
-
throw Error(`Lexical node does not exist in active
|
|
6432
|
+
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.`);
|
|
6407
6433
|
}
|
|
6408
6434
|
}
|
|
6409
6435
|
|
|
@@ -7051,7 +7077,7 @@ class ElementNode extends LexicalNode {
|
|
|
7051
7077
|
|
|
7052
7078
|
if (nodeToInsert.__key === writableSelfKey) {
|
|
7053
7079
|
{
|
|
7054
|
-
throw Error(`append:
|
|
7080
|
+
throw Error(`append: attempting to append self`);
|
|
7055
7081
|
}
|
|
7056
7082
|
}
|
|
7057
7083
|
|
|
@@ -7842,6 +7868,10 @@ class TextNode extends LexicalNode {
|
|
|
7842
7868
|
conversion: convertBringAttentionToElement,
|
|
7843
7869
|
priority: 0
|
|
7844
7870
|
}),
|
|
7871
|
+
code: node => ({
|
|
7872
|
+
conversion: convertTextFormatElement,
|
|
7873
|
+
priority: 0
|
|
7874
|
+
}),
|
|
7845
7875
|
em: node => ({
|
|
7846
7876
|
conversion: convertTextFormatElement,
|
|
7847
7877
|
priority: 0
|
|
@@ -8199,25 +8229,39 @@ function convertSpanElement(domNode) {
|
|
|
8199
8229
|
|
|
8200
8230
|
const hasItalicFontStyle = span.style.fontStyle === 'italic'; // Google Docs uses span tags + text-decoration: underline for underline text
|
|
8201
8231
|
|
|
8202
|
-
const hasUnderlineTextDecoration = span.style.textDecoration === 'underline';
|
|
8232
|
+
const hasUnderlineTextDecoration = span.style.textDecoration === 'underline'; // Google Docs uses span tags + vertical-align to specify subscript and superscript
|
|
8233
|
+
|
|
8234
|
+
const verticalAlign = span.style.verticalAlign;
|
|
8203
8235
|
return {
|
|
8204
8236
|
forChild: lexicalNode => {
|
|
8205
|
-
if (
|
|
8237
|
+
if (!$isTextNode(lexicalNode)) {
|
|
8238
|
+
return lexicalNode;
|
|
8239
|
+
}
|
|
8240
|
+
|
|
8241
|
+
if (hasBoldFontWeight) {
|
|
8206
8242
|
lexicalNode.toggleFormat('bold');
|
|
8207
8243
|
}
|
|
8208
8244
|
|
|
8209
|
-
if (
|
|
8245
|
+
if (hasLinethroughTextDecoration) {
|
|
8210
8246
|
lexicalNode.toggleFormat('strikethrough');
|
|
8211
8247
|
}
|
|
8212
8248
|
|
|
8213
|
-
if (
|
|
8249
|
+
if (hasItalicFontStyle) {
|
|
8214
8250
|
lexicalNode.toggleFormat('italic');
|
|
8215
8251
|
}
|
|
8216
8252
|
|
|
8217
|
-
if (
|
|
8253
|
+
if (hasUnderlineTextDecoration) {
|
|
8218
8254
|
lexicalNode.toggleFormat('underline');
|
|
8219
8255
|
}
|
|
8220
8256
|
|
|
8257
|
+
if (verticalAlign === 'sub') {
|
|
8258
|
+
lexicalNode.toggleFormat('subscript');
|
|
8259
|
+
}
|
|
8260
|
+
|
|
8261
|
+
if (verticalAlign === 'super') {
|
|
8262
|
+
lexicalNode.toggleFormat('superscript');
|
|
8263
|
+
}
|
|
8264
|
+
|
|
8221
8265
|
return lexicalNode;
|
|
8222
8266
|
},
|
|
8223
8267
|
node: null
|
|
@@ -8261,6 +8305,7 @@ function convertTextDOMNode(domNode) {
|
|
|
8261
8305
|
}
|
|
8262
8306
|
|
|
8263
8307
|
const nodeNameToTextFormat = {
|
|
8308
|
+
code: 'code',
|
|
8264
8309
|
em: 'italic',
|
|
8265
8310
|
i: 'italic',
|
|
8266
8311
|
strong: 'bold',
|
|
@@ -8605,7 +8650,7 @@ class LexicalEditor {
|
|
|
8605
8650
|
this._nodes = nodes; // React node decorators for portals
|
|
8606
8651
|
|
|
8607
8652
|
this._decorators = {};
|
|
8608
|
-
this._pendingDecorators = null; // Used to optimize
|
|
8653
|
+
this._pendingDecorators = null; // Used to optimize reconciliation
|
|
8609
8654
|
|
|
8610
8655
|
this._dirtyType = NO_DIRTY_NODES;
|
|
8611
8656
|
this._cloneNotNeeded = new Set();
|
|
@@ -8853,7 +8898,7 @@ class LexicalEditor {
|
|
|
8853
8898
|
updateEditor(this, updateFn, options);
|
|
8854
8899
|
}
|
|
8855
8900
|
|
|
8856
|
-
focus(callbackFn) {
|
|
8901
|
+
focus(callbackFn, options = {}) {
|
|
8857
8902
|
const rootElement = this._rootElement;
|
|
8858
8903
|
|
|
8859
8904
|
if (rootElement !== null) {
|
|
@@ -8867,7 +8912,11 @@ class LexicalEditor {
|
|
|
8867
8912
|
// Marking the selection dirty will force the selection back to it
|
|
8868
8913
|
selection.dirty = true;
|
|
8869
8914
|
} else if (root.getChildrenSize() !== 0) {
|
|
8870
|
-
|
|
8915
|
+
if (options.defaultSelection === 'rootStart') {
|
|
8916
|
+
root.selectStart();
|
|
8917
|
+
} else {
|
|
8918
|
+
root.selectEnd();
|
|
8919
|
+
}
|
|
8871
8920
|
}
|
|
8872
8921
|
}, {
|
|
8873
8922
|
onUpdate: () => {
|
|
@@ -8921,7 +8970,7 @@ class LexicalEditor {
|
|
|
8921
8970
|
* LICENSE file in the root directory of this source tree.
|
|
8922
8971
|
*
|
|
8923
8972
|
*/
|
|
8924
|
-
const VERSION = '0.3.
|
|
8973
|
+
const VERSION = '0.3.9';
|
|
8925
8974
|
|
|
8926
8975
|
/**
|
|
8927
8976
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|