lexical 0.7.9 → 0.8.1
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 +96 -28
- package/Lexical.prod.js +196 -195
- package/LexicalCommands.d.ts +1 -0
- package/LexicalEditor.d.ts +20 -15
- package/LexicalEvents.d.ts +1 -1
- package/LexicalSelection.d.ts +3 -1
- package/index.d.ts +2 -2
- package/package.json +1 -1
package/Lexical.dev.js
CHANGED
|
@@ -32,6 +32,7 @@ const DELETE_LINE_COMMAND = createCommand('DELETE_LINE_COMMAND');
|
|
|
32
32
|
const FORMAT_TEXT_COMMAND = createCommand('FORMAT_TEXT_COMMAND');
|
|
33
33
|
const UNDO_COMMAND = createCommand('UNDO_COMMAND');
|
|
34
34
|
const REDO_COMMAND = createCommand('REDO_COMMAND');
|
|
35
|
+
const KEY_DOWN_COMMAND = createCommand('KEYDOWN_COMMAND');
|
|
35
36
|
const KEY_ARROW_RIGHT_COMMAND = createCommand('KEY_ARROW_RIGHT_COMMAND');
|
|
36
37
|
const MOVE_TO_END = createCommand('MOVE_TO_END');
|
|
37
38
|
const KEY_ARROW_LEFT_COMMAND = createCommand('KEY_ARROW_LEFT_COMMAND');
|
|
@@ -90,6 +91,8 @@ const IS_IOS = CAN_USE_DOM && /iPad|iPhone|iPod/.test(navigator.userAgent) && !w
|
|
|
90
91
|
const IS_CHROME = CAN_USE_DOM && /^(?=.*Chrome).*/i.test(navigator.userAgent);
|
|
91
92
|
// export const canUseTextInputEvent: boolean = CAN_USE_DOM && 'TextEvent' in window && !documentMode;
|
|
92
93
|
|
|
94
|
+
const IS_APPLE_WEBKIT = CAN_USE_DOM && /AppleWebKit\/[\d.]+/.test(navigator.userAgent) && !IS_CHROME;
|
|
95
|
+
|
|
93
96
|
/**
|
|
94
97
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
95
98
|
*
|
|
@@ -142,7 +145,7 @@ const ZERO_WIDTH_SPACE = '\u200b';
|
|
|
142
145
|
|
|
143
146
|
// For iOS/Safari we use a non breaking space, otherwise the cursor appears
|
|
144
147
|
// overlapping the composed text.
|
|
145
|
-
const COMPOSITION_SUFFIX = IS_SAFARI || IS_IOS ? NON_BREAKING_SPACE : ZERO_WIDTH_SPACE;
|
|
148
|
+
const COMPOSITION_SUFFIX = IS_SAFARI || IS_IOS || IS_APPLE_WEBKIT ? NON_BREAKING_SPACE : ZERO_WIDTH_SPACE;
|
|
146
149
|
const DOUBLE_LINE_BREAK = '\n\n';
|
|
147
150
|
|
|
148
151
|
// For FF, we need to use a non-breaking space, or it gets composition
|
|
@@ -856,7 +859,7 @@ function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focu
|
|
|
856
859
|
if (compositionEnd || normalizedTextContent !== prevTextContent) {
|
|
857
860
|
if (normalizedTextContent === '') {
|
|
858
861
|
$setCompositionKey(null);
|
|
859
|
-
if (!IS_SAFARI && !IS_IOS) {
|
|
862
|
+
if (!IS_SAFARI && !IS_IOS && !IS_APPLE_WEBKIT) {
|
|
860
863
|
// For composition (mainly Android), we have to remove the node on a later update
|
|
861
864
|
const editor = getActiveEditor();
|
|
862
865
|
setTimeout(() => {
|
|
@@ -1645,8 +1648,19 @@ function destroyChildren(children, _startIndex, endIndex, dom) {
|
|
|
1645
1648
|
function setTextAlign(domStyle, value) {
|
|
1646
1649
|
domStyle.setProperty('text-align', value);
|
|
1647
1650
|
}
|
|
1651
|
+
const DEFAULT_INDENT_VALUE = '20px';
|
|
1648
1652
|
function setElementIndent(dom, indent) {
|
|
1649
|
-
|
|
1653
|
+
const indentClassName = activeEditorConfig.theme.indent;
|
|
1654
|
+
if (typeof indentClassName === 'string') {
|
|
1655
|
+
const elementHasClassName = dom.classList.contains(indentClassName);
|
|
1656
|
+
if (indent > 0 && !elementHasClassName) {
|
|
1657
|
+
dom.classList.add(indentClassName);
|
|
1658
|
+
} else if (indent < 1 && elementHasClassName) {
|
|
1659
|
+
dom.classList.remove(indentClassName);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
const indentationBaseValue = getComputedStyle(dom).getPropertyValue('--lexical-indent-base-value') || DEFAULT_INDENT_VALUE;
|
|
1663
|
+
dom.style.setProperty('padding-inline-start', indent === 0 ? '' : `calc(${indent} * ${indentationBaseValue})`);
|
|
1650
1664
|
}
|
|
1651
1665
|
function setElementFormat(dom, format) {
|
|
1652
1666
|
const domStyle = dom.style;
|
|
@@ -2179,12 +2193,13 @@ if (CAN_USE_BEFORE_INPUT) {
|
|
|
2179
2193
|
let lastKeyDownTimeStamp = 0;
|
|
2180
2194
|
let lastKeyCode = 0;
|
|
2181
2195
|
let lastBeforeInputInsertTextTimeStamp = 0;
|
|
2196
|
+
let unprocessedBeforeInputData = null;
|
|
2182
2197
|
let rootElementsRegistered = 0;
|
|
2183
2198
|
let isSelectionChangeFromDOMUpdate = false;
|
|
2184
2199
|
let isSelectionChangeFromMouseDown = false;
|
|
2185
2200
|
let isInsertLineBreak = false;
|
|
2186
2201
|
let isFirefoxEndingComposition = false;
|
|
2187
|
-
let collapsedSelectionFormat = [0, 0, 'root', 0];
|
|
2202
|
+
let collapsedSelectionFormat = [0, '', 0, 'root', 0];
|
|
2188
2203
|
|
|
2189
2204
|
// This function is used to determine if Lexical should attempt to override
|
|
2190
2205
|
// the default browser behavior for insertion of text and use its own internal
|
|
@@ -2221,7 +2236,7 @@ function $shouldPreventDefaultAndInsertText(selection, text, timeStamp, isBefore
|
|
|
2221
2236
|
// If the DOM selection element is not the same as the backing node during beforeinput.
|
|
2222
2237
|
(isBeforeInput || !CAN_USE_BEFORE_INPUT) && backingAnchorElement !== null && !anchorNode.isComposing() && domAnchorNode !== getDOMTextNode(backingAnchorElement) ||
|
|
2223
2238
|
// Check if we're changing from bold to italics, or some other format.
|
|
2224
|
-
anchorNode.getFormat() !== selection.format ||
|
|
2239
|
+
anchorNode.getFormat() !== selection.format || anchorNode.getStyle() !== selection.style ||
|
|
2225
2240
|
// One last set of heuristics to check against.
|
|
2226
2241
|
$shouldInsertTextAfterOrBeforeTextNode(selection, anchorNode);
|
|
2227
2242
|
}
|
|
@@ -2277,14 +2292,17 @@ function onSelectionChange(domSelection, editor, isActive) {
|
|
|
2277
2292
|
// instead of getting the format from the anchor node.
|
|
2278
2293
|
const windowEvent = getWindow(editor).event;
|
|
2279
2294
|
const currentTimeStamp = windowEvent ? windowEvent.timeStamp : performance.now();
|
|
2280
|
-
const [lastFormat, lastOffset, lastKey, timeStamp] = collapsedSelectionFormat;
|
|
2295
|
+
const [lastFormat, lastStyle, lastOffset, lastKey, timeStamp] = collapsedSelectionFormat;
|
|
2281
2296
|
if (currentTimeStamp < timeStamp + 200 && anchor.offset === lastOffset && anchor.key === lastKey) {
|
|
2282
2297
|
selection.format = lastFormat;
|
|
2298
|
+
selection.style = lastStyle;
|
|
2283
2299
|
} else {
|
|
2284
2300
|
if (anchor.type === 'text') {
|
|
2285
2301
|
selection.format = anchorNode.getFormat();
|
|
2302
|
+
selection.style = anchorNode.getStyle();
|
|
2286
2303
|
} else if (anchor.type === 'element') {
|
|
2287
2304
|
selection.format = 0;
|
|
2305
|
+
selection.style = '';
|
|
2288
2306
|
}
|
|
2289
2307
|
}
|
|
2290
2308
|
} else {
|
|
@@ -2295,6 +2313,7 @@ function onSelectionChange(domSelection, editor, isActive) {
|
|
|
2295
2313
|
for (let i = 0; i < nodesLength; i++) {
|
|
2296
2314
|
const node = nodes[i];
|
|
2297
2315
|
if ($isTextNode(node)) {
|
|
2316
|
+
// TODO: what about style?
|
|
2298
2317
|
hasTextNodes = true;
|
|
2299
2318
|
combinedFormat &= node.getFormat();
|
|
2300
2319
|
if (combinedFormat === 0) {
|
|
@@ -2399,6 +2418,7 @@ function onBeforeInput(event, editor) {
|
|
|
2399
2418
|
const anchorNode = selection.anchor.getNode();
|
|
2400
2419
|
anchorNode.markDirty();
|
|
2401
2420
|
selection.format = anchorNode.getFormat();
|
|
2421
|
+
selection.style = anchorNode.getStyle();
|
|
2402
2422
|
}
|
|
2403
2423
|
} else {
|
|
2404
2424
|
event.preventDefault();
|
|
@@ -2411,9 +2431,21 @@ function onBeforeInput(event, editor) {
|
|
|
2411
2431
|
return;
|
|
2412
2432
|
}
|
|
2413
2433
|
const data = event.data;
|
|
2414
|
-
|
|
2434
|
+
|
|
2435
|
+
// This represents the case when two beforeinput events are triggered at the same time (without a
|
|
2436
|
+
// full event loop ending at input). This happens with MacOS with the default keyboard settings,
|
|
2437
|
+
// a combination of autocorrection + autocapitalization.
|
|
2438
|
+
// Having Lexical run everything in controlled mode would fix the issue without additional code
|
|
2439
|
+
// but this would kill the massive performance win from the most common typing event.
|
|
2440
|
+
// Alternatively, when this happens we can prematurely update our EditorState based on the DOM
|
|
2441
|
+
// content, a job that would usually be the input event's responsibility.
|
|
2442
|
+
if (unprocessedBeforeInputData !== null) {
|
|
2443
|
+
$updateSelectedTextFromDOM(false, editor, unprocessedBeforeInputData);
|
|
2444
|
+
}
|
|
2445
|
+
if ((!selection.dirty || unprocessedBeforeInputData !== null) && selection.isCollapsed() && !$isRootNode(selection.anchor.getNode())) {
|
|
2415
2446
|
$applyTargetRange(selection, event);
|
|
2416
2447
|
}
|
|
2448
|
+
unprocessedBeforeInputData = null;
|
|
2417
2449
|
const anchor = selection.anchor;
|
|
2418
2450
|
const focus = selection.focus;
|
|
2419
2451
|
const anchorNode = anchor.getNode();
|
|
@@ -2433,6 +2465,8 @@ function onBeforeInput(event, editor) {
|
|
|
2433
2465
|
} else if (data != null && $shouldPreventDefaultAndInsertText(selection, data, event.timeStamp, true)) {
|
|
2434
2466
|
event.preventDefault();
|
|
2435
2467
|
dispatchCommand(editor, CONTROLLED_TEXT_INSERTION_COMMAND, data);
|
|
2468
|
+
} else {
|
|
2469
|
+
unprocessedBeforeInputData = data;
|
|
2436
2470
|
}
|
|
2437
2471
|
lastBeforeInputInsertTextTimeStamp = event.timeStamp;
|
|
2438
2472
|
return;
|
|
@@ -2597,7 +2631,7 @@ function onInput(event, editor) {
|
|
|
2597
2631
|
}
|
|
2598
2632
|
|
|
2599
2633
|
// This ensures consistency on Android.
|
|
2600
|
-
if (!IS_SAFARI && !IS_IOS && editor.isComposing()) {
|
|
2634
|
+
if (!IS_SAFARI && !IS_IOS && !IS_APPLE_WEBKIT && editor.isComposing()) {
|
|
2601
2635
|
lastKeyDownTimeStamp = 0;
|
|
2602
2636
|
$setCompositionKey(null);
|
|
2603
2637
|
}
|
|
@@ -2616,12 +2650,14 @@ function onInput(event, editor) {
|
|
|
2616
2650
|
// since the change.
|
|
2617
2651
|
$flushMutations();
|
|
2618
2652
|
});
|
|
2653
|
+
unprocessedBeforeInputData = null;
|
|
2619
2654
|
}
|
|
2620
2655
|
function onCompositionStart(event, editor) {
|
|
2621
2656
|
updateEditor(editor, () => {
|
|
2622
2657
|
const selection = $getSelection();
|
|
2623
2658
|
if ($isRangeSelection(selection) && !editor.isComposing()) {
|
|
2624
2659
|
const anchor = selection.anchor;
|
|
2660
|
+
const node = selection.anchor.getNode();
|
|
2625
2661
|
$setCompositionKey(anchor.key);
|
|
2626
2662
|
if (
|
|
2627
2663
|
// If it has been 30ms since the last keydown, then we should
|
|
@@ -2630,7 +2666,7 @@ function onCompositionStart(event, editor) {
|
|
|
2630
2666
|
event.timeStamp < lastKeyDownTimeStamp + ANDROID_COMPOSITION_LATENCY ||
|
|
2631
2667
|
// FF has issues around composing multibyte characters, so we also
|
|
2632
2668
|
// need to invoke the empty space heuristic below.
|
|
2633
|
-
anchor.type === 'element' || !selection.isCollapsed() ||
|
|
2669
|
+
anchor.type === 'element' || !selection.isCollapsed() || node.getFormat() !== selection.format || node.getStyle() !== selection.style) {
|
|
2634
2670
|
// We insert a zero width character, ready for the composition
|
|
2635
2671
|
// to get inserted into the new node we create. If
|
|
2636
2672
|
// we don't do this, Safari will fail on us because
|
|
@@ -2700,6 +2736,9 @@ function onKeyDown(event, editor) {
|
|
|
2700
2736
|
metaKey,
|
|
2701
2737
|
altKey
|
|
2702
2738
|
} = event;
|
|
2739
|
+
if (dispatchCommand(editor, KEY_DOWN_COMMAND, event)) {
|
|
2740
|
+
return;
|
|
2741
|
+
}
|
|
2703
2742
|
if (isMoveForward(keyCode, ctrlKey, altKey, metaKey)) {
|
|
2704
2743
|
dispatchCommand(editor, KEY_ARROW_RIGHT_COMMAND, event);
|
|
2705
2744
|
} else if (isMoveToEnd(keyCode, ctrlKey, shiftKey, altKey, metaKey)) {
|
|
@@ -2963,8 +3002,8 @@ function cleanActiveNestedEditorsMap(editor) {
|
|
|
2963
3002
|
function markSelectionChangeFromDOMUpdate() {
|
|
2964
3003
|
isSelectionChangeFromDOMUpdate = true;
|
|
2965
3004
|
}
|
|
2966
|
-
function markCollapsedSelectionFormat(format, offset, key, timeStamp) {
|
|
2967
|
-
collapsedSelectionFormat = [format, offset, key, timeStamp];
|
|
3005
|
+
function markCollapsedSelectionFormat(format, style, offset, key, timeStamp) {
|
|
3006
|
+
collapsedSelectionFormat = [format, style, offset, key, timeStamp];
|
|
2968
3007
|
}
|
|
2969
3008
|
|
|
2970
3009
|
/**
|
|
@@ -3071,12 +3110,13 @@ function $moveSelectionPointToEnd(point, node) {
|
|
|
3071
3110
|
selectPointOnNode(point, node);
|
|
3072
3111
|
}
|
|
3073
3112
|
}
|
|
3074
|
-
function $transferStartingElementPointToTextPoint(start, end, format) {
|
|
3113
|
+
function $transferStartingElementPointToTextPoint(start, end, format, style) {
|
|
3075
3114
|
const element = start.getNode();
|
|
3076
3115
|
const placementNode = element.getChildAtIndex(start.offset);
|
|
3077
3116
|
const textNode = $createTextNode();
|
|
3078
3117
|
const target = $isRootNode(element) ? $createParagraphNode().append(textNode) : textNode;
|
|
3079
3118
|
textNode.setFormat(format);
|
|
3119
|
+
textNode.setStyle(style);
|
|
3080
3120
|
if (placementNode === null) {
|
|
3081
3121
|
element.append(target);
|
|
3082
3122
|
} else {
|
|
@@ -3325,11 +3365,12 @@ function DEPRECATED_$isGridSelection(x) {
|
|
|
3325
3365
|
return x instanceof GridSelection;
|
|
3326
3366
|
}
|
|
3327
3367
|
class RangeSelection {
|
|
3328
|
-
constructor(anchor, focus, format) {
|
|
3368
|
+
constructor(anchor, focus, format, style) {
|
|
3329
3369
|
this.anchor = anchor;
|
|
3330
3370
|
this.focus = focus;
|
|
3331
3371
|
this.dirty = false;
|
|
3332
3372
|
this.format = format;
|
|
3373
|
+
this.style = style;
|
|
3333
3374
|
this._cachedNodes = null;
|
|
3334
3375
|
anchor._selection = this;
|
|
3335
3376
|
focus._selection = this;
|
|
@@ -3338,7 +3379,7 @@ class RangeSelection {
|
|
|
3338
3379
|
if (!$isRangeSelection(selection)) {
|
|
3339
3380
|
return false;
|
|
3340
3381
|
}
|
|
3341
|
-
return this.anchor.is(selection.anchor) && this.focus.is(selection.focus) && this.format === selection.format;
|
|
3382
|
+
return this.anchor.is(selection.anchor) && this.focus.is(selection.focus) && this.format === selection.format && this.style === selection.style;
|
|
3342
3383
|
}
|
|
3343
3384
|
isBackward() {
|
|
3344
3385
|
return this.focus.isBefore(this.anchor);
|
|
@@ -3452,13 +3493,17 @@ class RangeSelection {
|
|
|
3452
3493
|
clone() {
|
|
3453
3494
|
const anchor = this.anchor;
|
|
3454
3495
|
const focus = this.focus;
|
|
3455
|
-
const selection = new RangeSelection($createPoint(anchor.key, anchor.offset, anchor.type), $createPoint(focus.key, focus.offset, focus.type), this.format);
|
|
3496
|
+
const selection = new RangeSelection($createPoint(anchor.key, anchor.offset, anchor.type), $createPoint(focus.key, focus.offset, focus.type), this.format, this.style);
|
|
3456
3497
|
return selection;
|
|
3457
3498
|
}
|
|
3458
3499
|
toggleFormat(format) {
|
|
3459
3500
|
this.format = toggleTextFormatType(this.format, format, null);
|
|
3460
3501
|
this.dirty = true;
|
|
3461
3502
|
}
|
|
3503
|
+
setStyle(style) {
|
|
3504
|
+
this.style = style;
|
|
3505
|
+
this.dirty = true;
|
|
3506
|
+
}
|
|
3462
3507
|
hasFormat(type) {
|
|
3463
3508
|
const formatFlag = TEXT_TYPE_TO_FORMAT[type];
|
|
3464
3509
|
return (this.format & formatFlag) !== 0;
|
|
@@ -3487,10 +3532,11 @@ class RangeSelection {
|
|
|
3487
3532
|
const focus = this.focus;
|
|
3488
3533
|
const isBefore = this.isCollapsed() || anchor.isBefore(focus);
|
|
3489
3534
|
const format = this.format;
|
|
3535
|
+
const style = this.style;
|
|
3490
3536
|
if (isBefore && anchor.type === 'element') {
|
|
3491
|
-
$transferStartingElementPointToTextPoint(anchor, focus, format);
|
|
3537
|
+
$transferStartingElementPointToTextPoint(anchor, focus, format, style);
|
|
3492
3538
|
} else if (!isBefore && focus.type === 'element') {
|
|
3493
|
-
$transferStartingElementPointToTextPoint(focus, anchor, format);
|
|
3539
|
+
$transferStartingElementPointToTextPoint(focus, anchor, format, style);
|
|
3494
3540
|
}
|
|
3495
3541
|
const selectedNodes = this.getNodes();
|
|
3496
3542
|
const selectedNodesLength = selectedNodes.length;
|
|
@@ -3569,12 +3615,15 @@ class RangeSelection {
|
|
|
3569
3615
|
return;
|
|
3570
3616
|
}
|
|
3571
3617
|
const firstNodeFormat = firstNode.getFormat();
|
|
3572
|
-
|
|
3618
|
+
const firstNodeStyle = firstNode.getStyle();
|
|
3619
|
+
if (startOffset === endOffset && (firstNodeFormat !== format || firstNodeStyle !== style)) {
|
|
3573
3620
|
if (firstNode.getTextContent() === '') {
|
|
3574
3621
|
firstNode.setFormat(format);
|
|
3622
|
+
firstNode.setStyle(style);
|
|
3575
3623
|
} else {
|
|
3576
3624
|
const textNode = $createTextNode(text);
|
|
3577
3625
|
textNode.setFormat(format);
|
|
3626
|
+
textNode.setStyle(style);
|
|
3578
3627
|
textNode.select();
|
|
3579
3628
|
if (startOffset === 0) {
|
|
3580
3629
|
firstNode.insertBefore(textNode, false);
|
|
@@ -3601,6 +3650,7 @@ class RangeSelection {
|
|
|
3601
3650
|
this.anchor.offset -= text.length;
|
|
3602
3651
|
} else {
|
|
3603
3652
|
this.format = firstNodeFormat;
|
|
3653
|
+
this.style = firstNodeStyle;
|
|
3604
3654
|
}
|
|
3605
3655
|
}
|
|
3606
3656
|
} else {
|
|
@@ -4787,7 +4837,7 @@ function $isBlockElementNode(node) {
|
|
|
4787
4837
|
|
|
4788
4838
|
function internalMakeRangeSelection(anchorKey, anchorOffset, focusKey, focusOffset, anchorType, focusType) {
|
|
4789
4839
|
const editorState = getActiveEditorState();
|
|
4790
|
-
const selection = new RangeSelection($createPoint(anchorKey, anchorOffset, anchorType), $createPoint(focusKey, focusOffset, focusType), 0);
|
|
4840
|
+
const selection = new RangeSelection($createPoint(anchorKey, anchorOffset, anchorType), $createPoint(focusKey, focusOffset, focusType), 0, '');
|
|
4791
4841
|
selection.dirty = true;
|
|
4792
4842
|
editorState._selection = selection;
|
|
4793
4843
|
return selection;
|
|
@@ -4795,7 +4845,7 @@ function internalMakeRangeSelection(anchorKey, anchorOffset, focusKey, focusOffs
|
|
|
4795
4845
|
function $createRangeSelection() {
|
|
4796
4846
|
const anchor = $createPoint('root', 0, 'element');
|
|
4797
4847
|
const focus = $createPoint('root', 0, 'element');
|
|
4798
|
-
return new RangeSelection(anchor, focus, 0);
|
|
4848
|
+
return new RangeSelection(anchor, focus, 0, '');
|
|
4799
4849
|
}
|
|
4800
4850
|
function $createNodeSelection() {
|
|
4801
4851
|
return new NodeSelection(new Set());
|
|
@@ -4859,7 +4909,7 @@ function internalCreateRangeSelection(lastSelection, domSelection, editor) {
|
|
|
4859
4909
|
return null;
|
|
4860
4910
|
}
|
|
4861
4911
|
const [resolvedAnchorPoint, resolvedFocusPoint] = resolvedSelectionPoints;
|
|
4862
|
-
return new RangeSelection(resolvedAnchorPoint, resolvedFocusPoint, !$isRangeSelection(lastSelection) ? 0 : lastSelection.format);
|
|
4912
|
+
return new RangeSelection(resolvedAnchorPoint, resolvedFocusPoint, !$isRangeSelection(lastSelection) ? 0 : lastSelection.format, !$isRangeSelection(lastSelection) ? '' : lastSelection.style);
|
|
4863
4913
|
}
|
|
4864
4914
|
function $getSelection() {
|
|
4865
4915
|
const editorState = getActiveEditorState();
|
|
@@ -5055,13 +5105,17 @@ function updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
|
|
|
5055
5105
|
const nextAnchorOffset = anchor.offset;
|
|
5056
5106
|
const nextFocusOffset = focus.offset;
|
|
5057
5107
|
const nextFormat = nextSelection.format;
|
|
5108
|
+
const nextStyle = nextSelection.style;
|
|
5058
5109
|
const isCollapsed = nextSelection.isCollapsed();
|
|
5059
5110
|
let nextAnchorNode = anchorDOM;
|
|
5060
5111
|
let nextFocusNode = focusDOM;
|
|
5061
|
-
let
|
|
5112
|
+
let anchorFormatOrStyleChanged = false;
|
|
5062
5113
|
if (anchor.type === 'text') {
|
|
5063
5114
|
nextAnchorNode = getDOMTextNode(anchorDOM);
|
|
5064
|
-
|
|
5115
|
+
const anchorNode = anchor.getNode();
|
|
5116
|
+
anchorFormatOrStyleChanged = anchorNode.getFormat() !== nextFormat || anchorNode.getStyle() !== nextStyle;
|
|
5117
|
+
} else if ($isRangeSelection(prevSelection) && prevSelection.anchor.type === 'text') {
|
|
5118
|
+
anchorFormatOrStyleChanged = true;
|
|
5065
5119
|
}
|
|
5066
5120
|
if (focus.type === 'text') {
|
|
5067
5121
|
nextFocusNode = getDOMTextNode(focusDOM);
|
|
@@ -5072,8 +5126,8 @@ function updateDOMSelection(prevSelection, nextSelection, editor, domSelection,
|
|
|
5072
5126
|
if (nextAnchorNode === null || nextFocusNode === null) {
|
|
5073
5127
|
return;
|
|
5074
5128
|
}
|
|
5075
|
-
if (isCollapsed && (prevSelection === null ||
|
|
5076
|
-
markCollapsedSelectionFormat(nextFormat, nextAnchorOffset, anchorKey, performance.now());
|
|
5129
|
+
if (isCollapsed && (prevSelection === null || anchorFormatOrStyleChanged || $isRangeSelection(prevSelection) && (prevSelection.format !== nextFormat || prevSelection.style !== nextStyle))) {
|
|
5130
|
+
markCollapsedSelectionFormat(nextFormat, nextStyle, nextAnchorOffset, anchorKey, performance.now());
|
|
5077
5131
|
}
|
|
5078
5132
|
|
|
5079
5133
|
// Diff against the native DOM selection to ensure we don't do
|
|
@@ -8172,10 +8226,12 @@ function createEditor(editorConfig) {
|
|
|
8172
8226
|
for (let i = 0; i < nodes.length; i++) {
|
|
8173
8227
|
let klass = nodes[i];
|
|
8174
8228
|
let replacementClass = null;
|
|
8229
|
+
let replacementKlass = null;
|
|
8175
8230
|
if (typeof klass !== 'function') {
|
|
8176
8231
|
const options = klass;
|
|
8177
8232
|
klass = options.replace;
|
|
8178
8233
|
replacementClass = options.with;
|
|
8234
|
+
replacementKlass = options.withKlass ? options.withKlass : null;
|
|
8179
8235
|
}
|
|
8180
8236
|
// Ensure custom nodes implement required methods.
|
|
8181
8237
|
{
|
|
@@ -8217,6 +8273,7 @@ function createEditor(editorConfig) {
|
|
|
8217
8273
|
registeredNodes.set(type, {
|
|
8218
8274
|
klass,
|
|
8219
8275
|
replace: replacementClass,
|
|
8276
|
+
replaceWithKlass: replacementKlass,
|
|
8220
8277
|
transforms: new Set()
|
|
8221
8278
|
});
|
|
8222
8279
|
}
|
|
@@ -8364,7 +8421,7 @@ class LexicalEditor {
|
|
|
8364
8421
|
mutations.delete(listener);
|
|
8365
8422
|
};
|
|
8366
8423
|
}
|
|
8367
|
-
|
|
8424
|
+
registerNodeTransformToKlass(klass, listener) {
|
|
8368
8425
|
const type = klass.getType();
|
|
8369
8426
|
const registeredNode = this._nodes.get(type);
|
|
8370
8427
|
if (registeredNode === undefined) {
|
|
@@ -8374,9 +8431,19 @@ class LexicalEditor {
|
|
|
8374
8431
|
}
|
|
8375
8432
|
const transforms = registeredNode.transforms;
|
|
8376
8433
|
transforms.add(listener);
|
|
8377
|
-
|
|
8434
|
+
return registeredNode;
|
|
8435
|
+
}
|
|
8436
|
+
registerNodeTransform(klass, listener) {
|
|
8437
|
+
const registeredNode = this.registerNodeTransformToKlass(klass, listener);
|
|
8438
|
+
const registeredNodes = [registeredNode];
|
|
8439
|
+
const replaceWithKlass = registeredNode.replaceWithKlass;
|
|
8440
|
+
if (replaceWithKlass != null) {
|
|
8441
|
+
const registeredReplaceWithNode = this.registerNodeTransformToKlass(replaceWithKlass, listener);
|
|
8442
|
+
registeredNodes.push(registeredReplaceWithNode);
|
|
8443
|
+
}
|
|
8444
|
+
markAllNodesAsDirty(this, klass.getType());
|
|
8378
8445
|
return () => {
|
|
8379
|
-
transforms.delete(listener);
|
|
8446
|
+
registeredNodes.forEach(node => node.transforms.delete(listener));
|
|
8380
8447
|
};
|
|
8381
8448
|
}
|
|
8382
8449
|
hasNodes(nodes) {
|
|
@@ -8668,6 +8735,7 @@ exports.KEY_ARROW_RIGHT_COMMAND = KEY_ARROW_RIGHT_COMMAND;
|
|
|
8668
8735
|
exports.KEY_ARROW_UP_COMMAND = KEY_ARROW_UP_COMMAND;
|
|
8669
8736
|
exports.KEY_BACKSPACE_COMMAND = KEY_BACKSPACE_COMMAND;
|
|
8670
8737
|
exports.KEY_DELETE_COMMAND = KEY_DELETE_COMMAND;
|
|
8738
|
+
exports.KEY_DOWN_COMMAND = KEY_DOWN_COMMAND;
|
|
8671
8739
|
exports.KEY_ENTER_COMMAND = KEY_ENTER_COMMAND;
|
|
8672
8740
|
exports.KEY_ESCAPE_COMMAND = KEY_ESCAPE_COMMAND;
|
|
8673
8741
|
exports.KEY_MODIFIER_COMMAND = KEY_MODIFIER_COMMAND;
|