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 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
- dom.style.setProperty('padding-inline-start', indent === 0 ? '' : indent * 20 + 'px');
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
- if (!selection.dirty && selection.isCollapsed() && !$isRootNode(selection.anchor.getNode())) {
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() || selection.anchor.getNode().getFormat() !== selection.format) {
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
- if (startOffset === endOffset && firstNodeFormat !== format) {
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 anchorFormatChanged = false;
5112
+ let anchorFormatOrStyleChanged = false;
5062
5113
  if (anchor.type === 'text') {
5063
5114
  nextAnchorNode = getDOMTextNode(anchorDOM);
5064
- anchorFormatChanged = anchor.getNode().getFormat() !== nextFormat;
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 || anchorFormatChanged || $isRangeSelection(prevSelection) && prevSelection.format !== nextFormat)) {
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
- registerNodeTransform(klass, listener) {
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
- markAllNodesAsDirty(this, type);
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;