lexical 0.2.2 → 0.2.5
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.d.ts +71 -60
- package/Lexical.dev.js +653 -195
- package/Lexical.js.flow +30 -45
- package/Lexical.prod.js +164 -157
- package/package.json +1 -1
package/Lexical.dev.js
CHANGED
|
@@ -56,6 +56,7 @@ const IS_ALIGN_RIGHT = 3;
|
|
|
56
56
|
const IS_ALIGN_JUSTIFY = 4; // Reconciliation
|
|
57
57
|
|
|
58
58
|
const ZERO_WIDTH_CHAR = '\u200b';
|
|
59
|
+
const DOUBLE_LINE_BREAK = '\n\n';
|
|
59
60
|
const RTL = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC';
|
|
60
61
|
const LTR = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6' + '\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u200E\u2C00-\uFB1C' + '\uFE00-\uFE6F\uFEFD-\uFFFF';
|
|
61
62
|
const RTL_REGEX = new RegExp('^[^' + LTR + ']*[' + RTL + ']');
|
|
@@ -181,6 +182,9 @@ function getTextDirection(text) {
|
|
|
181
182
|
|
|
182
183
|
return null;
|
|
183
184
|
}
|
|
185
|
+
function $isTokenOrInertOrSegmented(node) {
|
|
186
|
+
return $isTokenOrInert(node) || node.isSegmented();
|
|
187
|
+
}
|
|
184
188
|
function $isTokenOrInert(node) {
|
|
185
189
|
return node.isToken() || node.isInert();
|
|
186
190
|
}
|
|
@@ -322,21 +326,24 @@ function $setCompositionKey(compositionKey) {
|
|
|
322
326
|
errorOnReadOnly();
|
|
323
327
|
const editor = getActiveEditor();
|
|
324
328
|
const previousCompositionKey = editor._compositionKey;
|
|
325
|
-
editor._compositionKey = compositionKey;
|
|
326
329
|
|
|
327
|
-
if (
|
|
328
|
-
|
|
330
|
+
if (compositionKey !== previousCompositionKey) {
|
|
331
|
+
editor._compositionKey = compositionKey;
|
|
329
332
|
|
|
330
|
-
if (
|
|
331
|
-
node
|
|
333
|
+
if (previousCompositionKey !== null) {
|
|
334
|
+
const node = $getNodeByKey(previousCompositionKey);
|
|
335
|
+
|
|
336
|
+
if (node !== null) {
|
|
337
|
+
node.getWritable();
|
|
338
|
+
}
|
|
332
339
|
}
|
|
333
|
-
}
|
|
334
340
|
|
|
335
|
-
|
|
336
|
-
|
|
341
|
+
if (compositionKey !== null) {
|
|
342
|
+
const node = $getNodeByKey(compositionKey);
|
|
337
343
|
|
|
338
|
-
|
|
339
|
-
|
|
344
|
+
if (node !== null) {
|
|
345
|
+
node.getWritable();
|
|
346
|
+
}
|
|
340
347
|
}
|
|
341
348
|
}
|
|
342
349
|
}
|
|
@@ -543,7 +550,9 @@ function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focu
|
|
|
543
550
|
const editor = getActiveEditor();
|
|
544
551
|
setTimeout(() => {
|
|
545
552
|
editor.update(() => {
|
|
546
|
-
node.
|
|
553
|
+
if (node.isAttached()) {
|
|
554
|
+
node.remove();
|
|
555
|
+
}
|
|
547
556
|
});
|
|
548
557
|
}, 20);
|
|
549
558
|
} else {
|
|
@@ -724,6 +733,9 @@ function isMoveUp(keyCode, ctrlKey, shiftKey, altKey, metaKey) {
|
|
|
724
733
|
function isMoveDown(keyCode, ctrlKey, shiftKey, altKey, metaKey) {
|
|
725
734
|
return isArrowDown(keyCode) && !ctrlKey && !metaKey;
|
|
726
735
|
}
|
|
736
|
+
function isModifier(ctrlKey, shiftKey, altKey, metaKey) {
|
|
737
|
+
return ctrlKey || shiftKey || altKey || metaKey;
|
|
738
|
+
}
|
|
727
739
|
function controlOrMeta(metaKey, ctrlKey) {
|
|
728
740
|
if (IS_APPLE) {
|
|
729
741
|
return metaKey;
|
|
@@ -849,6 +861,9 @@ function isFirefoxClipboardEvents() {
|
|
|
849
861
|
function dispatchCommand(editor, type, payload) {
|
|
850
862
|
return triggerCommandListeners(editor, type, payload);
|
|
851
863
|
}
|
|
864
|
+
function $textContentRequiresDoubleLinebreakAtEnd(node) {
|
|
865
|
+
return !$isRootNode(node) && !node.isLastChild() && !node.isInline();
|
|
866
|
+
}
|
|
852
867
|
|
|
853
868
|
/**
|
|
854
869
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -1088,6 +1103,7 @@ function internalCreateNodeFromParse(parsedNode, parsedNodeMap, editor, parentKe
|
|
|
1088
1103
|
node.__style = parsedNode.__style;
|
|
1089
1104
|
node.__mode = parsedNode.__mode;
|
|
1090
1105
|
node.__detail = parsedNode.__detail;
|
|
1106
|
+
node.__marks = parsedNode.__marks;
|
|
1091
1107
|
} // The selection might refer to an old node whose key has changed. Produce a
|
|
1092
1108
|
// new selection record with the old keys mapped to the new ones.
|
|
1093
1109
|
|
|
@@ -1138,8 +1154,8 @@ function internalCreateNodeFromParse(parsedNode, parsedNodeMap, editor, parentKe
|
|
|
1138
1154
|
}
|
|
1139
1155
|
} else if (originalSelection.type === 'grid') {
|
|
1140
1156
|
const gridKey = originalSelection.gridKey;
|
|
1141
|
-
const anchorCellKey = originalSelection.
|
|
1142
|
-
const focusCellKey = originalSelection.
|
|
1157
|
+
const anchorCellKey = originalSelection.anchor.key;
|
|
1158
|
+
const focusCellKey = originalSelection.focus.key;
|
|
1143
1159
|
|
|
1144
1160
|
if (remappedSelection == null && (gridKey === parsedKey || gridKey === anchorCellKey || gridKey === focusCellKey)) {
|
|
1145
1161
|
state.remappedSelection = remappedSelection = { ...originalSelection,
|
|
@@ -1153,11 +1169,11 @@ function internalCreateNodeFromParse(parsedNode, parsedNodeMap, editor, parentKe
|
|
|
1153
1169
|
}
|
|
1154
1170
|
|
|
1155
1171
|
if (anchorCellKey === parsedKey) {
|
|
1156
|
-
remappedSelection.
|
|
1172
|
+
remappedSelection.anchor.key = key;
|
|
1157
1173
|
}
|
|
1158
1174
|
|
|
1159
1175
|
if (focusCellKey === parsedKey) {
|
|
1160
|
-
remappedSelection.
|
|
1176
|
+
remappedSelection.focus.key = key;
|
|
1161
1177
|
}
|
|
1162
1178
|
}
|
|
1163
1179
|
}
|
|
@@ -1166,6 +1182,56 @@ function internalCreateNodeFromParse(parsedNode, parsedNodeMap, editor, parentKe
|
|
|
1166
1182
|
return node;
|
|
1167
1183
|
}
|
|
1168
1184
|
|
|
1185
|
+
/**
|
|
1186
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
1187
|
+
*
|
|
1188
|
+
* This source code is licensed under the MIT license found in the
|
|
1189
|
+
* LICENSE file in the root directory of this source tree.
|
|
1190
|
+
*
|
|
1191
|
+
*
|
|
1192
|
+
*/
|
|
1193
|
+
function createCommand() {
|
|
1194
|
+
// $FlowFixMe: avoid freezing the object for perf reasons
|
|
1195
|
+
return {};
|
|
1196
|
+
}
|
|
1197
|
+
const SELECTION_CHANGE_COMMAND = createCommand();
|
|
1198
|
+
const CLICK_COMMAND = createCommand();
|
|
1199
|
+
const DELETE_CHARACTER_COMMAND = createCommand();
|
|
1200
|
+
const INSERT_LINE_BREAK_COMMAND = createCommand();
|
|
1201
|
+
const INSERT_PARAGRAPH_COMMAND = createCommand();
|
|
1202
|
+
const INSERT_TEXT_COMMAND = createCommand();
|
|
1203
|
+
const PASTE_COMMAND = createCommand();
|
|
1204
|
+
const REMOVE_TEXT_COMMAND = createCommand();
|
|
1205
|
+
const DELETE_WORD_COMMAND = createCommand();
|
|
1206
|
+
const DELETE_LINE_COMMAND = createCommand();
|
|
1207
|
+
const FORMAT_TEXT_COMMAND = createCommand();
|
|
1208
|
+
const UNDO_COMMAND = createCommand();
|
|
1209
|
+
const REDO_COMMAND = createCommand();
|
|
1210
|
+
const KEY_ARROW_RIGHT_COMMAND = createCommand();
|
|
1211
|
+
const KEY_ARROW_LEFT_COMMAND = createCommand();
|
|
1212
|
+
const KEY_ARROW_UP_COMMAND = createCommand();
|
|
1213
|
+
const KEY_ARROW_DOWN_COMMAND = createCommand();
|
|
1214
|
+
const KEY_ENTER_COMMAND = createCommand();
|
|
1215
|
+
const KEY_BACKSPACE_COMMAND = createCommand();
|
|
1216
|
+
const KEY_ESCAPE_COMMAND = createCommand();
|
|
1217
|
+
const KEY_DELETE_COMMAND = createCommand();
|
|
1218
|
+
const KEY_TAB_COMMAND = createCommand();
|
|
1219
|
+
const INDENT_CONTENT_COMMAND = createCommand();
|
|
1220
|
+
const OUTDENT_CONTENT_COMMAND = createCommand();
|
|
1221
|
+
const DROP_COMMAND = createCommand();
|
|
1222
|
+
const FORMAT_ELEMENT_COMMAND = createCommand();
|
|
1223
|
+
const DRAGSTART_COMMAND = createCommand();
|
|
1224
|
+
const DRAGEND_COMMAND = createCommand();
|
|
1225
|
+
const COPY_COMMAND = createCommand();
|
|
1226
|
+
const CUT_COMMAND = createCommand();
|
|
1227
|
+
const CLEAR_EDITOR_COMMAND = createCommand();
|
|
1228
|
+
const CLEAR_HISTORY_COMMAND = createCommand();
|
|
1229
|
+
const CAN_REDO_COMMAND = createCommand();
|
|
1230
|
+
const CAN_UNDO_COMMAND = createCommand();
|
|
1231
|
+
const FOCUS_COMMAND = createCommand();
|
|
1232
|
+
const BLUR_COMMAND = createCommand();
|
|
1233
|
+
const KEY_MODIFIER_COMMAND = createCommand();
|
|
1234
|
+
|
|
1169
1235
|
/**
|
|
1170
1236
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
1171
1237
|
*
|
|
@@ -1193,20 +1259,31 @@ if (CAN_USE_BEFORE_INPUT) {
|
|
|
1193
1259
|
let lastKeyDownTimeStamp = 0;
|
|
1194
1260
|
let rootElementsRegistered = 0;
|
|
1195
1261
|
let isSelectionChangeFromReconcile = false;
|
|
1262
|
+
let isInsertLineBreak = false;
|
|
1263
|
+
let collapsedSelectionFormat = [0, 0, 'root', 0];
|
|
1264
|
+
|
|
1265
|
+
function shouldSkipSelectionChange(domNode, offset) {
|
|
1266
|
+
return domNode !== null && domNode.nodeType === DOM_TEXT_TYPE && offset !== 0 && offset !== domNode.nodeValue.length;
|
|
1267
|
+
}
|
|
1196
1268
|
|
|
1197
1269
|
function onSelectionChange(domSelection, editor, isActive) {
|
|
1198
1270
|
if (isSelectionChangeFromReconcile) {
|
|
1199
1271
|
isSelectionChangeFromReconcile = false;
|
|
1200
1272
|
const {
|
|
1201
1273
|
anchorNode,
|
|
1202
|
-
|
|
1274
|
+
anchorOffset,
|
|
1275
|
+
focusNode,
|
|
1276
|
+
focusOffset
|
|
1203
1277
|
} = domSelection; // If native DOM selection is on a DOM element, then
|
|
1204
1278
|
// we should continue as usual, as Lexical's selection
|
|
1205
1279
|
// may have normalized to a better child. If the DOM
|
|
1206
1280
|
// element is a text node, we can safely apply this
|
|
1207
1281
|
// optimization and skip the selection change entirely.
|
|
1282
|
+
// We also need to check if the offset is at the boundary,
|
|
1283
|
+
// because in this case, we might need to normalize to a
|
|
1284
|
+
// sibling instead.
|
|
1208
1285
|
|
|
1209
|
-
if (anchorNode
|
|
1286
|
+
if (shouldSkipSelectionChange(anchorNode, anchorOffset) && shouldSkipSelectionChange(focusNode, focusOffset)) {
|
|
1210
1287
|
return;
|
|
1211
1288
|
}
|
|
1212
1289
|
}
|
|
@@ -1221,19 +1298,45 @@ function onSelectionChange(domSelection, editor, isActive) {
|
|
|
1221
1298
|
|
|
1222
1299
|
const selection = $getSelection(); // Update the selection format
|
|
1223
1300
|
|
|
1224
|
-
if ($isRangeSelection(selection)
|
|
1225
|
-
// Badly interpreted range selection when collapsed - #1482
|
|
1226
|
-
if (domSelection.type === 'Range') {
|
|
1227
|
-
selection.dirty = true;
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1301
|
+
if ($isRangeSelection(selection)) {
|
|
1230
1302
|
const anchor = selection.anchor;
|
|
1303
|
+
const anchorNode = anchor.getNode();
|
|
1304
|
+
|
|
1305
|
+
if (selection.isCollapsed()) {
|
|
1306
|
+
// Badly interpreted range selection when collapsed - #1482
|
|
1307
|
+
if (domSelection.type === 'Range') {
|
|
1308
|
+
selection.dirty = true;
|
|
1309
|
+
} // If we have marked a collapsed selection format, and we're
|
|
1310
|
+
// within the given time range – then attempt to use that format
|
|
1311
|
+
// instead of getting the format from the anchor node.
|
|
1312
|
+
|
|
1313
|
+
|
|
1314
|
+
const currentTimeStamp = window.event.timeStamp;
|
|
1315
|
+
const [lastFormat, lastOffset, lastKey, timeStamp] = collapsedSelectionFormat;
|
|
1316
|
+
|
|
1317
|
+
if (currentTimeStamp < timeStamp + 200 && anchor.offset === lastOffset && anchor.key === lastKey) {
|
|
1318
|
+
selection.format = lastFormat;
|
|
1319
|
+
} else {
|
|
1320
|
+
if (anchor.type === 'text') {
|
|
1321
|
+
selection.format = anchorNode.getFormat();
|
|
1322
|
+
} else if (anchor.type === 'element') {
|
|
1323
|
+
selection.format = 0;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
} else {
|
|
1327
|
+
const focus = selection.focus;
|
|
1328
|
+
const focusNode = focus.getNode();
|
|
1329
|
+
let combinedFormat = 0;
|
|
1231
1330
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1331
|
+
if (anchor.type === 'text') {
|
|
1332
|
+
combinedFormat |= anchorNode.getFormat();
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
if (focus.type === 'text' && !anchorNode.is(focusNode)) {
|
|
1336
|
+
combinedFormat |= focusNode.getFormat();
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
selection.format = combinedFormat;
|
|
1237
1340
|
}
|
|
1238
1341
|
}
|
|
1239
1342
|
|
|
@@ -1254,7 +1357,7 @@ function onClick(event, editor) {
|
|
|
1254
1357
|
const anchor = selection.anchor;
|
|
1255
1358
|
|
|
1256
1359
|
if (anchor.type === 'element' && anchor.offset === 0 && selection.isCollapsed() && $getRoot().getChildrenSize() === 1 && anchor.getNode().getTopLevelElementOrThrow().isEmpty()) {
|
|
1257
|
-
const lastSelection =
|
|
1360
|
+
const lastSelection = $getPreviousSelection();
|
|
1258
1361
|
|
|
1259
1362
|
if (lastSelection !== null && selection.is(lastSelection)) {
|
|
1260
1363
|
getDOMSelection().removeAllRanges();
|
|
@@ -1331,12 +1434,19 @@ function onBeforeInput(event, editor) {
|
|
|
1331
1434
|
updateEditor(editor, () => {
|
|
1332
1435
|
const selection = $getSelection();
|
|
1333
1436
|
|
|
1334
|
-
if (!$isRangeSelection(selection)) {
|
|
1335
|
-
return;
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
1437
|
if (inputType === 'deleteContentBackward') {
|
|
1339
|
-
|
|
1438
|
+
if (selection === null) {
|
|
1439
|
+
// Use previous selection
|
|
1440
|
+
const prevSelection = $getPreviousSelection();
|
|
1441
|
+
|
|
1442
|
+
if (!$isRangeSelection(prevSelection)) {
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
$setSelection(prevSelection.clone());
|
|
1447
|
+
} // Used for Android
|
|
1448
|
+
|
|
1449
|
+
|
|
1340
1450
|
$setCompositionKey(null);
|
|
1341
1451
|
event.preventDefault();
|
|
1342
1452
|
lastKeyDownTimeStamp = 0;
|
|
@@ -1350,6 +1460,10 @@ function onBeforeInput(event, editor) {
|
|
|
1350
1460
|
return;
|
|
1351
1461
|
}
|
|
1352
1462
|
|
|
1463
|
+
if (!$isRangeSelection(selection)) {
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1353
1467
|
const data = event.data;
|
|
1354
1468
|
|
|
1355
1469
|
if (!selection.dirty && selection.isCollapsed() && !$isRootNode(selection.anchor.getNode())) {
|
|
@@ -1365,7 +1479,7 @@ function onBeforeInput(event, editor) {
|
|
|
1365
1479
|
if (data === '\n') {
|
|
1366
1480
|
event.preventDefault();
|
|
1367
1481
|
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND);
|
|
1368
|
-
} else if (data ===
|
|
1482
|
+
} else if (data === DOUBLE_LINE_BREAK) {
|
|
1369
1483
|
event.preventDefault();
|
|
1370
1484
|
dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND);
|
|
1371
1485
|
} else if (data == null && event.dataTransfer) {
|
|
@@ -1414,8 +1528,16 @@ function onBeforeInput(event, editor) {
|
|
|
1414
1528
|
case 'insertParagraph':
|
|
1415
1529
|
{
|
|
1416
1530
|
// Used for Android
|
|
1417
|
-
$setCompositionKey(null);
|
|
1418
|
-
|
|
1531
|
+
$setCompositionKey(null); // Some browsers do not provide the type "insertLineBreak".
|
|
1532
|
+
// So instead, we need to infer it from the keyboard event.
|
|
1533
|
+
|
|
1534
|
+
if (isInsertLineBreak) {
|
|
1535
|
+
isInsertLineBreak = false;
|
|
1536
|
+
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND);
|
|
1537
|
+
} else {
|
|
1538
|
+
dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND);
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1419
1541
|
break;
|
|
1420
1542
|
}
|
|
1421
1543
|
|
|
@@ -1549,7 +1671,9 @@ function onCompositionStart(event, editor) {
|
|
|
1549
1671
|
|
|
1550
1672
|
if ( // If it has been 30ms since the last keydown, then we should
|
|
1551
1673
|
// apply the empty space heuristic.
|
|
1552
|
-
event.timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY ||
|
|
1674
|
+
event.timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY || // FF has issues around composing multibyte characters, so we also
|
|
1675
|
+
// need to invoke the empty space heuristic below.
|
|
1676
|
+
IS_FIREFOX && anchor.type === 'element' || !selection.isCollapsed() || selection.anchor.getNode().getFormat() !== selection.format) {
|
|
1553
1677
|
// We insert an empty space, ready for the composition
|
|
1554
1678
|
// to get inserted into the new node we create. If
|
|
1555
1679
|
// we don't do this, Safari will fail on us because
|
|
@@ -1620,11 +1744,14 @@ function onKeyDown(event, editor) {
|
|
|
1620
1744
|
} else if (isMoveDown(keyCode, ctrlKey, shiftKey, altKey, metaKey)) {
|
|
1621
1745
|
dispatchCommand(editor, KEY_ARROW_DOWN_COMMAND, event);
|
|
1622
1746
|
} else if (isLineBreak(keyCode, shiftKey)) {
|
|
1747
|
+
isInsertLineBreak = true;
|
|
1623
1748
|
dispatchCommand(editor, KEY_ENTER_COMMAND, event);
|
|
1624
1749
|
} else if (isOpenLineBreak(keyCode, ctrlKey)) {
|
|
1625
1750
|
event.preventDefault();
|
|
1751
|
+
isInsertLineBreak = true;
|
|
1626
1752
|
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, true);
|
|
1627
1753
|
} else if (isParagraph(keyCode, shiftKey)) {
|
|
1754
|
+
isInsertLineBreak = false;
|
|
1628
1755
|
dispatchCommand(editor, KEY_ENTER_COMMAND, event);
|
|
1629
1756
|
} else if (isDeleteBackward(keyCode, altKey, metaKey, ctrlKey)) {
|
|
1630
1757
|
if (isBackspace(keyCode)) {
|
|
@@ -1672,6 +1799,10 @@ function onKeyDown(event, editor) {
|
|
|
1672
1799
|
event.preventDefault();
|
|
1673
1800
|
dispatchCommand(editor, REDO_COMMAND);
|
|
1674
1801
|
}
|
|
1802
|
+
|
|
1803
|
+
if (isModifier(ctrlKey, shiftKey, altKey, metaKey)) {
|
|
1804
|
+
dispatchCommand(editor, KEY_MODIFIER_COMMAND, event);
|
|
1805
|
+
}
|
|
1675
1806
|
}
|
|
1676
1807
|
|
|
1677
1808
|
function getRootElementRemoveHandles(rootElement) {
|
|
@@ -1755,6 +1886,9 @@ function addRootElementEvents(rootElement, editor) {
|
|
|
1755
1886
|
case 'dragstart':
|
|
1756
1887
|
return dispatchCommand(editor, DRAGSTART_COMMAND, event);
|
|
1757
1888
|
|
|
1889
|
+
case 'dragend':
|
|
1890
|
+
return dispatchCommand(editor, DRAGEND_COMMAND, event);
|
|
1891
|
+
|
|
1758
1892
|
case 'focus':
|
|
1759
1893
|
return dispatchCommand(editor, FOCUS_COMMAND, event);
|
|
1760
1894
|
|
|
@@ -1821,6 +1955,9 @@ function cleanActiveNestedEditorsMap(editor) {
|
|
|
1821
1955
|
function markSelectionChangeFromReconcile() {
|
|
1822
1956
|
isSelectionChangeFromReconcile = true;
|
|
1823
1957
|
}
|
|
1958
|
+
function markCollapsedSelectionFormat(format, offset, key, timeStamp) {
|
|
1959
|
+
collapsedSelectionFormat = [format, offset, key, timeStamp];
|
|
1960
|
+
}
|
|
1824
1961
|
|
|
1825
1962
|
/**
|
|
1826
1963
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -1888,7 +2025,7 @@ function setTextAlign(domStyle, value) {
|
|
|
1888
2025
|
}
|
|
1889
2026
|
|
|
1890
2027
|
function setElementIndent(dom, indent) {
|
|
1891
|
-
dom.style.setProperty('padding-inline-start', indent === 0 ? '' : indent *
|
|
2028
|
+
dom.style.setProperty('padding-inline-start', indent === 0 ? '' : indent * 20 + 'px');
|
|
1892
2029
|
}
|
|
1893
2030
|
|
|
1894
2031
|
function setElementFormat(dom, format) {
|
|
@@ -1949,6 +2086,11 @@ function createNode(key, parentDOM, insertDOM) {
|
|
|
1949
2086
|
}
|
|
1950
2087
|
|
|
1951
2088
|
reconcileElementTerminatingLineBreak(null, children, dom);
|
|
2089
|
+
|
|
2090
|
+
if ($textContentRequiresDoubleLinebreakAtEnd(node)) {
|
|
2091
|
+
subTreeTextContent += DOUBLE_LINE_BREAK;
|
|
2092
|
+
editorTextContent += DOUBLE_LINE_BREAK;
|
|
2093
|
+
}
|
|
1952
2094
|
} else {
|
|
1953
2095
|
const text = node.getTextContent();
|
|
1954
2096
|
|
|
@@ -2120,12 +2262,12 @@ function reconcileBlockDirection(element, dom) {
|
|
|
2120
2262
|
function reconcileChildrenWithDirection(prevChildren, nextChildren, element, dom) {
|
|
2121
2263
|
const previousSubTreeDirectionTextContent = subTreeDirectionedTextContent;
|
|
2122
2264
|
subTreeDirectionedTextContent = '';
|
|
2123
|
-
reconcileChildren(prevChildren, nextChildren, dom);
|
|
2265
|
+
reconcileChildren(element, prevChildren, nextChildren, dom);
|
|
2124
2266
|
reconcileBlockDirection(element, dom);
|
|
2125
2267
|
subTreeDirectionedTextContent = previousSubTreeDirectionTextContent;
|
|
2126
2268
|
}
|
|
2127
2269
|
|
|
2128
|
-
function reconcileChildren(prevChildren, nextChildren, dom) {
|
|
2270
|
+
function reconcileChildren(element, prevChildren, nextChildren, dom) {
|
|
2129
2271
|
const previousSubTreeTextContent = subTreeTextContent;
|
|
2130
2272
|
subTreeTextContent = '';
|
|
2131
2273
|
const prevChildrenLength = prevChildren.length;
|
|
@@ -2160,7 +2302,11 @@ function reconcileChildren(prevChildren, nextChildren, dom) {
|
|
|
2160
2302
|
}
|
|
2161
2303
|
}
|
|
2162
2304
|
} else {
|
|
2163
|
-
reconcileNodeChildren(prevChildren, nextChildren, prevChildrenLength, nextChildrenLength, dom);
|
|
2305
|
+
reconcileNodeChildren(prevChildren, nextChildren, prevChildrenLength, nextChildrenLength, element, dom);
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
if ($textContentRequiresDoubleLinebreakAtEnd(element)) {
|
|
2309
|
+
subTreeTextContent += DOUBLE_LINE_BREAK;
|
|
2164
2310
|
} // $FlowFixMe: internal field
|
|
2165
2311
|
|
|
2166
2312
|
|
|
@@ -2255,6 +2401,11 @@ function reconcileNode(key, parentDOM) {
|
|
|
2255
2401
|
reconcileElementTerminatingLineBreak(prevChildren, nextChildren, dom);
|
|
2256
2402
|
}
|
|
2257
2403
|
}
|
|
2404
|
+
|
|
2405
|
+
if ($textContentRequiresDoubleLinebreakAtEnd(nextNode)) {
|
|
2406
|
+
subTreeTextContent += DOUBLE_LINE_BREAK;
|
|
2407
|
+
editorTextContent += DOUBLE_LINE_BREAK;
|
|
2408
|
+
}
|
|
2258
2409
|
} else {
|
|
2259
2410
|
const text = nextNode.getTextContent();
|
|
2260
2411
|
|
|
@@ -2264,6 +2415,9 @@ function reconcileNode(key, parentDOM) {
|
|
|
2264
2415
|
if (decorator !== null) {
|
|
2265
2416
|
reconcileDecorator(key, decorator);
|
|
2266
2417
|
}
|
|
2418
|
+
|
|
2419
|
+
subTreeTextContent += text;
|
|
2420
|
+
editorTextContent += text;
|
|
2267
2421
|
} else if ($isTextNode(nextNode) && !nextNode.isDirectionless()) {
|
|
2268
2422
|
// Handle text content, for LTR, LTR cases.
|
|
2269
2423
|
subTreeDirectionedTextContent += text;
|
|
@@ -2312,7 +2466,7 @@ function getNextSibling(element) {
|
|
|
2312
2466
|
return element.nextSibling;
|
|
2313
2467
|
}
|
|
2314
2468
|
|
|
2315
|
-
function reconcileNodeChildren(prevChildren, nextChildren, prevChildrenLength, nextChildrenLength, dom) {
|
|
2469
|
+
function reconcileNodeChildren(prevChildren, nextChildren, prevChildrenLength, nextChildrenLength, element, dom) {
|
|
2316
2470
|
const prevEndIndex = prevChildrenLength - 1;
|
|
2317
2471
|
const nextEndIndex = nextChildrenLength - 1;
|
|
2318
2472
|
let prevChildrenSet;
|
|
@@ -2453,7 +2607,7 @@ function updateEditorState(rootElement, currentEditorState, pendingEditorState,
|
|
|
2453
2607
|
|
|
2454
2608
|
const domSelection = getDOMSelection();
|
|
2455
2609
|
|
|
2456
|
-
if (domSelection !== null && (needsUpdate || pendingSelection === null || pendingSelection.dirty)) {
|
|
2610
|
+
if (!editor._readOnly && domSelection !== null && (needsUpdate || pendingSelection === null || pendingSelection.dirty)) {
|
|
2457
2611
|
reconcileSelection(currentSelection, pendingSelection, editor, domSelection);
|
|
2458
2612
|
}
|
|
2459
2613
|
|
|
@@ -2526,11 +2680,15 @@ function reconcileSelection(prevSelection, nextSelection, editor, domSelection)
|
|
|
2526
2680
|
const focusDOM = getElementByKeyOrThrow(editor, focusKey);
|
|
2527
2681
|
const nextAnchorOffset = anchor.offset;
|
|
2528
2682
|
const nextFocusOffset = focus.offset;
|
|
2683
|
+
const nextFormat = nextSelection.format;
|
|
2684
|
+
const isCollapsed = nextSelection.isCollapsed();
|
|
2529
2685
|
let nextAnchorNode = anchorDOM;
|
|
2530
2686
|
let nextFocusNode = focusDOM;
|
|
2687
|
+
let anchorFormatChanged = false;
|
|
2531
2688
|
|
|
2532
2689
|
if (anchor.type === 'text') {
|
|
2533
2690
|
nextAnchorNode = getDOMTextNode(anchorDOM);
|
|
2691
|
+
anchorFormatChanged = anchor.getNode().getFormat() !== nextFormat;
|
|
2534
2692
|
}
|
|
2535
2693
|
|
|
2536
2694
|
if (focus.type === 'text') {
|
|
@@ -2541,20 +2699,31 @@ function reconcileSelection(prevSelection, nextSelection, editor, domSelection)
|
|
|
2541
2699
|
|
|
2542
2700
|
if (nextAnchorNode === null || nextFocusNode === null) {
|
|
2543
2701
|
return;
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
if (isCollapsed && (prevSelection === null || anchorFormatChanged || prevSelection.format !== nextFormat)) {
|
|
2705
|
+
markCollapsedSelectionFormat(nextFormat, nextAnchorOffset, anchorKey, performance.now());
|
|
2544
2706
|
} // Diff against the native DOM selection to ensure we don't do
|
|
2545
|
-
// an unnecessary selection update.
|
|
2707
|
+
// an unnecessary selection update. We also skip this check if
|
|
2708
|
+
// we're moving selection to within an element, as this can
|
|
2709
|
+
// sometimes be problematic around scrolling.
|
|
2546
2710
|
|
|
2547
2711
|
|
|
2548
2712
|
if (anchorOffset === nextAnchorOffset && focusOffset === nextFocusOffset && anchorDOMNode === nextAnchorNode && focusDOMNode === nextFocusNode && // Badly interpreted range selection when collapsed - #1482
|
|
2549
|
-
!(domSelection.type === 'Range' &&
|
|
2713
|
+
!(domSelection.type === 'Range' && isCollapsed)) {
|
|
2550
2714
|
// If the root element does not have focus, ensure it has focus
|
|
2551
2715
|
if (rootElement !== null && (activeElement === null || !rootElement.contains(activeElement))) {
|
|
2552
2716
|
rootElement.focus({
|
|
2553
2717
|
preventScroll: true
|
|
2554
2718
|
});
|
|
2555
|
-
}
|
|
2719
|
+
} // In Safari/iOS if we have selection on an element, then we also
|
|
2720
|
+
// need to additionally set the DOM selection, otherwise a selectionchange
|
|
2721
|
+
// event will not fire.
|
|
2556
2722
|
|
|
2557
|
-
|
|
2723
|
+
|
|
2724
|
+
if (!(IS_IOS || IS_SAFARI) || anchor.type !== 'element') {
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2558
2727
|
} // Apply the updated selection to the DOM. Note: this will trigger
|
|
2559
2728
|
// a "selectionchange" event, although it will be asynchronous.
|
|
2560
2729
|
|
|
@@ -2685,7 +2854,7 @@ function $normalizeAllDirtyTextNodes(editorState, editor) {
|
|
|
2685
2854
|
for (const nodeKey of dirtyLeaves) {
|
|
2686
2855
|
const node = nodeMap.get(nodeKey);
|
|
2687
2856
|
|
|
2688
|
-
if ($isTextNode(node) && node.isSimpleText() && !node.isUnmergeable()) {
|
|
2857
|
+
if ($isTextNode(node) && node.isAttached() && node.isSimpleText() && !node.isUnmergeable()) {
|
|
2689
2858
|
$normalizeTextNode(node);
|
|
2690
2859
|
}
|
|
2691
2860
|
}
|
|
@@ -2721,7 +2890,7 @@ function $applyAllTransforms(editorState, editor) {
|
|
|
2721
2890
|
for (const nodeKey of untransformedDirtyLeaves) {
|
|
2722
2891
|
const node = nodeMap.get(nodeKey);
|
|
2723
2892
|
|
|
2724
|
-
if ($isTextNode(node) && node.isSimpleText() && !node.isUnmergeable()) {
|
|
2893
|
+
if ($isTextNode(node) && node.isAttached() && node.isSimpleText() && !node.isUnmergeable()) {
|
|
2725
2894
|
$normalizeTextNode(node);
|
|
2726
2895
|
}
|
|
2727
2896
|
|
|
@@ -3039,9 +3208,9 @@ function triggerDeferredUpdateCallbacks(editor) {
|
|
|
3039
3208
|
}
|
|
3040
3209
|
}
|
|
3041
3210
|
|
|
3042
|
-
function processNestedUpdates(editor) {
|
|
3211
|
+
function processNestedUpdates(editor, initialSkipTransforms) {
|
|
3043
3212
|
const queuedUpdates = editor._updates;
|
|
3044
|
-
let skipTransforms = false; // Updates might grow as we process them, we so we'll need
|
|
3213
|
+
let skipTransforms = initialSkipTransforms || false; // Updates might grow as we process them, we so we'll need
|
|
3045
3214
|
// to handle each update as we go until the updates array is
|
|
3046
3215
|
// empty.
|
|
3047
3216
|
|
|
@@ -3119,7 +3288,7 @@ function beginUpdate(editor, updateFn, options) {
|
|
|
3119
3288
|
|
|
3120
3289
|
const startingCompositionKey = editor._compositionKey;
|
|
3121
3290
|
updateFn();
|
|
3122
|
-
skipTransforms = processNestedUpdates(editor);
|
|
3291
|
+
skipTransforms = processNestedUpdates(editor, skipTransforms);
|
|
3123
3292
|
applySelectionTransforms(pendingEditorState, editor);
|
|
3124
3293
|
|
|
3125
3294
|
if (editor._dirtyType !== NO_DIRTY_NODES) {
|
|
@@ -3480,10 +3649,6 @@ class Point {
|
|
|
3480
3649
|
return aNode.isBefore(bNode);
|
|
3481
3650
|
}
|
|
3482
3651
|
|
|
3483
|
-
getCharacterOffset() {
|
|
3484
|
-
return this.type === 'text' ? this.offset : 0;
|
|
3485
|
-
}
|
|
3486
|
-
|
|
3487
3652
|
getNode() {
|
|
3488
3653
|
const key = this.key;
|
|
3489
3654
|
const node = $getNodeByKey(key);
|
|
@@ -3665,12 +3830,10 @@ function $isRangeSelection(x) {
|
|
|
3665
3830
|
return x instanceof RangeSelection;
|
|
3666
3831
|
}
|
|
3667
3832
|
class GridSelection {
|
|
3668
|
-
constructor(gridKey,
|
|
3833
|
+
constructor(gridKey, anchor, focus) {
|
|
3669
3834
|
this.gridKey = gridKey;
|
|
3670
|
-
this.
|
|
3671
|
-
this.
|
|
3672
|
-
this.focusCellKey = focusCellKey;
|
|
3673
|
-
this.focus = $createPoint(focusCellKey, 0, 'element');
|
|
3835
|
+
this.anchor = anchor;
|
|
3836
|
+
this.focus = focus;
|
|
3674
3837
|
this.dirty = false;
|
|
3675
3838
|
}
|
|
3676
3839
|
|
|
@@ -3679,18 +3842,18 @@ class GridSelection {
|
|
|
3679
3842
|
return false;
|
|
3680
3843
|
}
|
|
3681
3844
|
|
|
3682
|
-
return this.gridKey === selection.gridKey && this.
|
|
3845
|
+
return this.gridKey === selection.gridKey && this.anchor.is(this.focus);
|
|
3683
3846
|
}
|
|
3684
3847
|
|
|
3685
3848
|
set(gridKey, anchorCellKey, focusCellKey) {
|
|
3686
3849
|
this.dirty = true;
|
|
3687
3850
|
this.gridKey = gridKey;
|
|
3688
|
-
this.
|
|
3689
|
-
this.
|
|
3851
|
+
this.anchor.key = anchorCellKey;
|
|
3852
|
+
this.focus.key = focusCellKey;
|
|
3690
3853
|
}
|
|
3691
3854
|
|
|
3692
3855
|
clone() {
|
|
3693
|
-
return new GridSelection(this.gridKey, this.
|
|
3856
|
+
return new GridSelection(this.gridKey, this.anchor, this.focus);
|
|
3694
3857
|
}
|
|
3695
3858
|
|
|
3696
3859
|
isCollapsed() {
|
|
@@ -3701,6 +3864,10 @@ class GridSelection {
|
|
|
3701
3864
|
return this.focus.isBefore(this.anchor);
|
|
3702
3865
|
}
|
|
3703
3866
|
|
|
3867
|
+
getCharacterOffsets() {
|
|
3868
|
+
return getCharacterOffsets(this);
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3704
3871
|
extract() {
|
|
3705
3872
|
return this.getNodes();
|
|
3706
3873
|
}
|
|
@@ -3712,7 +3879,7 @@ class GridSelection {
|
|
|
3712
3879
|
}
|
|
3713
3880
|
|
|
3714
3881
|
getShape() {
|
|
3715
|
-
const anchorCellNode = $getNodeByKey(this.
|
|
3882
|
+
const anchorCellNode = $getNodeByKey(this.anchor.key);
|
|
3716
3883
|
|
|
3717
3884
|
if (!anchorCellNode) {
|
|
3718
3885
|
throw Error(`getNodes: expected to find AnchorNode`);
|
|
@@ -3720,7 +3887,7 @@ class GridSelection {
|
|
|
3720
3887
|
|
|
3721
3888
|
const anchorCellNodeIndex = anchorCellNode.getIndexWithinParent();
|
|
3722
3889
|
const anchorCelRoweIndex = anchorCellNode.getParentOrThrow().getIndexWithinParent();
|
|
3723
|
-
const focusCellNode = $getNodeByKey(this.
|
|
3890
|
+
const focusCellNode = $getNodeByKey(this.focus.key);
|
|
3724
3891
|
|
|
3725
3892
|
if (!focusCellNode) {
|
|
3726
3893
|
throw Error(`getNodes: expected to find FocusNode`);
|
|
@@ -3851,7 +4018,7 @@ class RangeSelection {
|
|
|
3851
4018
|
}
|
|
3852
4019
|
|
|
3853
4020
|
if (firstNode.is(lastNode)) {
|
|
3854
|
-
if ($isElementNode(firstNode)) {
|
|
4021
|
+
if ($isElementNode(firstNode) && (firstNode.getChildrenSize() > 0 || firstNode.excludeFromCopy())) {
|
|
3855
4022
|
return [];
|
|
3856
4023
|
}
|
|
3857
4024
|
|
|
@@ -3879,8 +4046,7 @@ class RangeSelection {
|
|
|
3879
4046
|
const anchor = this.anchor;
|
|
3880
4047
|
const focus = this.focus;
|
|
3881
4048
|
const isBefore = anchor.isBefore(focus);
|
|
3882
|
-
const anchorOffset =
|
|
3883
|
-
const focusOffset = focus.getCharacterOffset();
|
|
4049
|
+
const [anchorOffset, focusOffset] = getCharacterOffsets(this);
|
|
3884
4050
|
let textContent = '';
|
|
3885
4051
|
let prevWasElement = true;
|
|
3886
4052
|
|
|
@@ -4008,11 +4174,13 @@ class RangeSelection {
|
|
|
4008
4174
|
const firstNodeText = firstNode.getTextContent();
|
|
4009
4175
|
const firstNodeTextLength = firstNodeText.length;
|
|
4010
4176
|
const firstNodeParent = firstNode.getParentOrThrow();
|
|
4177
|
+
const lastIndex = selectedNodesLength - 1;
|
|
4178
|
+
let lastNode = selectedNodes[lastIndex];
|
|
4011
4179
|
|
|
4012
4180
|
if (this.isCollapsed() && startOffset === firstNodeTextLength && (firstNode.isSegmented() || firstNode.isToken() || !firstNode.canInsertTextAfter() || !firstNodeParent.canInsertTextAfter())) {
|
|
4013
4181
|
let nextSibling = firstNode.getNextSibling();
|
|
4014
4182
|
|
|
4015
|
-
if (!$isTextNode(nextSibling) || $
|
|
4183
|
+
if (!$isTextNode(nextSibling) || $isTokenOrInertOrSegmented(nextSibling)) {
|
|
4016
4184
|
nextSibling = $createTextNode();
|
|
4017
4185
|
|
|
4018
4186
|
if (!firstNodeParent.canInsertTextAfter()) {
|
|
@@ -4032,7 +4200,7 @@ class RangeSelection {
|
|
|
4032
4200
|
} else if (this.isCollapsed() && startOffset === 0 && (firstNode.isSegmented() || firstNode.isToken() || !firstNode.canInsertTextBefore() || !firstNodeParent.canInsertTextBefore())) {
|
|
4033
4201
|
let prevSibling = firstNode.getPreviousSibling();
|
|
4034
4202
|
|
|
4035
|
-
if (!$isTextNode(prevSibling) || $
|
|
4203
|
+
if (!$isTextNode(prevSibling) || $isTokenOrInertOrSegmented(prevSibling)) {
|
|
4036
4204
|
prevSibling = $createTextNode();
|
|
4037
4205
|
|
|
4038
4206
|
if (!firstNodeParent.canInsertTextBefore()) {
|
|
@@ -4053,6 +4221,19 @@ class RangeSelection {
|
|
|
4053
4221
|
const textNode = $createTextNode(firstNode.getTextContent());
|
|
4054
4222
|
firstNode.replace(textNode);
|
|
4055
4223
|
firstNode = textNode;
|
|
4224
|
+
} else if (!this.isCollapsed() && text !== '') {
|
|
4225
|
+
// When the firstNode or lastNode parents are elements that
|
|
4226
|
+
// do not allow text to be inserted before or after, we first
|
|
4227
|
+
// clear the content. Then we normalize selection, then insert
|
|
4228
|
+
// the new content.
|
|
4229
|
+
const lastNodeParent = lastNode.getParent();
|
|
4230
|
+
|
|
4231
|
+
if (!firstNodeParent.canInsertTextBefore() || !firstNodeParent.canInsertTextAfter() || $isElementNode(lastNodeParent) && (!lastNodeParent.canInsertTextBefore() || !lastNodeParent.canInsertTextAfter())) {
|
|
4232
|
+
this.insertText('');
|
|
4233
|
+
normalizeSelectionPointsForBoundaries(this.anchor, this.focus, null);
|
|
4234
|
+
this.insertText(text);
|
|
4235
|
+
return;
|
|
4236
|
+
}
|
|
4056
4237
|
}
|
|
4057
4238
|
|
|
4058
4239
|
if (selectedNodesLength === 1) {
|
|
@@ -4101,8 +4282,6 @@ class RangeSelection {
|
|
|
4101
4282
|
this.anchor.offset -= text.length;
|
|
4102
4283
|
}
|
|
4103
4284
|
} else {
|
|
4104
|
-
const lastIndex = selectedNodesLength - 1;
|
|
4105
|
-
let lastNode = selectedNodes[lastIndex];
|
|
4106
4285
|
const markedNodeKeysForKeep = new Set([...firstNode.getParentKeys(), ...lastNode.getParentKeys()]);
|
|
4107
4286
|
const firstElement = $isElementNode(firstNode) ? firstNode : firstNode.getParentOrThrow();
|
|
4108
4287
|
const lastElement = $isElementNode(lastNode) ? lastNode : lastNode.getParentOrThrow(); // Handle mutations to the last node.
|
|
@@ -4118,7 +4297,13 @@ class RangeSelection {
|
|
|
4118
4297
|
lastNode = lastNode.spliceText(0, endOffset, '');
|
|
4119
4298
|
markedNodeKeysForKeep.add(lastNode.__key);
|
|
4120
4299
|
} else {
|
|
4121
|
-
lastNode.
|
|
4300
|
+
const lastNodeParent = lastNode.getParentOrThrow();
|
|
4301
|
+
|
|
4302
|
+
if (!lastNodeParent.canBeEmpty() && lastNodeParent.getChildrenSize() === 1) {
|
|
4303
|
+
lastNodeParent.remove();
|
|
4304
|
+
} else {
|
|
4305
|
+
lastNode.remove();
|
|
4306
|
+
}
|
|
4122
4307
|
}
|
|
4123
4308
|
} else {
|
|
4124
4309
|
markedNodeKeysForKeep.add(lastNode.__key);
|
|
@@ -4227,10 +4412,9 @@ class RangeSelection {
|
|
|
4227
4412
|
|
|
4228
4413
|
const anchor = this.anchor;
|
|
4229
4414
|
const focus = this.focus;
|
|
4230
|
-
const firstNodeText = firstNode.getTextContent();
|
|
4231
|
-
const firstNodeTextLength = firstNodeText.length;
|
|
4232
4415
|
const focusOffset = focus.offset;
|
|
4233
4416
|
let firstNextFormat = 0;
|
|
4417
|
+
let firstNodeTextLength = firstNode.getTextContent().length;
|
|
4234
4418
|
|
|
4235
4419
|
for (let i = 0; i < selectedNodes.length; i++) {
|
|
4236
4420
|
const selectedNode = selectedNodes[i];
|
|
@@ -4257,6 +4441,7 @@ class RangeSelection {
|
|
|
4257
4441
|
anchorOffset = 0;
|
|
4258
4442
|
startOffset = 0;
|
|
4259
4443
|
firstNode = nextSibling;
|
|
4444
|
+
firstNodeTextLength = nextSibling.getTextContent().length;
|
|
4260
4445
|
firstNextFormat = firstNode.getFormatFlags(formatType, null);
|
|
4261
4446
|
}
|
|
4262
4447
|
} // This is the case where we only selected a single node
|
|
@@ -4637,6 +4822,7 @@ class RangeSelection {
|
|
|
4637
4822
|
|
|
4638
4823
|
if (anchor.type === 'text') {
|
|
4639
4824
|
const anchorNode = anchor.getNode();
|
|
4825
|
+
nodesToMove = anchorNode.getNextSiblings().reverse();
|
|
4640
4826
|
currentElement = anchorNode.getParentOrThrow();
|
|
4641
4827
|
const isInline = currentElement.isInline();
|
|
4642
4828
|
const textContentLength = isInline ? currentElement.getTextContentSize() : anchorNode.getTextContentSize();
|
|
@@ -4652,8 +4838,6 @@ class RangeSelection {
|
|
|
4652
4838
|
}
|
|
4653
4839
|
|
|
4654
4840
|
if (anchorOffset !== textContentLength) {
|
|
4655
|
-
nodesToMove = anchorNode.getNextSiblings().reverse();
|
|
4656
|
-
|
|
4657
4841
|
if (!isInline || anchorOffset !== anchorNode.getTextContentSize()) {
|
|
4658
4842
|
const [, splitNode] = anchorNode.splitText(anchorOffset);
|
|
4659
4843
|
nodesToMove.push(splitNode);
|
|
@@ -4757,6 +4941,10 @@ class RangeSelection {
|
|
|
4757
4941
|
}
|
|
4758
4942
|
}
|
|
4759
4943
|
|
|
4944
|
+
getCharacterOffsets() {
|
|
4945
|
+
return getCharacterOffsets(this);
|
|
4946
|
+
}
|
|
4947
|
+
|
|
4760
4948
|
extract() {
|
|
4761
4949
|
const selectedNodes = this.getNodes();
|
|
4762
4950
|
const selectedNodesLength = selectedNodes.length;
|
|
@@ -4765,8 +4953,7 @@ class RangeSelection {
|
|
|
4765
4953
|
const focus = this.focus;
|
|
4766
4954
|
let firstNode = selectedNodes[0];
|
|
4767
4955
|
let lastNode = selectedNodes[lastIndex];
|
|
4768
|
-
const anchorOffset =
|
|
4769
|
-
const focusOffset = focus.getCharacterOffset();
|
|
4956
|
+
const [anchorOffset, focusOffset] = getCharacterOffsets(this);
|
|
4770
4957
|
|
|
4771
4958
|
if (selectedNodesLength === 0) {
|
|
4772
4959
|
return [];
|
|
@@ -4844,6 +5031,16 @@ class RangeSelection {
|
|
|
4844
5031
|
anchor.set(elementKey, offset, 'element');
|
|
4845
5032
|
}
|
|
4846
5033
|
|
|
5034
|
+
return;
|
|
5035
|
+
} else {
|
|
5036
|
+
const siblingKey = sibling.__key;
|
|
5037
|
+
const offset = isBackward ? sibling.getTextContent().length : 0;
|
|
5038
|
+
focus.set(siblingKey, offset, 'text');
|
|
5039
|
+
|
|
5040
|
+
if (collapse) {
|
|
5041
|
+
anchor.set(siblingKey, offset, 'text');
|
|
5042
|
+
}
|
|
5043
|
+
|
|
4847
5044
|
return;
|
|
4848
5045
|
}
|
|
4849
5046
|
}
|
|
@@ -4946,6 +5143,29 @@ function $isNodeSelection(x) {
|
|
|
4946
5143
|
return x instanceof NodeSelection;
|
|
4947
5144
|
}
|
|
4948
5145
|
|
|
5146
|
+
function getCharacterOffset(point) {
|
|
5147
|
+
const offset = point.offset;
|
|
5148
|
+
|
|
5149
|
+
if (point.type === 'text') {
|
|
5150
|
+
return offset;
|
|
5151
|
+
} // $FlowFixMe: cast
|
|
5152
|
+
|
|
5153
|
+
|
|
5154
|
+
const parent = point.getNode();
|
|
5155
|
+
return offset === parent.getChildrenSize() ? parent.getTextContent().length : 0;
|
|
5156
|
+
}
|
|
5157
|
+
|
|
5158
|
+
function getCharacterOffsets(selection) {
|
|
5159
|
+
const anchor = selection.anchor;
|
|
5160
|
+
const focus = selection.focus;
|
|
5161
|
+
|
|
5162
|
+
if (anchor.type === 'element' && focus.type === 'element' && anchor.key === focus.key && anchor.offset === focus.offset) {
|
|
5163
|
+
return [0, 0];
|
|
5164
|
+
}
|
|
5165
|
+
|
|
5166
|
+
return [getCharacterOffset(anchor), getCharacterOffset(focus)];
|
|
5167
|
+
}
|
|
5168
|
+
|
|
4949
5169
|
function $swapPoints(selection) {
|
|
4950
5170
|
const focus = selection.focus;
|
|
4951
5171
|
const anchor = selection.anchor;
|
|
@@ -5114,6 +5334,78 @@ function internalResolveSelectionPoint(dom, offset, lastPoint) {
|
|
|
5114
5334
|
return $createPoint(resolvedNode.__key, resolvedOffset, 'text');
|
|
5115
5335
|
}
|
|
5116
5336
|
|
|
5337
|
+
function resolveSelectionPointOnBoundary(point, isBackward, isCollapsed) {
|
|
5338
|
+
const offset = point.offset;
|
|
5339
|
+
const node = point.getNode();
|
|
5340
|
+
|
|
5341
|
+
if (offset === 0) {
|
|
5342
|
+
const prevSibling = node.getPreviousSibling();
|
|
5343
|
+
const parent = node.getParent();
|
|
5344
|
+
|
|
5345
|
+
if (!isBackward) {
|
|
5346
|
+
if ($isElementNode(prevSibling) && !isCollapsed && prevSibling.isInline()) {
|
|
5347
|
+
point.key = prevSibling.__key;
|
|
5348
|
+
point.offset = prevSibling.getChildrenSize(); // $FlowFixMe: intentional
|
|
5349
|
+
|
|
5350
|
+
point.type = 'element';
|
|
5351
|
+
} else if ($isTextNode(prevSibling) && !prevSibling.isInert()) {
|
|
5352
|
+
point.key = prevSibling.__key;
|
|
5353
|
+
point.offset = prevSibling.getTextContent().length;
|
|
5354
|
+
}
|
|
5355
|
+
} else if ((isCollapsed || !isBackward) && prevSibling === null && $isElementNode(parent) && parent.isInline()) {
|
|
5356
|
+
const parentSibling = parent.getPreviousSibling();
|
|
5357
|
+
|
|
5358
|
+
if ($isTextNode(parentSibling)) {
|
|
5359
|
+
point.key = parentSibling.__key;
|
|
5360
|
+
point.offset = parentSibling.getTextContent().length;
|
|
5361
|
+
}
|
|
5362
|
+
}
|
|
5363
|
+
} else if (offset === node.getTextContent().length) {
|
|
5364
|
+
const nextSibling = node.getNextSibling();
|
|
5365
|
+
const parent = node.getParent();
|
|
5366
|
+
|
|
5367
|
+
if (isBackward && $isElementNode(nextSibling) && nextSibling.isInline()) {
|
|
5368
|
+
point.key = nextSibling.__key;
|
|
5369
|
+
point.offset = 0; // $FlowFixMe: intentional
|
|
5370
|
+
|
|
5371
|
+
point.type = 'element';
|
|
5372
|
+
} else if ((isCollapsed || isBackward) && nextSibling === null && $isElementNode(parent) && parent.isInline()) {
|
|
5373
|
+
const parentSibling = parent.getNextSibling();
|
|
5374
|
+
|
|
5375
|
+
if ($isTextNode(parentSibling)) {
|
|
5376
|
+
point.key = parentSibling.__key;
|
|
5377
|
+
point.offset = 0;
|
|
5378
|
+
}
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
}
|
|
5382
|
+
|
|
5383
|
+
function normalizeSelectionPointsForBoundaries(anchor, focus, lastSelection) {
|
|
5384
|
+
if (anchor.type === 'text' && focus.type === 'text') {
|
|
5385
|
+
const isBackward = anchor.isBefore(focus);
|
|
5386
|
+
const isCollapsed = anchor.is(focus); // Attempt to normalize the offset to the previous sibling if we're at the
|
|
5387
|
+
// start of a text node and the sibling is a text node or inline element.
|
|
5388
|
+
|
|
5389
|
+
resolveSelectionPointOnBoundary(anchor, isBackward, isCollapsed);
|
|
5390
|
+
resolveSelectionPointOnBoundary(focus, !isBackward, isCollapsed);
|
|
5391
|
+
|
|
5392
|
+
if (isCollapsed) {
|
|
5393
|
+
focus.key = anchor.key;
|
|
5394
|
+
focus.offset = anchor.offset;
|
|
5395
|
+
focus.type = anchor.type;
|
|
5396
|
+
}
|
|
5397
|
+
|
|
5398
|
+
const editor = getActiveEditor();
|
|
5399
|
+
|
|
5400
|
+
if (editor.isComposing() && editor._compositionKey !== anchor.key && $isRangeSelection(lastSelection)) {
|
|
5401
|
+
const lastAnchor = lastSelection.anchor;
|
|
5402
|
+
const lastFocus = lastSelection.focus;
|
|
5403
|
+
$setPointValues(anchor, lastAnchor.key, lastAnchor.offset, lastAnchor.type);
|
|
5404
|
+
$setPointValues(focus, lastFocus.key, lastFocus.offset, lastFocus.type);
|
|
5405
|
+
}
|
|
5406
|
+
}
|
|
5407
|
+
}
|
|
5408
|
+
|
|
5117
5409
|
function internalResolveSelectionPoints(anchorDOM, anchorOffset, focusDOM, focusOffset, editor, lastSelection) {
|
|
5118
5410
|
if (anchorDOM === null || focusDOM === null || !isSelectionWithinEditor(editor, anchorDOM, focusDOM)) {
|
|
5119
5411
|
return null;
|
|
@@ -5129,48 +5421,10 @@ function internalResolveSelectionPoints(anchorDOM, anchorOffset, focusDOM, focus
|
|
|
5129
5421
|
|
|
5130
5422
|
if (resolvedFocusPoint === null) {
|
|
5131
5423
|
return null;
|
|
5132
|
-
}
|
|
5424
|
+
} // Handle normalization of selection when it is at the boundaries.
|
|
5133
5425
|
|
|
5134
|
-
if (resolvedAnchorPoint.type === 'text' && resolvedFocusPoint.type === 'text') {
|
|
5135
|
-
const resolvedAnchorNode = resolvedAnchorPoint.getNode();
|
|
5136
|
-
const resolvedFocusNode = resolvedFocusPoint.getNode(); // Handle normalization of selection when it is at the boundaries.
|
|
5137
|
-
|
|
5138
|
-
const textContentSize = resolvedAnchorNode.getTextContentSize();
|
|
5139
|
-
const resolvedAnchorOffset = resolvedAnchorPoint.offset;
|
|
5140
|
-
const resolvedFocusOffset = resolvedFocusPoint.offset;
|
|
5141
|
-
|
|
5142
|
-
if (resolvedAnchorNode === resolvedFocusNode && resolvedAnchorOffset === resolvedFocusOffset) {
|
|
5143
|
-
if (anchorOffset === 0) {
|
|
5144
|
-
const prevSibling = resolvedAnchorNode.getPreviousSibling();
|
|
5145
|
-
|
|
5146
|
-
if ($isTextNode(prevSibling) && !prevSibling.isInert()) {
|
|
5147
|
-
const offset = prevSibling.getTextContentSize();
|
|
5148
|
-
const key = prevSibling.__key;
|
|
5149
|
-
resolvedAnchorPoint.key = key;
|
|
5150
|
-
resolvedFocusPoint.key = key;
|
|
5151
|
-
resolvedAnchorPoint.offset = offset;
|
|
5152
|
-
resolvedFocusPoint.offset = offset;
|
|
5153
|
-
}
|
|
5154
|
-
}
|
|
5155
|
-
} else {
|
|
5156
|
-
if (resolvedAnchorOffset === textContentSize) {
|
|
5157
|
-
const nextSibling = resolvedAnchorNode.getNextSibling();
|
|
5158
|
-
|
|
5159
|
-
if ($isTextNode(nextSibling) && !nextSibling.isInert()) {
|
|
5160
|
-
resolvedAnchorPoint.key = nextSibling.__key;
|
|
5161
|
-
resolvedAnchorPoint.offset = 0;
|
|
5162
|
-
}
|
|
5163
|
-
}
|
|
5164
|
-
}
|
|
5165
|
-
|
|
5166
|
-
if (editor.isComposing() && editor._compositionKey !== resolvedAnchorPoint.key && $isRangeSelection(lastSelection)) {
|
|
5167
|
-
const lastAnchor = lastSelection.anchor;
|
|
5168
|
-
const lastFocus = lastSelection.focus;
|
|
5169
|
-
$setPointValues(resolvedAnchorPoint, lastAnchor.key, lastAnchor.offset, lastAnchor.type);
|
|
5170
|
-
$setPointValues(resolvedFocusPoint, lastFocus.key, lastFocus.offset, lastFocus.type);
|
|
5171
|
-
}
|
|
5172
|
-
}
|
|
5173
5426
|
|
|
5427
|
+
normalizeSelectionPointsForBoundaries(resolvedAnchorPoint, resolvedFocusPoint, lastSelection);
|
|
5174
5428
|
return [resolvedAnchorPoint, resolvedFocusPoint];
|
|
5175
5429
|
} // This is used to make a selection when the existing
|
|
5176
5430
|
// selection is null, i.e. forcing selection on the editor
|
|
@@ -5193,7 +5447,9 @@ function $createEmptyObjectSelection() {
|
|
|
5193
5447
|
return new NodeSelection(new Set());
|
|
5194
5448
|
}
|
|
5195
5449
|
function $createEmptyGridSelection() {
|
|
5196
|
-
|
|
5450
|
+
const anchor = $createPoint('root', 0, 'element');
|
|
5451
|
+
const focus = $createPoint('root', 0, 'element');
|
|
5452
|
+
return new GridSelection('root', anchor, focus);
|
|
5197
5453
|
}
|
|
5198
5454
|
|
|
5199
5455
|
function getActiveEventType() {
|
|
@@ -5272,7 +5528,7 @@ function internalCreateSelectionFromParse(parsedSelection) {
|
|
|
5272
5528
|
} else if (parsedSelection.type === 'node') {
|
|
5273
5529
|
return new NodeSelection(new Set(parsedSelection.nodes));
|
|
5274
5530
|
} else if (parsedSelection.type === 'grid') {
|
|
5275
|
-
return new GridSelection(parsedSelection.gridKey, parsedSelection.
|
|
5531
|
+
return new GridSelection(parsedSelection.gridKey, $createPoint(parsedSelection.anchor.key, parsedSelection.anchor.offset, parsedSelection.anchor.type), $createPoint(parsedSelection.focus.key, parsedSelection.focus.offset, parsedSelection.focus.type));
|
|
5276
5532
|
}
|
|
5277
5533
|
}
|
|
5278
5534
|
|
|
@@ -5479,7 +5735,7 @@ function adjustPointOffsetForMergedSibling(point, isBefore, key, target, textLen
|
|
|
5479
5735
|
*
|
|
5480
5736
|
*
|
|
5481
5737
|
*/
|
|
5482
|
-
function removeNode(nodeToRemove, restoreSelection) {
|
|
5738
|
+
function removeNode(nodeToRemove, restoreSelection, preserveEmptyParent) {
|
|
5483
5739
|
errorOnReadOnly();
|
|
5484
5740
|
const key = nodeToRemove.__key;
|
|
5485
5741
|
const parent = nodeToRemove.getParent();
|
|
@@ -5525,7 +5781,7 @@ function removeNode(nodeToRemove, restoreSelection) {
|
|
|
5525
5781
|
$updateElementSelectionOnCreateDeleteNode(selection, parent, index, -1);
|
|
5526
5782
|
}
|
|
5527
5783
|
|
|
5528
|
-
if (parent !== null && !$isRootNode(parent) && !parent.canBeEmpty() && parent.isEmpty()) {
|
|
5784
|
+
if (!preserveEmptyParent && parent !== null && !$isRootNode(parent) && !parent.canBeEmpty() && parent.isEmpty()) {
|
|
5529
5785
|
removeNode(parent, restoreSelection);
|
|
5530
5786
|
}
|
|
5531
5787
|
|
|
@@ -6016,10 +6272,12 @@ class LexicalNode {
|
|
|
6016
6272
|
mutableNode.__format = latestNode.__format;
|
|
6017
6273
|
mutableNode.__dir = latestNode.__dir;
|
|
6018
6274
|
} else if ($isTextNode(latestNode) && $isTextNode(mutableNode)) {
|
|
6275
|
+
const marks = latestNode.__marks;
|
|
6019
6276
|
mutableNode.__format = latestNode.__format;
|
|
6020
6277
|
mutableNode.__style = latestNode.__style;
|
|
6021
6278
|
mutableNode.__mode = latestNode.__mode;
|
|
6022
6279
|
mutableNode.__detail = latestNode.__detail;
|
|
6280
|
+
mutableNode.__marks = marks === null ? marks : Array.from(marks);
|
|
6023
6281
|
}
|
|
6024
6282
|
|
|
6025
6283
|
cloneNotNeeded.add(key);
|
|
@@ -6040,23 +6298,20 @@ class LexicalNode {
|
|
|
6040
6298
|
getTextContentSize(includeInert, includeDirectionless) {
|
|
6041
6299
|
return this.getTextContent(includeInert, includeDirectionless).length;
|
|
6042
6300
|
} // View
|
|
6043
|
-
// $FlowFixMe: Revise typings for EditorContext
|
|
6044
6301
|
|
|
6045
6302
|
|
|
6046
6303
|
createDOM(config, editor) {
|
|
6047
6304
|
{
|
|
6048
6305
|
throw Error(`createDOM: base method not extended`);
|
|
6049
6306
|
}
|
|
6050
|
-
}
|
|
6051
|
-
|
|
6307
|
+
}
|
|
6052
6308
|
|
|
6053
6309
|
updateDOM( // $FlowFixMe: TODO
|
|
6054
6310
|
prevNode, dom, config) {
|
|
6055
6311
|
{
|
|
6056
6312
|
throw Error(`updateDOM: base method not extended`);
|
|
6057
6313
|
}
|
|
6058
|
-
}
|
|
6059
|
-
|
|
6314
|
+
}
|
|
6060
6315
|
|
|
6061
6316
|
exportDOM(editor) {
|
|
6062
6317
|
if ($isDecoratorNode(this)) {
|
|
@@ -6077,9 +6332,9 @@ class LexicalNode {
|
|
|
6077
6332
|
} // Setters and mutators
|
|
6078
6333
|
|
|
6079
6334
|
|
|
6080
|
-
remove() {
|
|
6335
|
+
remove(preserveEmptyParent) {
|
|
6081
6336
|
errorOnReadOnly();
|
|
6082
|
-
removeNode(this, true);
|
|
6337
|
+
removeNode(this, true, preserveEmptyParent);
|
|
6083
6338
|
}
|
|
6084
6339
|
|
|
6085
6340
|
replace(replaceWith) {
|
|
@@ -6373,6 +6628,12 @@ class ElementNode extends LexicalNode {
|
|
|
6373
6628
|
return dirtyElements !== null && dirtyElements.has(this.__key);
|
|
6374
6629
|
}
|
|
6375
6630
|
|
|
6631
|
+
isLastChild() {
|
|
6632
|
+
const self = this.getLatest();
|
|
6633
|
+
const parent = self.getParentOrThrow();
|
|
6634
|
+
return parent.getLastChild() === self;
|
|
6635
|
+
}
|
|
6636
|
+
|
|
6376
6637
|
getAllTextNodes(includeInert) {
|
|
6377
6638
|
const textNodes = [];
|
|
6378
6639
|
const self = this.getLatest();
|
|
@@ -6507,7 +6768,7 @@ class ElementNode extends LexicalNode {
|
|
|
6507
6768
|
textContent += child.getTextContent(includeInert, includeDirectionless);
|
|
6508
6769
|
|
|
6509
6770
|
if ($isElementNode(child) && i !== childrenLength - 1 && !child.isInline()) {
|
|
6510
|
-
textContent +=
|
|
6771
|
+
textContent += DOUBLE_LINE_BREAK;
|
|
6511
6772
|
}
|
|
6512
6773
|
}
|
|
6513
6774
|
|
|
@@ -6736,6 +6997,10 @@ class ElementNode extends LexicalNode {
|
|
|
6736
6997
|
return false;
|
|
6737
6998
|
}
|
|
6738
6999
|
|
|
7000
|
+
canIndent() {
|
|
7001
|
+
return true;
|
|
7002
|
+
}
|
|
7003
|
+
|
|
6739
7004
|
collapseAtStart(selection) {
|
|
6740
7005
|
return false;
|
|
6741
7006
|
}
|
|
@@ -6956,8 +7221,16 @@ class EditorState {
|
|
|
6956
7221
|
nodes: Array.from(selection._nodes),
|
|
6957
7222
|
type: 'node'
|
|
6958
7223
|
} : $isGridSelection(selection) ? {
|
|
6959
|
-
|
|
6960
|
-
|
|
7224
|
+
anchor: {
|
|
7225
|
+
key: selection.anchor.key,
|
|
7226
|
+
offset: selection.anchor.offset,
|
|
7227
|
+
type: selection.anchor.type
|
|
7228
|
+
},
|
|
7229
|
+
focus: {
|
|
7230
|
+
key: selection.focus.key,
|
|
7231
|
+
offset: selection.focus.offset,
|
|
7232
|
+
type: selection.focus.type
|
|
7233
|
+
},
|
|
6961
7234
|
gridKey: selection.gridKey,
|
|
6962
7235
|
type: 'grid'
|
|
6963
7236
|
} : null
|
|
@@ -7023,6 +7296,44 @@ function $isLineBreakNode(node) {
|
|
|
7023
7296
|
return node instanceof LineBreakNode;
|
|
7024
7297
|
}
|
|
7025
7298
|
|
|
7299
|
+
/**
|
|
7300
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
7301
|
+
*
|
|
7302
|
+
* This source code is licensed under the MIT license found in the
|
|
7303
|
+
* LICENSE file in the root directory of this source tree.
|
|
7304
|
+
*
|
|
7305
|
+
*
|
|
7306
|
+
*/
|
|
7307
|
+
function simpleDiffWithCursor(a, b, cursor) {
|
|
7308
|
+
const aLength = a.length;
|
|
7309
|
+
const bLength = b.length;
|
|
7310
|
+
let left = 0; // number of same characters counting from left
|
|
7311
|
+
|
|
7312
|
+
let right = 0; // number of same characters counting from right
|
|
7313
|
+
// Iterate left to the right until we find a changed character
|
|
7314
|
+
// First iteration considers the current cursor position
|
|
7315
|
+
|
|
7316
|
+
while (left < aLength && left < bLength && a[left] === b[left] && left < cursor) {
|
|
7317
|
+
left++;
|
|
7318
|
+
} // Iterate right to the left until we find a changed character
|
|
7319
|
+
|
|
7320
|
+
|
|
7321
|
+
while (right + left < aLength && right + left < bLength && a[aLength - right - 1] === b[bLength - right - 1]) {
|
|
7322
|
+
right++;
|
|
7323
|
+
} // Try to iterate left further to the right without caring about the current cursor position
|
|
7324
|
+
|
|
7325
|
+
|
|
7326
|
+
while (right + left < aLength && right + left < bLength && a[left] === b[left]) {
|
|
7327
|
+
left++;
|
|
7328
|
+
}
|
|
7329
|
+
|
|
7330
|
+
return {
|
|
7331
|
+
index: left,
|
|
7332
|
+
insert: b.slice(left, bLength - right),
|
|
7333
|
+
remove: aLength - left - right
|
|
7334
|
+
};
|
|
7335
|
+
}
|
|
7336
|
+
|
|
7026
7337
|
/**
|
|
7027
7338
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
7028
7339
|
*
|
|
@@ -7037,6 +7348,14 @@ function getElementOuterTag(node, format) {
|
|
|
7037
7348
|
return 'code';
|
|
7038
7349
|
}
|
|
7039
7350
|
|
|
7351
|
+
if (format & IS_SUBSCRIPT) {
|
|
7352
|
+
return 'sub';
|
|
7353
|
+
}
|
|
7354
|
+
|
|
7355
|
+
if (format & IS_SUPERSCRIPT) {
|
|
7356
|
+
return 'sup';
|
|
7357
|
+
}
|
|
7358
|
+
|
|
7040
7359
|
return null;
|
|
7041
7360
|
}
|
|
7042
7361
|
|
|
@@ -7163,6 +7482,42 @@ function createTextInnerDOM(innerDOM, node, innerTag, format, text, config) {
|
|
|
7163
7482
|
}
|
|
7164
7483
|
}
|
|
7165
7484
|
|
|
7485
|
+
function updateTextMarks(textNode, marks, offset, delCount, size) {
|
|
7486
|
+
for (let i = 0; i < marks.length; i++) {
|
|
7487
|
+
const {
|
|
7488
|
+
id,
|
|
7489
|
+
start,
|
|
7490
|
+
end
|
|
7491
|
+
} = marks[i];
|
|
7492
|
+
let newStart = start;
|
|
7493
|
+
let newEnd = end;
|
|
7494
|
+
|
|
7495
|
+
if (newStart !== null && newStart >= offset) {
|
|
7496
|
+
if (offset + delCount >= newStart) {
|
|
7497
|
+
newStart = offset + delCount;
|
|
7498
|
+
}
|
|
7499
|
+
|
|
7500
|
+
newStart += size - delCount;
|
|
7501
|
+
}
|
|
7502
|
+
|
|
7503
|
+
if (newEnd !== null && newEnd >= offset) {
|
|
7504
|
+
if (offset + delCount >= newEnd) {
|
|
7505
|
+
newEnd = offset;
|
|
7506
|
+
}
|
|
7507
|
+
|
|
7508
|
+
newEnd += size - delCount;
|
|
7509
|
+
}
|
|
7510
|
+
|
|
7511
|
+
if (newStart !== start || newEnd !== end) {
|
|
7512
|
+
if (newStart === null && newEnd === null || newStart !== null && newEnd !== null && newStart >= newEnd) {
|
|
7513
|
+
textNode.deleteMark(id);
|
|
7514
|
+
} else {
|
|
7515
|
+
textNode.setMark(id, newStart, newEnd);
|
|
7516
|
+
}
|
|
7517
|
+
}
|
|
7518
|
+
}
|
|
7519
|
+
}
|
|
7520
|
+
|
|
7166
7521
|
class TextNode extends LexicalNode {
|
|
7167
7522
|
static getType() {
|
|
7168
7523
|
return 'text';
|
|
@@ -7179,6 +7534,7 @@ class TextNode extends LexicalNode {
|
|
|
7179
7534
|
this.__style = '';
|
|
7180
7535
|
this.__mode = 0;
|
|
7181
7536
|
this.__detail = 0;
|
|
7537
|
+
this.__marks = null;
|
|
7182
7538
|
}
|
|
7183
7539
|
|
|
7184
7540
|
getFormat() {
|
|
@@ -7186,6 +7542,23 @@ class TextNode extends LexicalNode {
|
|
|
7186
7542
|
return self.__format;
|
|
7187
7543
|
}
|
|
7188
7544
|
|
|
7545
|
+
getMark(id) {
|
|
7546
|
+
const self = this.getLatest();
|
|
7547
|
+
const marks = self.__marks;
|
|
7548
|
+
|
|
7549
|
+
if (marks !== null) {
|
|
7550
|
+
for (let i = 0; i < marks.length; i++) {
|
|
7551
|
+
const mark = marks[i];
|
|
7552
|
+
|
|
7553
|
+
if (mark.id === id) {
|
|
7554
|
+
return mark;
|
|
7555
|
+
}
|
|
7556
|
+
}
|
|
7557
|
+
}
|
|
7558
|
+
|
|
7559
|
+
return null;
|
|
7560
|
+
}
|
|
7561
|
+
|
|
7189
7562
|
getStyle() {
|
|
7190
7563
|
const self = this.getLatest();
|
|
7191
7564
|
return self.__style;
|
|
@@ -7239,7 +7612,6 @@ class TextNode extends LexicalNode {
|
|
|
7239
7612
|
const format = self.__format;
|
|
7240
7613
|
return toggleTextFormatType(format, type, alignWithFormat);
|
|
7241
7614
|
} // View
|
|
7242
|
-
// $FlowFixMe: Revise typings for EditorContext
|
|
7243
7615
|
|
|
7244
7616
|
|
|
7245
7617
|
createDOM(config) {
|
|
@@ -7264,8 +7636,7 @@ class TextNode extends LexicalNode {
|
|
|
7264
7636
|
}
|
|
7265
7637
|
|
|
7266
7638
|
return dom;
|
|
7267
|
-
}
|
|
7268
|
-
|
|
7639
|
+
}
|
|
7269
7640
|
|
|
7270
7641
|
updateDOM(prevNode, dom, config) {
|
|
7271
7642
|
const nextText = this.__text;
|
|
@@ -7401,6 +7772,64 @@ class TextNode extends LexicalNode {
|
|
|
7401
7772
|
return self;
|
|
7402
7773
|
}
|
|
7403
7774
|
|
|
7775
|
+
setMark(id, start, end) {
|
|
7776
|
+
errorOnReadOnly();
|
|
7777
|
+
const self = this.getWritable();
|
|
7778
|
+
let marks = self.__marks;
|
|
7779
|
+
let found = false;
|
|
7780
|
+
|
|
7781
|
+
if (marks === null) {
|
|
7782
|
+
self.__marks = marks = [];
|
|
7783
|
+
}
|
|
7784
|
+
|
|
7785
|
+
const nextMark = {
|
|
7786
|
+
end,
|
|
7787
|
+
id,
|
|
7788
|
+
start
|
|
7789
|
+
};
|
|
7790
|
+
|
|
7791
|
+
{
|
|
7792
|
+
Object.freeze(nextMark);
|
|
7793
|
+
}
|
|
7794
|
+
|
|
7795
|
+
for (let i = 0; i < marks.length; i++) {
|
|
7796
|
+
const prevMark = marks[i];
|
|
7797
|
+
|
|
7798
|
+
if (prevMark.id === id) {
|
|
7799
|
+
found = true;
|
|
7800
|
+
marks.splice(i, 1, nextMark);
|
|
7801
|
+
break;
|
|
7802
|
+
}
|
|
7803
|
+
}
|
|
7804
|
+
|
|
7805
|
+
if (!found) {
|
|
7806
|
+
marks.push(nextMark);
|
|
7807
|
+
}
|
|
7808
|
+
}
|
|
7809
|
+
|
|
7810
|
+
deleteMark(id) {
|
|
7811
|
+
errorOnReadOnly();
|
|
7812
|
+
const self = this.getWritable();
|
|
7813
|
+
const marks = self.__marks;
|
|
7814
|
+
|
|
7815
|
+
if (marks === null) {
|
|
7816
|
+
return;
|
|
7817
|
+
}
|
|
7818
|
+
|
|
7819
|
+
for (let i = 0; i < marks.length; i++) {
|
|
7820
|
+
const prevMark = marks[i];
|
|
7821
|
+
|
|
7822
|
+
if (prevMark.id === id) {
|
|
7823
|
+
marks.splice(i, 1);
|
|
7824
|
+
break;
|
|
7825
|
+
}
|
|
7826
|
+
}
|
|
7827
|
+
|
|
7828
|
+
if (marks.length === 0) {
|
|
7829
|
+
self.__marks = null;
|
|
7830
|
+
}
|
|
7831
|
+
}
|
|
7832
|
+
|
|
7404
7833
|
setMode(type) {
|
|
7405
7834
|
errorOnReadOnly();
|
|
7406
7835
|
const mode = TEXT_MODE_TO_TYPE[type];
|
|
@@ -7412,7 +7841,26 @@ class TextNode extends LexicalNode {
|
|
|
7412
7841
|
setTextContent(text) {
|
|
7413
7842
|
errorOnReadOnly();
|
|
7414
7843
|
const writableSelf = this.getWritable();
|
|
7415
|
-
|
|
7844
|
+
const marks = writableSelf.__marks;
|
|
7845
|
+
|
|
7846
|
+
if (marks !== null) {
|
|
7847
|
+
const selection = $getSelection();
|
|
7848
|
+
let cursorOffset = text.length;
|
|
7849
|
+
|
|
7850
|
+
if ($isRangeSelection(selection) && selection.isCollapsed()) {
|
|
7851
|
+
const anchor = selection.anchor;
|
|
7852
|
+
|
|
7853
|
+
if (anchor.key === this.__key) {
|
|
7854
|
+
cursorOffset = anchor.offset;
|
|
7855
|
+
}
|
|
7856
|
+
}
|
|
7857
|
+
|
|
7858
|
+
const diff = simpleDiffWithCursor(writableSelf.__text, text, cursorOffset);
|
|
7859
|
+
this.spliceText(diff.index, diff.remove, diff.insert);
|
|
7860
|
+
} else {
|
|
7861
|
+
writableSelf.__text = text;
|
|
7862
|
+
}
|
|
7863
|
+
|
|
7416
7864
|
return writableSelf;
|
|
7417
7865
|
}
|
|
7418
7866
|
|
|
@@ -7477,7 +7925,14 @@ class TextNode extends LexicalNode {
|
|
|
7477
7925
|
}
|
|
7478
7926
|
|
|
7479
7927
|
const updatedText = text.slice(0, index) + newText + text.slice(index + delCount);
|
|
7480
|
-
|
|
7928
|
+
const marks = writableSelf.__marks;
|
|
7929
|
+
|
|
7930
|
+
if (marks !== null) {
|
|
7931
|
+
updateTextMarks(writableSelf, marks, offset, delCount, handledTextLength);
|
|
7932
|
+
}
|
|
7933
|
+
|
|
7934
|
+
writableSelf.__text = updatedText;
|
|
7935
|
+
return writableSelf;
|
|
7481
7936
|
}
|
|
7482
7937
|
|
|
7483
7938
|
canInsertTextBefore() {
|
|
@@ -7527,6 +7982,7 @@ class TextNode extends LexicalNode {
|
|
|
7527
7982
|
const format = self.getFormat();
|
|
7528
7983
|
const style = self.getStyle();
|
|
7529
7984
|
const detail = self.__detail;
|
|
7985
|
+
const marks = self.__marks;
|
|
7530
7986
|
let hasReplacedSelf = false;
|
|
7531
7987
|
|
|
7532
7988
|
if (self.isSegmented()) {
|
|
@@ -7536,6 +7992,7 @@ class TextNode extends LexicalNode {
|
|
|
7536
7992
|
writableNode.__format = format;
|
|
7537
7993
|
writableNode.__style = style;
|
|
7538
7994
|
writableNode.__detail = detail;
|
|
7995
|
+
writableNode.__marks = marks;
|
|
7539
7996
|
hasReplacedSelf = true;
|
|
7540
7997
|
} else {
|
|
7541
7998
|
// For the first part, update the existing node
|
|
@@ -7583,6 +8040,46 @@ class TextNode extends LexicalNode {
|
|
|
7583
8040
|
textSize = nextTextSize;
|
|
7584
8041
|
sibling.__parent = parentKey;
|
|
7585
8042
|
splitNodes.push(sibling);
|
|
8043
|
+
}
|
|
8044
|
+
|
|
8045
|
+
if (marks !== null) {
|
|
8046
|
+
for (let i = 0; i < marks.length; i++) {
|
|
8047
|
+
const {
|
|
8048
|
+
id,
|
|
8049
|
+
start,
|
|
8050
|
+
end
|
|
8051
|
+
} = marks[i];
|
|
8052
|
+
let foundStart = false;
|
|
8053
|
+
let foundEnd = false;
|
|
8054
|
+
let partSize = 0;
|
|
8055
|
+
|
|
8056
|
+
for (let s = 0; s < partsLength; s++) {
|
|
8057
|
+
const textNode = splitNodes[s];
|
|
8058
|
+
const nextPartSize = partSize + parts[s].length;
|
|
8059
|
+
const nextStart = !foundStart && start !== null && nextPartSize > start - (start === 0 ? 1 : 0) ? start - partSize : null;
|
|
8060
|
+
const nextEnd = !foundEnd && end !== null && nextPartSize >= end ? end - partSize : null;
|
|
8061
|
+
|
|
8062
|
+
if (nextStart !== null || nextEnd !== null) {
|
|
8063
|
+
if (nextStart !== null) {
|
|
8064
|
+
foundStart = true;
|
|
8065
|
+
}
|
|
8066
|
+
|
|
8067
|
+
if (nextEnd !== null) {
|
|
8068
|
+
foundEnd = true;
|
|
8069
|
+
}
|
|
8070
|
+
|
|
8071
|
+
textNode.setMark(id, nextStart, nextEnd);
|
|
8072
|
+
|
|
8073
|
+
if (foundStart && foundEnd) {
|
|
8074
|
+
break;
|
|
8075
|
+
}
|
|
8076
|
+
} else {
|
|
8077
|
+
textNode.deleteMark(id);
|
|
8078
|
+
}
|
|
8079
|
+
|
|
8080
|
+
partSize = nextPartSize;
|
|
8081
|
+
}
|
|
8082
|
+
}
|
|
7586
8083
|
} // Insert the nodes into the parent's children
|
|
7587
8084
|
|
|
7588
8085
|
|
|
@@ -7642,10 +8139,20 @@ class TextNode extends LexicalNode {
|
|
|
7642
8139
|
}
|
|
7643
8140
|
}
|
|
7644
8141
|
|
|
7645
|
-
const
|
|
8142
|
+
const targetText = target.__text;
|
|
8143
|
+
const targetTextLength = targetText.length;
|
|
8144
|
+
const newText = isBefore ? targetText + text : text + targetText;
|
|
7646
8145
|
this.setTextContent(newText);
|
|
8146
|
+
const writableSelf = this.getWritable();
|
|
8147
|
+
|
|
8148
|
+
const marks = target.getLatest().__marks;
|
|
8149
|
+
|
|
8150
|
+
if (marks !== null) {
|
|
8151
|
+
updateTextMarks(writableSelf, marks, isBefore ? targetTextLength : 0, 0, textLength);
|
|
8152
|
+
}
|
|
8153
|
+
|
|
7647
8154
|
target.remove();
|
|
7648
|
-
return
|
|
8155
|
+
return writableSelf;
|
|
7649
8156
|
}
|
|
7650
8157
|
|
|
7651
8158
|
isTextEntity() {
|
|
@@ -7919,7 +8426,6 @@ function createEditor(editorConfig) {
|
|
|
7919
8426
|
const config = editorConfig || {};
|
|
7920
8427
|
const namespace = config.namespace || createUID();
|
|
7921
8428
|
const theme = config.theme || {};
|
|
7922
|
-
const context = config.context || {};
|
|
7923
8429
|
const parentEditor = config.parentEditor || null;
|
|
7924
8430
|
const disableEvents = config.disableEvents || false;
|
|
7925
8431
|
const editorState = createEmptyEditorState();
|
|
@@ -7941,8 +8447,6 @@ function createEditor(editorConfig) {
|
|
|
7941
8447
|
|
|
7942
8448
|
|
|
7943
8449
|
const editor = new LexicalEditor(editorState, parentEditor, registeredNodes, {
|
|
7944
|
-
// $FlowFixMe: we use our internal type to simpify the generics
|
|
7945
|
-
context,
|
|
7946
8450
|
disableEvents,
|
|
7947
8451
|
namespace,
|
|
7948
8452
|
theme
|
|
@@ -8301,7 +8805,7 @@ class LexicalEditor {
|
|
|
8301
8805
|
*
|
|
8302
8806
|
*
|
|
8303
8807
|
*/
|
|
8304
|
-
const VERSION = '0.2.
|
|
8808
|
+
const VERSION = '0.2.5';
|
|
8305
8809
|
|
|
8306
8810
|
/**
|
|
8307
8811
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -8347,54 +8851,6 @@ function $isGridRowNode(node) {
|
|
|
8347
8851
|
return node instanceof GridRowNode;
|
|
8348
8852
|
}
|
|
8349
8853
|
|
|
8350
|
-
/**
|
|
8351
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
8352
|
-
*
|
|
8353
|
-
* This source code is licensed under the MIT license found in the
|
|
8354
|
-
* LICENSE file in the root directory of this source tree.
|
|
8355
|
-
*
|
|
8356
|
-
*
|
|
8357
|
-
*/
|
|
8358
|
-
function createCommand() {
|
|
8359
|
-
// $FlowFixMe: avoid freezing the object for perf reasons
|
|
8360
|
-
return {};
|
|
8361
|
-
}
|
|
8362
|
-
const SELECTION_CHANGE_COMMAND = createCommand();
|
|
8363
|
-
const CLICK_COMMAND = createCommand();
|
|
8364
|
-
const DELETE_CHARACTER_COMMAND = createCommand();
|
|
8365
|
-
const INSERT_LINE_BREAK_COMMAND = createCommand();
|
|
8366
|
-
const INSERT_PARAGRAPH_COMMAND = createCommand();
|
|
8367
|
-
const INSERT_TEXT_COMMAND = createCommand();
|
|
8368
|
-
const PASTE_COMMAND = createCommand();
|
|
8369
|
-
const REMOVE_TEXT_COMMAND = createCommand();
|
|
8370
|
-
const DELETE_WORD_COMMAND = createCommand();
|
|
8371
|
-
const DELETE_LINE_COMMAND = createCommand();
|
|
8372
|
-
const FORMAT_TEXT_COMMAND = createCommand();
|
|
8373
|
-
const UNDO_COMMAND = createCommand();
|
|
8374
|
-
const REDO_COMMAND = createCommand();
|
|
8375
|
-
const KEY_ARROW_RIGHT_COMMAND = createCommand();
|
|
8376
|
-
const KEY_ARROW_LEFT_COMMAND = createCommand();
|
|
8377
|
-
const KEY_ARROW_UP_COMMAND = createCommand();
|
|
8378
|
-
const KEY_ARROW_DOWN_COMMAND = createCommand();
|
|
8379
|
-
const KEY_ENTER_COMMAND = createCommand();
|
|
8380
|
-
const KEY_BACKSPACE_COMMAND = createCommand();
|
|
8381
|
-
const KEY_ESCAPE_COMMAND = createCommand();
|
|
8382
|
-
const KEY_DELETE_COMMAND = createCommand();
|
|
8383
|
-
const KEY_TAB_COMMAND = createCommand();
|
|
8384
|
-
const INDENT_CONTENT_COMMAND = createCommand();
|
|
8385
|
-
const OUTDENT_CONTENT_COMMAND = createCommand();
|
|
8386
|
-
const DROP_COMMAND = createCommand();
|
|
8387
|
-
const FORMAT_ELEMENT_COMMAND = createCommand();
|
|
8388
|
-
const DRAGSTART_COMMAND = createCommand();
|
|
8389
|
-
const COPY_COMMAND = createCommand();
|
|
8390
|
-
const CUT_COMMAND = createCommand();
|
|
8391
|
-
const CLEAR_EDITOR_COMMAND = createCommand();
|
|
8392
|
-
const CLEAR_HISTORY_COMMAND = createCommand();
|
|
8393
|
-
const CAN_REDO_COMMAND = createCommand();
|
|
8394
|
-
const CAN_UNDO_COMMAND = createCommand();
|
|
8395
|
-
const FOCUS_COMMAND = createCommand();
|
|
8396
|
-
const BLUR_COMMAND = createCommand();
|
|
8397
|
-
|
|
8398
8854
|
exports.$createGridSelection = $createEmptyGridSelection;
|
|
8399
8855
|
exports.$createLineBreakNode = $createLineBreakNode;
|
|
8400
8856
|
exports.$createNodeFromParse = $createNodeFromParse;
|
|
@@ -8440,6 +8896,7 @@ exports.CUT_COMMAND = CUT_COMMAND;
|
|
|
8440
8896
|
exports.DELETE_CHARACTER_COMMAND = DELETE_CHARACTER_COMMAND;
|
|
8441
8897
|
exports.DELETE_LINE_COMMAND = DELETE_LINE_COMMAND;
|
|
8442
8898
|
exports.DELETE_WORD_COMMAND = DELETE_WORD_COMMAND;
|
|
8899
|
+
exports.DRAGEND_COMMAND = DRAGEND_COMMAND;
|
|
8443
8900
|
exports.DRAGSTART_COMMAND = DRAGSTART_COMMAND;
|
|
8444
8901
|
exports.DROP_COMMAND = DROP_COMMAND;
|
|
8445
8902
|
exports.DecoratorNode = DecoratorNode;
|
|
@@ -8462,6 +8919,7 @@ exports.KEY_BACKSPACE_COMMAND = KEY_BACKSPACE_COMMAND;
|
|
|
8462
8919
|
exports.KEY_DELETE_COMMAND = KEY_DELETE_COMMAND;
|
|
8463
8920
|
exports.KEY_ENTER_COMMAND = KEY_ENTER_COMMAND;
|
|
8464
8921
|
exports.KEY_ESCAPE_COMMAND = KEY_ESCAPE_COMMAND;
|
|
8922
|
+
exports.KEY_MODIFIER_COMMAND = KEY_MODIFIER_COMMAND;
|
|
8465
8923
|
exports.KEY_TAB_COMMAND = KEY_TAB_COMMAND;
|
|
8466
8924
|
exports.OUTDENT_CONTENT_COMMAND = OUTDENT_CONTENT_COMMAND;
|
|
8467
8925
|
exports.PASTE_COMMAND = PASTE_COMMAND;
|