lexical 0.38.3-nightly.20251106.0 → 0.38.3-nightly.20251107.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Lexical.dev.js +335 -302
- package/Lexical.dev.mjs +332 -303
- package/Lexical.js.flow +4 -4
- package/Lexical.mjs +4 -0
- package/Lexical.node.mjs +4 -0
- package/Lexical.prod.js +1 -1
- package/Lexical.prod.mjs +1 -1
- package/LexicalCommands.d.ts +4 -0
- package/LexicalEvents.d.ts +1 -0
- package/index.d.ts +1 -1
- package/package.json +1 -1
package/Lexical.dev.js
CHANGED
|
@@ -1929,6 +1929,10 @@ function createCommand(type) {
|
|
|
1929
1929
|
const SELECTION_CHANGE_COMMAND = createCommand('SELECTION_CHANGE_COMMAND');
|
|
1930
1930
|
const SELECTION_INSERT_CLIPBOARD_NODES_COMMAND = createCommand('SELECTION_INSERT_CLIPBOARD_NODES_COMMAND');
|
|
1931
1931
|
const CLICK_COMMAND = createCommand('CLICK_COMMAND');
|
|
1932
|
+
const BEFORE_INPUT_COMMAND = createCommand('BEFORE_INPUT_COMMAND');
|
|
1933
|
+
const INPUT_COMMAND = createCommand('INPUT_COMMAND');
|
|
1934
|
+
const COMPOSITION_START_COMMAND = createCommand('COMPOSITION_START_COMMAND');
|
|
1935
|
+
const COMPOSITION_END_COMMAND = createCommand('COMPOSITION_END_COMMAND');
|
|
1932
1936
|
/**
|
|
1933
1937
|
* Dispatched to delete a character, the payload will be `true` if the deletion
|
|
1934
1938
|
* is backwards (backspace or delete on macOS) and `false` if forwards
|
|
@@ -2360,9 +2364,15 @@ function $canRemoveText(anchorNode, focusNode) {
|
|
|
2360
2364
|
function isPossiblyAndroidKeyPress(timeStamp) {
|
|
2361
2365
|
return lastKeyCode === 'MediaLast' && timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY;
|
|
2362
2366
|
}
|
|
2367
|
+
function registerDefaultCommandHandlers(editor) {
|
|
2368
|
+
editor.registerCommand(BEFORE_INPUT_COMMAND, $handleBeforeInput, COMMAND_PRIORITY_EDITOR);
|
|
2369
|
+
editor.registerCommand(INPUT_COMMAND, $handleInput, COMMAND_PRIORITY_EDITOR);
|
|
2370
|
+
editor.registerCommand(COMPOSITION_START_COMMAND, $handleCompositionStart, COMMAND_PRIORITY_EDITOR);
|
|
2371
|
+
editor.registerCommand(COMPOSITION_END_COMMAND, $handleCompositionEnd, COMMAND_PRIORITY_EDITOR);
|
|
2372
|
+
editor.registerCommand(KEY_DOWN_COMMAND, $handleKeyDown, COMMAND_PRIORITY_EDITOR);
|
|
2373
|
+
}
|
|
2363
2374
|
function onBeforeInput(event, editor) {
|
|
2364
2375
|
const inputType = event.inputType;
|
|
2365
|
-
const targetRange = getTargetRange(event);
|
|
2366
2376
|
|
|
2367
2377
|
// We let the browser do its own thing for composition.
|
|
2368
2378
|
if (inputType === 'deleteCompositionText' ||
|
|
@@ -2376,237 +2386,242 @@ function onBeforeInput(event, editor) {
|
|
|
2376
2386
|
} else if (inputType === 'insertCompositionText') {
|
|
2377
2387
|
return;
|
|
2378
2388
|
}
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
+
dispatchCommand(editor, BEFORE_INPUT_COMMAND, event);
|
|
2390
|
+
}
|
|
2391
|
+
function $handleBeforeInput(event) {
|
|
2392
|
+
const inputType = event.inputType;
|
|
2393
|
+
const targetRange = getTargetRange(event);
|
|
2394
|
+
const editor = getActiveEditor();
|
|
2395
|
+
const selection = $getSelection();
|
|
2396
|
+
if (inputType === 'deleteContentBackward') {
|
|
2397
|
+
if (selection === null) {
|
|
2398
|
+
// Use previous selection
|
|
2399
|
+
const prevSelection = $getPreviousSelection();
|
|
2400
|
+
if (!$isRangeSelection(prevSelection)) {
|
|
2401
|
+
return true;
|
|
2389
2402
|
}
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
}
|
|
2409
|
-
} else {
|
|
2410
|
-
$setCompositionKey(null);
|
|
2411
|
-
event.preventDefault();
|
|
2412
|
-
// Chromium Android at the moment seems to ignore the preventDefault
|
|
2413
|
-
// on 'deleteContentBackward' and still deletes the content. Which leads
|
|
2414
|
-
// to multiple deletions. So we let the browser handle the deletion in this case.
|
|
2415
|
-
const selectedNode = selection.anchor.getNode();
|
|
2416
|
-
const selectedNodeText = selectedNode.getTextContent();
|
|
2417
|
-
// When the target node has `canInsertTextAfter` set to false, the first deletion
|
|
2418
|
-
// doesn't have an effect, so we need to handle it with Lexical.
|
|
2419
|
-
const selectedNodeCanInsertTextAfter = selectedNode.canInsertTextAfter();
|
|
2420
|
-
const hasSelectedAllTextInNode = selection.anchor.offset === 0 && selection.focus.offset === selectedNodeText.length;
|
|
2421
|
-
let shouldLetBrowserHandleDelete = IS_ANDROID_CHROME && isSelectionAnchorSameAsFocus && !hasSelectedAllTextInNode && selectedNodeCanInsertTextAfter;
|
|
2422
|
-
// Check if selection is collapsed and if the previous node is a decorator node
|
|
2423
|
-
// If so, the browser will not be able to handle the deletion
|
|
2424
|
-
if (shouldLetBrowserHandleDelete && selection.isCollapsed()) {
|
|
2425
|
-
shouldLetBrowserHandleDelete = !$isDecoratorNode($getAdjacentNode(selection.anchor, true));
|
|
2403
|
+
$setSelection(prevSelection.clone());
|
|
2404
|
+
}
|
|
2405
|
+
if ($isRangeSelection(selection)) {
|
|
2406
|
+
const isSelectionAnchorSameAsFocus = selection.anchor.key === selection.focus.key;
|
|
2407
|
+
if (isPossiblyAndroidKeyPress(event.timeStamp) && editor.isComposing() && isSelectionAnchorSameAsFocus) {
|
|
2408
|
+
$setCompositionKey(null);
|
|
2409
|
+
lastKeyDownTimeStamp = 0;
|
|
2410
|
+
// Fixes an Android bug where selection flickers when backspacing
|
|
2411
|
+
setTimeout(() => {
|
|
2412
|
+
updateEditorSync(editor, () => {
|
|
2413
|
+
$setCompositionKey(null);
|
|
2414
|
+
});
|
|
2415
|
+
}, ANDROID_COMPOSITION_LATENCY);
|
|
2416
|
+
if ($isRangeSelection(selection)) {
|
|
2417
|
+
const anchorNode = selection.anchor.getNode();
|
|
2418
|
+
anchorNode.markDirty();
|
|
2419
|
+
if (!$isTextNode(anchorNode)) {
|
|
2420
|
+
formatDevErrorMessage(`Anchor node must be a TextNode`);
|
|
2426
2421
|
}
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2422
|
+
$updateSelectionFormatStyleFromTextNode(selection, anchorNode);
|
|
2423
|
+
}
|
|
2424
|
+
} else {
|
|
2425
|
+
$setCompositionKey(null);
|
|
2426
|
+
event.preventDefault();
|
|
2427
|
+
// Chromium Android at the moment seems to ignore the preventDefault
|
|
2428
|
+
// on 'deleteContentBackward' and still deletes the content. Which leads
|
|
2429
|
+
// to multiple deletions. So we let the browser handle the deletion in this case.
|
|
2430
|
+
const selectedNode = selection.anchor.getNode();
|
|
2431
|
+
const selectedNodeText = selectedNode.getTextContent();
|
|
2432
|
+
// When the target node has `canInsertTextAfter` set to false, the first deletion
|
|
2433
|
+
// doesn't have an effect, so we need to handle it with Lexical.
|
|
2434
|
+
const selectedNodeCanInsertTextAfter = selectedNode.canInsertTextAfter();
|
|
2435
|
+
const hasSelectedAllTextInNode = selection.anchor.offset === 0 && selection.focus.offset === selectedNodeText.length;
|
|
2436
|
+
let shouldLetBrowserHandleDelete = IS_ANDROID_CHROME && isSelectionAnchorSameAsFocus && !hasSelectedAllTextInNode && selectedNodeCanInsertTextAfter;
|
|
2437
|
+
// Check if selection is collapsed and if the previous node is a decorator node
|
|
2438
|
+
// If so, the browser will not be able to handle the deletion
|
|
2439
|
+
if (shouldLetBrowserHandleDelete && selection.isCollapsed()) {
|
|
2440
|
+
shouldLetBrowserHandleDelete = !$isDecoratorNode($getAdjacentNode(selection.anchor, true));
|
|
2441
|
+
}
|
|
2442
|
+
if (!shouldLetBrowserHandleDelete) {
|
|
2443
|
+
dispatchCommand(editor, DELETE_CHARACTER_COMMAND, true);
|
|
2444
|
+
// When deleting across paragraphs, Chrome on Android incorrectly shifts the selection rightwards
|
|
2445
|
+
// We save the correct selection to restore later during handling of selectionchange event
|
|
2446
|
+
const selectionAfterDelete = $getSelection();
|
|
2447
|
+
if (IS_ANDROID_CHROME && $isRangeSelection(selectionAfterDelete) && selectionAfterDelete.isCollapsed()) {
|
|
2448
|
+
postDeleteSelectionToRestore = selectionAfterDelete;
|
|
2449
|
+
// Cleanup in case selectionchange does not fire
|
|
2450
|
+
setTimeout(() => postDeleteSelectionToRestore = null);
|
|
2437
2451
|
}
|
|
2438
2452
|
}
|
|
2439
|
-
return;
|
|
2440
2453
|
}
|
|
2454
|
+
return true;
|
|
2441
2455
|
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2456
|
+
}
|
|
2457
|
+
if (!$isRangeSelection(selection)) {
|
|
2458
|
+
return true;
|
|
2459
|
+
}
|
|
2460
|
+
const data = event.data;
|
|
2446
2461
|
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2462
|
+
// This represents the case when two beforeinput events are triggered at the same time (without a
|
|
2463
|
+
// full event loop ending at input). This happens with MacOS with the default keyboard settings,
|
|
2464
|
+
// a combination of autocorrection + autocapitalization.
|
|
2465
|
+
// Having Lexical run everything in controlled mode would fix the issue without additional code
|
|
2466
|
+
// but this would kill the massive performance win from the most common typing event.
|
|
2467
|
+
// Alternatively, when this happens we can prematurely update our EditorState based on the DOM
|
|
2468
|
+
// content, a job that would usually be the input event's responsibility.
|
|
2469
|
+
if (unprocessedBeforeInputData !== null) {
|
|
2470
|
+
$updateSelectedTextFromDOM(false, editor, unprocessedBeforeInputData);
|
|
2471
|
+
}
|
|
2472
|
+
if ((!selection.dirty || unprocessedBeforeInputData !== null) && selection.isCollapsed() && !$isRootNode(selection.anchor.getNode()) && targetRange !== null) {
|
|
2473
|
+
selection.applyDOMRange(targetRange);
|
|
2474
|
+
}
|
|
2475
|
+
unprocessedBeforeInputData = null;
|
|
2476
|
+
const anchor = selection.anchor;
|
|
2477
|
+
const focus = selection.focus;
|
|
2478
|
+
const anchorNode = anchor.getNode();
|
|
2479
|
+
const focusNode = focus.getNode();
|
|
2480
|
+
if (inputType === 'insertText' || inputType === 'insertTranspose') {
|
|
2481
|
+
if (data === '\n') {
|
|
2482
|
+
event.preventDefault();
|
|
2483
|
+
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
|
|
2484
|
+
} else if (data === DOUBLE_LINE_BREAK) {
|
|
2485
|
+
event.preventDefault();
|
|
2486
|
+
dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND, undefined);
|
|
2487
|
+
} else if (data == null && event.dataTransfer) {
|
|
2488
|
+
// Gets around a Safari text replacement bug.
|
|
2489
|
+
const text = event.dataTransfer.getData('text/plain');
|
|
2490
|
+
event.preventDefault();
|
|
2491
|
+
selection.insertRawText(text);
|
|
2492
|
+
} else if (data != null && $shouldPreventDefaultAndInsertText(selection, targetRange, data, event.timeStamp, true)) {
|
|
2493
|
+
event.preventDefault();
|
|
2494
|
+
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
|
|
2495
|
+
} else {
|
|
2496
|
+
unprocessedBeforeInputData = data;
|
|
2459
2497
|
}
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2498
|
+
lastBeforeInputInsertTextTimeStamp = event.timeStamp;
|
|
2499
|
+
return true;
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
// Prevent the browser from carrying out
|
|
2503
|
+
// the input event, so we can control the
|
|
2504
|
+
// output.
|
|
2505
|
+
event.preventDefault();
|
|
2506
|
+
switch (inputType) {
|
|
2507
|
+
case 'insertFromYank':
|
|
2508
|
+
case 'insertFromDrop':
|
|
2509
|
+
case 'insertReplacementText':
|
|
2510
|
+
{
|
|
2511
|
+
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
|
|
2512
|
+
break;
|
|
2513
|
+
}
|
|
2514
|
+
case 'insertFromComposition':
|
|
2515
|
+
{
|
|
2516
|
+
// This is the end of composition
|
|
2517
|
+
$setCompositionKey(null);
|
|
2518
|
+
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
|
|
2519
|
+
break;
|
|
2520
|
+
}
|
|
2521
|
+
case 'insertLineBreak':
|
|
2522
|
+
{
|
|
2523
|
+
// Used for Android
|
|
2524
|
+
$setCompositionKey(null);
|
|
2468
2525
|
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
|
|
2469
|
-
|
|
2470
|
-
event.preventDefault();
|
|
2471
|
-
dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND, undefined);
|
|
2472
|
-
} else if (data == null && event.dataTransfer) {
|
|
2473
|
-
// Gets around a Safari text replacement bug.
|
|
2474
|
-
const text = event.dataTransfer.getData('text/plain');
|
|
2475
|
-
event.preventDefault();
|
|
2476
|
-
selection.insertRawText(text);
|
|
2477
|
-
} else if (data != null && $shouldPreventDefaultAndInsertText(selection, targetRange, data, event.timeStamp, true)) {
|
|
2478
|
-
event.preventDefault();
|
|
2479
|
-
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
|
|
2480
|
-
} else {
|
|
2481
|
-
unprocessedBeforeInputData = data;
|
|
2526
|
+
break;
|
|
2482
2527
|
}
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2528
|
+
case 'insertParagraph':
|
|
2529
|
+
{
|
|
2530
|
+
// Used for Android
|
|
2531
|
+
$setCompositionKey(null);
|
|
2486
2532
|
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
case 'insertFromDrop':
|
|
2494
|
-
case 'insertReplacementText':
|
|
2495
|
-
{
|
|
2496
|
-
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
|
|
2497
|
-
break;
|
|
2498
|
-
}
|
|
2499
|
-
case 'insertFromComposition':
|
|
2500
|
-
{
|
|
2501
|
-
// This is the end of composition
|
|
2502
|
-
$setCompositionKey(null);
|
|
2503
|
-
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, event);
|
|
2504
|
-
break;
|
|
2505
|
-
}
|
|
2506
|
-
case 'insertLineBreak':
|
|
2507
|
-
{
|
|
2508
|
-
// Used for Android
|
|
2509
|
-
$setCompositionKey(null);
|
|
2533
|
+
// Safari does not provide the type "insertLineBreak".
|
|
2534
|
+
// So instead, we need to infer it from the keyboard event.
|
|
2535
|
+
// We do not apply this logic to iOS to allow newline auto-capitalization
|
|
2536
|
+
// work without creating linebreaks when pressing Enter
|
|
2537
|
+
if (isInsertLineBreak && !IS_IOS) {
|
|
2538
|
+
isInsertLineBreak = false;
|
|
2510
2539
|
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
case 'insertParagraph':
|
|
2514
|
-
{
|
|
2515
|
-
// Used for Android
|
|
2516
|
-
$setCompositionKey(null);
|
|
2517
|
-
|
|
2518
|
-
// Safari does not provide the type "insertLineBreak".
|
|
2519
|
-
// So instead, we need to infer it from the keyboard event.
|
|
2520
|
-
// We do not apply this logic to iOS to allow newline auto-capitalization
|
|
2521
|
-
// work without creating linebreaks when pressing Enter
|
|
2522
|
-
if (isInsertLineBreak && !IS_IOS) {
|
|
2523
|
-
isInsertLineBreak = false;
|
|
2524
|
-
dispatchCommand(editor, INSERT_LINE_BREAK_COMMAND, false);
|
|
2525
|
-
} else {
|
|
2526
|
-
dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND, undefined);
|
|
2527
|
-
}
|
|
2528
|
-
break;
|
|
2529
|
-
}
|
|
2530
|
-
case 'insertFromPaste':
|
|
2531
|
-
case 'insertFromPasteAsQuotation':
|
|
2532
|
-
{
|
|
2533
|
-
dispatchCommand(editor, PASTE_COMMAND, event);
|
|
2534
|
-
break;
|
|
2535
|
-
}
|
|
2536
|
-
case 'deleteByComposition':
|
|
2537
|
-
{
|
|
2538
|
-
if ($canRemoveText(anchorNode, focusNode)) {
|
|
2539
|
-
dispatchCommand(editor, REMOVE_TEXT_COMMAND, event);
|
|
2540
|
-
}
|
|
2541
|
-
break;
|
|
2540
|
+
} else {
|
|
2541
|
+
dispatchCommand(editor, INSERT_PARAGRAPH_COMMAND, undefined);
|
|
2542
2542
|
}
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2543
|
+
break;
|
|
2544
|
+
}
|
|
2545
|
+
case 'insertFromPaste':
|
|
2546
|
+
case 'insertFromPasteAsQuotation':
|
|
2547
|
+
{
|
|
2548
|
+
dispatchCommand(editor, PASTE_COMMAND, event);
|
|
2549
|
+
break;
|
|
2550
|
+
}
|
|
2551
|
+
case 'deleteByComposition':
|
|
2552
|
+
{
|
|
2553
|
+
if ($canRemoveText(anchorNode, focusNode)) {
|
|
2546
2554
|
dispatchCommand(editor, REMOVE_TEXT_COMMAND, event);
|
|
2547
|
-
break;
|
|
2548
|
-
}
|
|
2549
|
-
case 'deleteContent':
|
|
2550
|
-
{
|
|
2551
|
-
dispatchCommand(editor, DELETE_CHARACTER_COMMAND, false);
|
|
2552
|
-
break;
|
|
2553
|
-
}
|
|
2554
|
-
case 'deleteWordBackward':
|
|
2555
|
-
{
|
|
2556
|
-
dispatchCommand(editor, DELETE_WORD_COMMAND, true);
|
|
2557
|
-
break;
|
|
2558
|
-
}
|
|
2559
|
-
case 'deleteWordForward':
|
|
2560
|
-
{
|
|
2561
|
-
dispatchCommand(editor, DELETE_WORD_COMMAND, false);
|
|
2562
|
-
break;
|
|
2563
|
-
}
|
|
2564
|
-
case 'deleteHardLineBackward':
|
|
2565
|
-
case 'deleteSoftLineBackward':
|
|
2566
|
-
{
|
|
2567
|
-
dispatchCommand(editor, DELETE_LINE_COMMAND, true);
|
|
2568
|
-
break;
|
|
2569
|
-
}
|
|
2570
|
-
case 'deleteContentForward':
|
|
2571
|
-
case 'deleteHardLineForward':
|
|
2572
|
-
case 'deleteSoftLineForward':
|
|
2573
|
-
{
|
|
2574
|
-
dispatchCommand(editor, DELETE_LINE_COMMAND, false);
|
|
2575
|
-
break;
|
|
2576
|
-
}
|
|
2577
|
-
case 'formatStrikeThrough':
|
|
2578
|
-
{
|
|
2579
|
-
dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'strikethrough');
|
|
2580
|
-
break;
|
|
2581
2555
|
}
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2556
|
+
break;
|
|
2557
|
+
}
|
|
2558
|
+
case 'deleteByDrag':
|
|
2559
|
+
case 'deleteByCut':
|
|
2560
|
+
{
|
|
2561
|
+
dispatchCommand(editor, REMOVE_TEXT_COMMAND, event);
|
|
2562
|
+
break;
|
|
2563
|
+
}
|
|
2564
|
+
case 'deleteContent':
|
|
2565
|
+
{
|
|
2566
|
+
dispatchCommand(editor, DELETE_CHARACTER_COMMAND, false);
|
|
2567
|
+
break;
|
|
2568
|
+
}
|
|
2569
|
+
case 'deleteWordBackward':
|
|
2570
|
+
{
|
|
2571
|
+
dispatchCommand(editor, DELETE_WORD_COMMAND, true);
|
|
2572
|
+
break;
|
|
2573
|
+
}
|
|
2574
|
+
case 'deleteWordForward':
|
|
2575
|
+
{
|
|
2576
|
+
dispatchCommand(editor, DELETE_WORD_COMMAND, false);
|
|
2577
|
+
break;
|
|
2578
|
+
}
|
|
2579
|
+
case 'deleteHardLineBackward':
|
|
2580
|
+
case 'deleteSoftLineBackward':
|
|
2581
|
+
{
|
|
2582
|
+
dispatchCommand(editor, DELETE_LINE_COMMAND, true);
|
|
2583
|
+
break;
|
|
2584
|
+
}
|
|
2585
|
+
case 'deleteContentForward':
|
|
2586
|
+
case 'deleteHardLineForward':
|
|
2587
|
+
case 'deleteSoftLineForward':
|
|
2588
|
+
{
|
|
2589
|
+
dispatchCommand(editor, DELETE_LINE_COMMAND, false);
|
|
2590
|
+
break;
|
|
2591
|
+
}
|
|
2592
|
+
case 'formatStrikeThrough':
|
|
2593
|
+
{
|
|
2594
|
+
dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'strikethrough');
|
|
2595
|
+
break;
|
|
2596
|
+
}
|
|
2597
|
+
case 'formatBold':
|
|
2598
|
+
{
|
|
2599
|
+
dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'bold');
|
|
2600
|
+
break;
|
|
2601
|
+
}
|
|
2602
|
+
case 'formatItalic':
|
|
2603
|
+
{
|
|
2604
|
+
dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'italic');
|
|
2605
|
+
break;
|
|
2606
|
+
}
|
|
2607
|
+
case 'formatUnderline':
|
|
2608
|
+
{
|
|
2609
|
+
dispatchCommand(editor, FORMAT_TEXT_COMMAND, 'underline');
|
|
2610
|
+
break;
|
|
2611
|
+
}
|
|
2612
|
+
case 'historyUndo':
|
|
2613
|
+
{
|
|
2614
|
+
dispatchCommand(editor, UNDO_COMMAND, undefined);
|
|
2615
|
+
break;
|
|
2616
|
+
}
|
|
2617
|
+
case 'historyRedo':
|
|
2618
|
+
{
|
|
2619
|
+
dispatchCommand(editor, REDO_COMMAND, undefined);
|
|
2620
|
+
break;
|
|
2621
|
+
}
|
|
2622
|
+
// NO-OP
|
|
2623
|
+
}
|
|
2624
|
+
return true;
|
|
2610
2625
|
}
|
|
2611
2626
|
function onInput(event, editor) {
|
|
2612
2627
|
// Note that the MutationObserver may or may not have already fired,
|
|
@@ -2618,90 +2633,103 @@ function onInput(event, editor) {
|
|
|
2618
2633
|
// We don't want the onInput to bubble, in the case of nested editors.
|
|
2619
2634
|
event.stopPropagation();
|
|
2620
2635
|
updateEditorSync(editor, () => {
|
|
2621
|
-
|
|
2622
|
-
|
|
2636
|
+
editor.dispatchCommand(INPUT_COMMAND, event);
|
|
2637
|
+
}, {
|
|
2638
|
+
event
|
|
2639
|
+
});
|
|
2640
|
+
unprocessedBeforeInputData = null;
|
|
2641
|
+
}
|
|
2642
|
+
function $handleInput(event) {
|
|
2643
|
+
if (isHTMLElement(event.target) && $isSelectionCapturedInDecorator(event.target)) {
|
|
2644
|
+
return true;
|
|
2645
|
+
}
|
|
2646
|
+
const editor = getActiveEditor();
|
|
2647
|
+
const selection = $getSelection();
|
|
2648
|
+
const data = event.data;
|
|
2649
|
+
const targetRange = getTargetRange(event);
|
|
2650
|
+
if (data != null && $isRangeSelection(selection) && $shouldPreventDefaultAndInsertText(selection, targetRange, data, event.timeStamp, false)) {
|
|
2651
|
+
// Given we're over-riding the default behavior, we will need
|
|
2652
|
+
// to ensure to disable composition before dispatching the
|
|
2653
|
+
// insertText command for when changing the sequence for FF.
|
|
2654
|
+
if (isFirefoxEndingComposition) {
|
|
2655
|
+
$onCompositionEndImpl(editor, data);
|
|
2656
|
+
isFirefoxEndingComposition = false;
|
|
2623
2657
|
}
|
|
2624
|
-
const
|
|
2625
|
-
const
|
|
2626
|
-
const
|
|
2627
|
-
if (
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
}
|
|
2641
|
-
const isBackward = selection.isBackward();
|
|
2642
|
-
const startOffset = isBackward ? selection.anchor.offset : selection.focus.offset;
|
|
2643
|
-
const endOffset = isBackward ? selection.focus.offset : selection.anchor.offset;
|
|
2644
|
-
// If the content is the same as inserted, then don't dispatch an insertion.
|
|
2645
|
-
// Given onInput doesn't take the current selection (it uses the previous)
|
|
2646
|
-
// we can compare that against what the DOM currently says.
|
|
2647
|
-
if (!CAN_USE_BEFORE_INPUT || selection.isCollapsed() || !$isTextNode(anchorNode) || domSelection.anchorNode === null || anchorNode.getTextContent().slice(0, startOffset) + data + anchorNode.getTextContent().slice(startOffset + endOffset) !== getAnchorTextFromDOM(domSelection.anchorNode)) {
|
|
2648
|
-
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
|
|
2649
|
-
}
|
|
2650
|
-
const textLength = data.length;
|
|
2658
|
+
const anchor = selection.anchor;
|
|
2659
|
+
const anchorNode = anchor.getNode();
|
|
2660
|
+
const domSelection = getDOMSelection(getWindow(editor));
|
|
2661
|
+
if (domSelection === null) {
|
|
2662
|
+
return true;
|
|
2663
|
+
}
|
|
2664
|
+
const isBackward = selection.isBackward();
|
|
2665
|
+
const startOffset = isBackward ? selection.anchor.offset : selection.focus.offset;
|
|
2666
|
+
const endOffset = isBackward ? selection.focus.offset : selection.anchor.offset;
|
|
2667
|
+
// If the content is the same as inserted, then don't dispatch an insertion.
|
|
2668
|
+
// Given onInput doesn't take the current selection (it uses the previous)
|
|
2669
|
+
// we can compare that against what the DOM currently says.
|
|
2670
|
+
if (!CAN_USE_BEFORE_INPUT || selection.isCollapsed() || !$isTextNode(anchorNode) || domSelection.anchorNode === null || anchorNode.getTextContent().slice(0, startOffset) + data + anchorNode.getTextContent().slice(startOffset + endOffset) !== getAnchorTextFromDOM(domSelection.anchorNode)) {
|
|
2671
|
+
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
|
|
2672
|
+
}
|
|
2673
|
+
const textLength = data.length;
|
|
2651
2674
|
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2675
|
+
// Another hack for FF, as it's possible that the IME is still
|
|
2676
|
+
// open, even though compositionend has already fired (sigh).
|
|
2677
|
+
if (IS_FIREFOX && textLength > 1 && event.inputType === 'insertCompositionText' && !editor.isComposing()) {
|
|
2678
|
+
selection.anchor.offset -= textLength;
|
|
2679
|
+
}
|
|
2657
2680
|
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2681
|
+
// This ensures consistency on Android.
|
|
2682
|
+
if (!IS_SAFARI && !IS_IOS && !IS_APPLE_WEBKIT && editor.isComposing()) {
|
|
2683
|
+
lastKeyDownTimeStamp = 0;
|
|
2684
|
+
$setCompositionKey(null);
|
|
2685
|
+
}
|
|
2686
|
+
} else {
|
|
2687
|
+
const characterData = data !== null ? data : undefined;
|
|
2688
|
+
$updateSelectedTextFromDOM(false, editor, characterData);
|
|
2666
2689
|
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
}
|
|
2690
|
+
// onInput always fires after onCompositionEnd for FF.
|
|
2691
|
+
if (isFirefoxEndingComposition) {
|
|
2692
|
+
$onCompositionEndImpl(editor, data || undefined);
|
|
2693
|
+
isFirefoxEndingComposition = false;
|
|
2672
2694
|
}
|
|
2695
|
+
}
|
|
2673
2696
|
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
event
|
|
2679
|
-
});
|
|
2680
|
-
unprocessedBeforeInputData = null;
|
|
2697
|
+
// Also flush any other mutations that might have occurred
|
|
2698
|
+
// since the change.
|
|
2699
|
+
$flushMutations();
|
|
2700
|
+
return true;
|
|
2681
2701
|
}
|
|
2682
2702
|
function onCompositionStart(event, editor) {
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
+
dispatchCommand(editor, COMPOSITION_START_COMMAND, event);
|
|
2704
|
+
}
|
|
2705
|
+
function $handleCompositionStart(event) {
|
|
2706
|
+
const editor = getActiveEditor();
|
|
2707
|
+
const selection = $getSelection();
|
|
2708
|
+
if ($isRangeSelection(selection) && !editor.isComposing()) {
|
|
2709
|
+
const anchor = selection.anchor;
|
|
2710
|
+
const node = selection.anchor.getNode();
|
|
2711
|
+
$setCompositionKey(anchor.key);
|
|
2712
|
+
if (
|
|
2713
|
+
// If it has been 30ms since the last keydown, then we should
|
|
2714
|
+
// apply the empty space heuristic. We can't do this for Safari,
|
|
2715
|
+
// as the keydown fires after composition start.
|
|
2716
|
+
event.timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY ||
|
|
2717
|
+
// FF has issues around composing multibyte characters, so we also
|
|
2718
|
+
// need to invoke the empty space heuristic below.
|
|
2719
|
+
anchor.type === 'element' || !selection.isCollapsed() || node.getFormat() !== selection.format || $isTextNode(node) && node.getStyle() !== selection.style) {
|
|
2720
|
+
// We insert a zero width character, ready for the composition
|
|
2721
|
+
// to get inserted into the new node we create. If
|
|
2722
|
+
// we don't do this, Safari will fail on us because
|
|
2723
|
+
// there is no text node matching the selection.
|
|
2724
|
+
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, COMPOSITION_START_CHAR);
|
|
2703
2725
|
}
|
|
2704
|
-
}
|
|
2726
|
+
}
|
|
2727
|
+
return true;
|
|
2728
|
+
}
|
|
2729
|
+
function $handleCompositionEnd(event) {
|
|
2730
|
+
const editor = getActiveEditor();
|
|
2731
|
+
$onCompositionEndImpl(editor, event.data);
|
|
2732
|
+
return true;
|
|
2705
2733
|
}
|
|
2706
2734
|
function $onCompositionEndImpl(editor, data) {
|
|
2707
2735
|
const compositionKey = editor._compositionKey;
|
|
@@ -2753,9 +2781,7 @@ function onCompositionEnd(event, editor) {
|
|
|
2753
2781
|
isSafariEndingComposition = true;
|
|
2754
2782
|
safariEndCompositionEventData = event.data;
|
|
2755
2783
|
} else {
|
|
2756
|
-
|
|
2757
|
-
$onCompositionEndImpl(editor, event.data);
|
|
2758
|
-
});
|
|
2784
|
+
dispatchCommand(editor, COMPOSITION_END_COMMAND, event);
|
|
2759
2785
|
}
|
|
2760
2786
|
}
|
|
2761
2787
|
function onKeyDown(event, editor) {
|
|
@@ -2764,11 +2790,12 @@ function onKeyDown(event, editor) {
|
|
|
2764
2790
|
if (editor.isComposing()) {
|
|
2765
2791
|
return;
|
|
2766
2792
|
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2793
|
+
dispatchCommand(editor, KEY_DOWN_COMMAND, event);
|
|
2794
|
+
}
|
|
2795
|
+
function $handleKeyDown(event) {
|
|
2796
|
+
const editor = getActiveEditor();
|
|
2770
2797
|
if (event.key == null) {
|
|
2771
|
-
return;
|
|
2798
|
+
return true;
|
|
2772
2799
|
}
|
|
2773
2800
|
if (isSafariEndingComposition && isBackspace(event)) {
|
|
2774
2801
|
updateEditorSync(editor, () => {
|
|
@@ -2776,7 +2803,7 @@ function onKeyDown(event, editor) {
|
|
|
2776
2803
|
});
|
|
2777
2804
|
isSafariEndingComposition = false;
|
|
2778
2805
|
safariEndCompositionEventData = '';
|
|
2779
|
-
return;
|
|
2806
|
+
return true;
|
|
2780
2807
|
}
|
|
2781
2808
|
if (isMoveForward(event)) {
|
|
2782
2809
|
dispatchCommand(editor, KEY_ARROW_RIGHT_COMMAND, event);
|
|
@@ -2867,8 +2894,9 @@ function onKeyDown(event, editor) {
|
|
|
2867
2894
|
}
|
|
2868
2895
|
}
|
|
2869
2896
|
if (isModifier(event)) {
|
|
2870
|
-
dispatchCommand(
|
|
2897
|
+
editor.dispatchCommand(KEY_MODIFIER_COMMAND, event);
|
|
2871
2898
|
}
|
|
2899
|
+
return true;
|
|
2872
2900
|
}
|
|
2873
2901
|
function getRootElementRemoveHandles(rootElement) {
|
|
2874
2902
|
// @ts-expect-error: internal field
|
|
@@ -10186,6 +10214,7 @@ function createEditor(editorConfig) {
|
|
|
10186
10214
|
editor._pendingEditorState = initialEditorState;
|
|
10187
10215
|
editor._dirtyType = FULL_RECONCILE;
|
|
10188
10216
|
}
|
|
10217
|
+
registerDefaultCommandHandlers(editor);
|
|
10189
10218
|
return editor;
|
|
10190
10219
|
}
|
|
10191
10220
|
class LexicalEditor {
|
|
@@ -10865,7 +10894,7 @@ class LexicalEditor {
|
|
|
10865
10894
|
};
|
|
10866
10895
|
}
|
|
10867
10896
|
}
|
|
10868
|
-
LexicalEditor.version = "0.38.3-nightly.
|
|
10897
|
+
LexicalEditor.version = "0.38.3-nightly.20251107.0+dev.cjs";
|
|
10869
10898
|
|
|
10870
10899
|
let pendingNodeToClone = null;
|
|
10871
10900
|
function setPendingNodeToClone(pendingNode) {
|
|
@@ -14078,6 +14107,7 @@ exports.$splitAtPointCaretNext = $splitAtPointCaretNext;
|
|
|
14078
14107
|
exports.$splitNode = $splitNode;
|
|
14079
14108
|
exports.$updateRangeSelectionFromCaretRange = $updateRangeSelectionFromCaretRange;
|
|
14080
14109
|
exports.ArtificialNode__DO_NOT_USE = ArtificialNode__DO_NOT_USE;
|
|
14110
|
+
exports.BEFORE_INPUT_COMMAND = BEFORE_INPUT_COMMAND;
|
|
14081
14111
|
exports.BLUR_COMMAND = BLUR_COMMAND;
|
|
14082
14112
|
exports.CAN_REDO_COMMAND = CAN_REDO_COMMAND;
|
|
14083
14113
|
exports.CAN_UNDO_COMMAND = CAN_UNDO_COMMAND;
|
|
@@ -14090,6 +14120,8 @@ exports.COMMAND_PRIORITY_EDITOR = COMMAND_PRIORITY_EDITOR;
|
|
|
14090
14120
|
exports.COMMAND_PRIORITY_HIGH = COMMAND_PRIORITY_HIGH;
|
|
14091
14121
|
exports.COMMAND_PRIORITY_LOW = COMMAND_PRIORITY_LOW;
|
|
14092
14122
|
exports.COMMAND_PRIORITY_NORMAL = COMMAND_PRIORITY_NORMAL;
|
|
14123
|
+
exports.COMPOSITION_END_COMMAND = COMPOSITION_END_COMMAND;
|
|
14124
|
+
exports.COMPOSITION_START_COMMAND = COMPOSITION_START_COMMAND;
|
|
14093
14125
|
exports.CONTROLLED_TEXT_INSERTION_COMMAND = CONTROLLED_TEXT_INSERTION_COMMAND;
|
|
14094
14126
|
exports.COPY_COMMAND = COPY_COMMAND;
|
|
14095
14127
|
exports.CUT_COMMAND = CUT_COMMAND;
|
|
@@ -14109,6 +14141,7 @@ exports.HISTORIC_TAG = HISTORIC_TAG;
|
|
|
14109
14141
|
exports.HISTORY_MERGE_TAG = HISTORY_MERGE_TAG;
|
|
14110
14142
|
exports.HISTORY_PUSH_TAG = HISTORY_PUSH_TAG;
|
|
14111
14143
|
exports.INDENT_CONTENT_COMMAND = INDENT_CONTENT_COMMAND;
|
|
14144
|
+
exports.INPUT_COMMAND = INPUT_COMMAND;
|
|
14112
14145
|
exports.INSERT_LINE_BREAK_COMMAND = INSERT_LINE_BREAK_COMMAND;
|
|
14113
14146
|
exports.INSERT_PARAGRAPH_COMMAND = INSERT_PARAGRAPH_COMMAND;
|
|
14114
14147
|
exports.INSERT_TAB_COMMAND = INSERT_TAB_COMMAND;
|