lexical 0.1.10 → 0.1.11

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.
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict
8
+ */
9
+
10
+ import type {LexicalNode, RangeSelection} from 'lexical';
11
+
12
+ import {LinkNode} from 'lexical/LinkNode';
13
+
14
+ declare export class AutoLinkNode extends LinkNode {
15
+ static getType(): string;
16
+ // $FlowFixMe[incompatible-extend]
17
+ static clone(node: AutoLinkNode): AutoLinkNode;
18
+ insertNewAfter(selection: RangeSelection): null | LexicalNode;
19
+ // $FlowFixMe[incompatible-extend]
20
+ canInsertTextAfter(): true;
21
+ }
22
+ declare export function $createAutoLinkNode(url: string): AutoLinkNode;
23
+ declare export function $isAutoLinkNode(
24
+ node: ?LexicalNode,
25
+ ): boolean %checks(node instanceof AutoLinkNode);
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict
8
+ */
9
+
10
+ import type {
11
+ EditorConfig,
12
+ EditorThemeClasses,
13
+ LexicalNode,
14
+ NodeKey,
15
+ } from 'lexical';
16
+
17
+ import {TextNode} from 'lexical';
18
+
19
+ declare export class CodeHighlightNode extends TextNode {
20
+ __highlightType: ?string;
21
+ constructor(text: string, highlightType?: string, key?: NodeKey): void;
22
+ static getType(): string;
23
+ static clone(node: CodeHighlightNode): CodeHighlightNode;
24
+ createDOM<EditorContext>(config: EditorConfig<EditorContext>): HTMLElement;
25
+ updateDOM<EditorContext>(
26
+ // $FlowFixMe
27
+ prevNode: CodeHighlightNode,
28
+ dom: HTMLElement,
29
+ config: EditorConfig<EditorContext>,
30
+ ): boolean;
31
+ setFormat(format: number): this;
32
+ }
33
+ declare function getHighlightThemeClass(
34
+ theme: EditorThemeClasses,
35
+ highlightType: ?string,
36
+ ): ?string;
37
+ declare export function $createCodeHighlightNode(
38
+ text: string,
39
+ highlightType?: string,
40
+ ): CodeHighlightNode;
41
+ declare export function $isCodeHighlightNode(
42
+ node: ?LexicalNode,
43
+ ): boolean %checks(node instanceof CodeHighlightNode);
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict
8
+ */
9
+
10
+ import type {
11
+ EditorConfig,
12
+ LexicalNode,
13
+ NodeKey,
14
+ ParagraphNode,
15
+ RangeSelection,
16
+ } from 'lexical';
17
+ import type {CodeHighlightNode} from 'lexical/CodeHighlightNode';
18
+
19
+ import {ElementNode} from 'lexical';
20
+
21
+ declare export class CodeNode extends ElementNode {
22
+ static getType(): string;
23
+ static clone(node: CodeNode): CodeNode;
24
+ constructor(key?: NodeKey): void;
25
+ createDOM<EditorContext>(config: EditorConfig<EditorContext>): HTMLElement;
26
+ updateDOM(prevNode: CodeNode, dom: HTMLElement): boolean;
27
+ insertNewAfter(
28
+ selection: RangeSelection,
29
+ ): null | ParagraphNode | CodeHighlightNode;
30
+ canInsertTab(): true;
31
+ collapseAtStart(): true;
32
+ setLanguage(language: string): void;
33
+ getLanguage(): string | void;
34
+ }
35
+ declare export function $createCodeNode(): CodeNode;
36
+ declare export function $isCodeNode(
37
+ node: ?LexicalNode,
38
+ ): boolean %checks(node instanceof CodeNode);
39
+
40
+ declare export function getFirstCodeHighlightNodeOfLine(
41
+ anchor: LexicalNode,
42
+ ): ?CodeHighlightNode;
43
+
44
+ declare export function getLastCodeHighlightNodeOfLine(
45
+ anchor: LexicalNode,
46
+ ): ?CodeHighlightNode;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict
8
+ */
9
+
10
+ import type {EditorConfig, LexicalNode, NodeKey} from 'lexical';
11
+
12
+ import {TextNode} from 'lexical';
13
+
14
+ declare export class HashtagNode extends TextNode {
15
+ static getType(): string;
16
+ static clone(node: HashtagNode): HashtagNode;
17
+ constructor(text: string, key?: NodeKey): void;
18
+ createDOM<EditorContext>(config: EditorConfig<EditorContext>): HTMLElement;
19
+ setTextContent(text: string): TextNode;
20
+ canInsertTextBefore(): boolean;
21
+ canInsertTextAfter(): boolean;
22
+ }
23
+ declare export function $toggleHashtag(node: TextNode): TextNode;
24
+ declare export function $createHashtagNode(text?: string): TextNode;
25
+ declare export function $isHashtagNode(
26
+ node: ?LexicalNode,
27
+ ): boolean %checks(node instanceof HashtagNode);
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict
8
+ */
9
+
10
+ import type {EditorConfig, LexicalNode, NodeKey, ParagraphNode} from 'lexical';
11
+
12
+ import {ElementNode} from 'lexical';
13
+
14
+ type HeadingTagType = 'h1' | 'h2' | 'h3' | 'h4' | 'h5';
15
+ declare export class HeadingNode extends ElementNode {
16
+ __tag: HeadingTagType;
17
+ static getType(): string;
18
+ static clone(node: HeadingNode): HeadingNode;
19
+ constructor(tag: HeadingTagType, key?: NodeKey): void;
20
+ getTag(): HeadingTagType;
21
+ createDOM<EditorContext>(config: EditorConfig<EditorContext>): HTMLElement;
22
+ updateDOM(prevNode: HeadingNode, dom: HTMLElement): boolean;
23
+ insertNewAfter(): ParagraphNode;
24
+ collapseAtStart(): true;
25
+ }
26
+ declare export function $createHeadingNode(
27
+ headingTag: HeadingTagType,
28
+ ): HeadingNode;
29
+
30
+ declare export function $isHeadingNode(
31
+ node: ?LexicalNode,
32
+ ): boolean %checks(node instanceof HeadingNode);
package/Lexical.dev.js CHANGED
@@ -43,7 +43,7 @@ const IS_ALIGN_CENTER = 2;
43
43
  const IS_ALIGN_RIGHT = 3;
44
44
  const IS_ALIGN_JUSTIFY = 4; // Reconciliation
45
45
 
46
- const NO_BREAK_SPACE_CHAR = '\u00A0';
46
+ const ZERO_WIDTH_CHAR = '\u200b';
47
47
  const RTL = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC';
48
48
  const LTR = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6' + '\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u200E\u2C00-\uFB1C' + '\uFE00-\uFE6F\uFEFD-\uFFFF';
49
49
  const RTL_REGEX = new RegExp('^[^' + LTR + ']*[' + RTL + ']');
@@ -70,55 +70,6 @@ const TEXT_MODE_TO_TYPE = {
70
70
  token: IS_TOKEN
71
71
  };
72
72
 
73
- /**
74
- * Copyright (c) Meta Platforms, Inc. and affiliates.
75
- *
76
- * This source code is licensed under the MIT license found in the
77
- * LICENSE file in the root directory of this source tree.
78
- *
79
- *
80
- */
81
- function resolveElement(element, isBackward, focusOffset) {
82
- const parent = element.getParent();
83
- let offset = focusOffset;
84
- let block = element;
85
-
86
- if (parent !== null) {
87
- if (isBackward && focusOffset === 0) {
88
- offset = block.getIndexWithinParent();
89
- block = parent;
90
- } else if (!isBackward && focusOffset === block.getChildrenSize()) {
91
- offset = block.getIndexWithinParent() + 1;
92
- block = parent;
93
- }
94
- }
95
-
96
- return block.getChildAtIndex(isBackward ? offset - 1 : offset);
97
- }
98
-
99
- function getPossibleDecoratorNode(focus, isBackward) {
100
- const focusOffset = focus.offset;
101
-
102
- if (focus.type === 'element') {
103
- const block = focus.getNode();
104
- return resolveElement(block, isBackward, focusOffset);
105
- } else {
106
- const focusNode = focus.getNode();
107
-
108
- if (isBackward && focusOffset === 0 || !isBackward && focusOffset === focusNode.getTextContentSize()) {
109
- const possibleNode = isBackward ? focusNode.getPreviousSibling() : focusNode.getNextSibling();
110
-
111
- if (possibleNode === null) {
112
- return resolveElement(focusNode.getParentOrThrow(), isBackward, focusNode.getIndexWithinParent() + (isBackward ? 0 : 1));
113
- }
114
-
115
- return possibleNode;
116
- }
117
- }
118
-
119
- return null;
120
- }
121
-
122
73
  /**
123
74
  * Copyright (c) Meta Platforms, Inc. and affiliates.
124
75
  *
@@ -511,7 +462,7 @@ function $updateTextNodeFromDOMContent(textNode, textContent, anchorOffset, focu
511
462
  const isComposing = node.isComposing();
512
463
  let normalizedTextContent = textContent;
513
464
 
514
- if ((isComposing || compositionEnd) && textContent[textContent.length - 1] === NO_BREAK_SPACE_CHAR) {
465
+ if ((isComposing || compositionEnd) && textContent[textContent.length - 1] === ZERO_WIDTH_CHAR) {
515
466
  normalizedTextContent = textContent.slice(0, -1);
516
467
  }
517
468
 
@@ -580,12 +531,17 @@ function $shouldPreventDefaultAndInsertText(selection, text, isBeforeInput) {
580
531
  const anchor = selection.anchor;
581
532
  const focus = selection.focus;
582
533
  const anchorNode = anchor.getNode();
583
- return anchor.key !== focus.key || // If we're working with a range that is not during composition.
534
+ const domSelection = window.getSelection();
535
+ const domAnchorNode = domSelection !== null ? domSelection.anchorNode : null;
536
+ const anchorKey = anchor.key;
537
+ const backingAnchorElement = getActiveEditor().getElementByKey(anchorKey);
538
+ return anchorKey !== focus.key || // If we're working with a non-text node.
539
+ !$isTextNode(anchorNode) || // If we're working with a range that is not during composition.
584
540
  anchor.offset !== focus.offset && !anchorNode.isComposing() || // If the text length is more than a single character and we're either
585
541
  // dealing with this in "beforeinput" or where the node has already recently
586
542
  // been changed (thus is dirty).
587
- (isBeforeInput || anchorNode.isDirty()) && text.length > 1 || // If we're working with a non-text node.
588
- !$isTextNode(anchorNode) || // Check if we're changing from bold to italics, or some other format.
543
+ (isBeforeInput || anchorNode.isDirty()) && text.length > 1 || // If the DOM selection element is not the same as the backing node
544
+ backingAnchorElement !== null && !anchorNode.isComposing() && domAnchorNode !== getDOMTextNode(backingAnchorElement) || // Check if we're changing from bold to italics, or some other format.
589
545
  anchorNode.getFormat() !== selection.format || // One last set of heuristics to check against.
590
546
  $shouldInsertTextAfterOrBeforeTextNode(selection, anchorNode);
591
547
  }
@@ -765,6 +721,47 @@ function $nodesOfType(klass) {
765
721
  return nodesOfType;
766
722
  }
767
723
 
724
+ function resolveElement(element, isBackward, focusOffset) {
725
+ const parent = element.getParent();
726
+ let offset = focusOffset;
727
+ let block = element;
728
+
729
+ if (parent !== null) {
730
+ if (isBackward && focusOffset === 0) {
731
+ offset = block.getIndexWithinParent();
732
+ block = parent;
733
+ } else if (!isBackward && focusOffset === block.getChildrenSize()) {
734
+ offset = block.getIndexWithinParent() + 1;
735
+ block = parent;
736
+ }
737
+ }
738
+
739
+ return block.getChildAtIndex(isBackward ? offset - 1 : offset);
740
+ }
741
+
742
+ function $getDecoratorNode(focus, isBackward) {
743
+ const focusOffset = focus.offset;
744
+
745
+ if (focus.type === 'element') {
746
+ const block = focus.getNode();
747
+ return resolveElement(block, isBackward, focusOffset);
748
+ } else {
749
+ const focusNode = focus.getNode();
750
+
751
+ if (isBackward && focusOffset === 0 || !isBackward && focusOffset === focusNode.getTextContentSize()) {
752
+ const possibleNode = isBackward ? focusNode.getPreviousSibling() : focusNode.getNextSibling();
753
+
754
+ if (possibleNode === null) {
755
+ return resolveElement(focusNode.getParentOrThrow(), isBackward, focusNode.getIndexWithinParent() + (isBackward ? 0 : 1));
756
+ }
757
+
758
+ return possibleNode;
759
+ }
760
+ }
761
+
762
+ return null;
763
+ }
764
+
768
765
  /**
769
766
  * Copyright (c) Meta Platforms, Inc. and affiliates.
770
767
  *
@@ -5006,7 +5003,7 @@ class RangeSelection {
5006
5003
  const anchor = this.anchor;
5007
5004
  const collapse = alter === 'move'; // Handle the selection movement around decorators.
5008
5005
 
5009
- const possibleNode = getPossibleDecoratorNode(focus, isBackward);
5006
+ const possibleNode = $getDecoratorNode(focus, isBackward);
5010
5007
 
5011
5008
  if ($isDecoratorNode(possibleNode) && !possibleNode.isIsolated()) {
5012
5009
  const sibling = isBackward ? possibleNode.getPreviousSibling() : possibleNode.getNextSibling();
@@ -6680,7 +6677,21 @@ function onCompositionStart(event, editor) {
6680
6677
 
6681
6678
  function onCompositionEndInternal(event, editor) {
6682
6679
  editor.update(() => {
6683
- $setCompositionKey(null);
6680
+ const compositionKey = editor._compositionKey;
6681
+ $setCompositionKey(null); // Handle termination of composition, as it can sometimes
6682
+ // move to an adjacent DOM node when backspacing.
6683
+
6684
+ if (compositionKey !== null && event.data === '') {
6685
+ const node = $getNodeByKey(compositionKey);
6686
+ const textNode = getDOMTextNode(editor.getElementByKey(compositionKey));
6687
+
6688
+ if (textNode !== null && $isTextNode(node)) {
6689
+ $updateTextNodeFromDOMContent(node, textNode.nodeValue, null, null, true);
6690
+ }
6691
+
6692
+ return;
6693
+ }
6694
+
6684
6695
  $updateSelectedTextFromDOM(editor, true);
6685
6696
  });
6686
6697
  }
@@ -6780,11 +6791,6 @@ function onKeyDown(event, editor) {
6780
6791
  }
6781
6792
  }
6782
6793
 
6783
- function isRootEditable(editor) {
6784
- const rootElement = editor.getRootElement();
6785
- return rootElement !== null && rootElement.contentEditable === 'true';
6786
- }
6787
-
6788
6794
  function getRootElementRemoveHandles(rootElement) {
6789
6795
  // $FlowFixMe: internal field
6790
6796
  let eventHandles = rootElement.__lexicalEventHandles;
@@ -6796,11 +6802,6 @@ function getRootElementRemoveHandles(rootElement) {
6796
6802
  }
6797
6803
 
6798
6804
  return eventHandles;
6799
- }
6800
-
6801
- function clearRootElementRemoveHandles(rootElement) {
6802
- // $FlowFixMe: internal field
6803
- rootElement._lexicalEventHandles = [];
6804
6805
  } // Mapping root editors to their active nested editors, contains nested editors
6805
6806
  // mapping only, so if root editor is selected map will have no reference to free up memory
6806
6807
 
@@ -6861,11 +6862,11 @@ function addRootElementEvents(rootElement, editor) {
6861
6862
  for (let i = 0; i < rootElementEvents.length; i++) {
6862
6863
  const [eventName, onEvent] = rootElementEvents[i];
6863
6864
  const eventHandler = typeof onEvent === 'function' ? event => {
6864
- if (isRootEditable(editor)) {
6865
+ if (!editor.isReadOnly()) {
6865
6866
  onEvent(event, editor);
6866
6867
  }
6867
6868
  } : event => {
6868
- if (isRootEditable(editor)) {
6869
+ if (!editor.isReadOnly()) {
6869
6870
  editor.execCommand(eventName, event);
6870
6871
  }
6871
6872
  };
@@ -6876,30 +6877,37 @@ function addRootElementEvents(rootElement, editor) {
6876
6877
  }
6877
6878
  }
6878
6879
  function removeRootElementEvents(rootElement) {
6879
- rootElementsRegistered--; // We only want to have a single global selectionchange event handler, shared
6880
- // between all editor instances.
6880
+ if (rootElementsRegistered !== 0) {
6881
+ rootElementsRegistered--; // We only want to have a single global selectionchange event handler, shared
6882
+ // between all editor instances.
6881
6883
 
6882
- if (rootElementsRegistered === 0) {
6883
- const doc = rootElement.ownerDocument;
6884
- doc.removeEventListener('selectionchange', onDocumentSelectionChange);
6884
+ if (rootElementsRegistered === 0) {
6885
+ const doc = rootElement.ownerDocument;
6886
+ doc.removeEventListener('selectionchange', onDocumentSelectionChange);
6887
+ }
6885
6888
  } // $FlowFixMe: internal field
6886
6889
 
6887
6890
 
6888
- cleanActiveNestedEditorsMap(rootElement.__lexicalEditor); // $FlowFixMe: internal field
6891
+ const editor = rootElement.__lexicalEditor;
6892
+
6893
+ if (editor != null) {
6894
+ cleanActiveNestedEditorsMap(editor); // $FlowFixMe: internal field
6895
+
6896
+ rootElement.__lexicalEditor = null;
6897
+ }
6889
6898
 
6890
- rootElement.__lexicalEditor = null;
6891
6899
  const removeHandles = getRootElementRemoveHandles(rootElement);
6892
6900
 
6893
6901
  for (let i = 0; i < removeHandles.length; i++) {
6894
6902
  removeHandles[i]();
6895
- }
6903
+ } // $FlowFixMe: internal field
6896
6904
 
6897
- clearRootElementRemoveHandles(rootElement);
6905
+
6906
+ rootElement.__lexicalEventHandles = [];
6898
6907
  }
6899
6908
 
6900
6909
  function cleanActiveNestedEditorsMap(editor) {
6901
- // $FlowFixMe: internal field
6902
- if (editor._parentEditor) {
6910
+ if (editor._parentEditor !== null) {
6903
6911
  // For nested editor cleanup map if this editor was marked as active
6904
6912
  const editors = getEditorsToPropagate(editor);
6905
6913
  const rootEditor = editors[editors.length - 1];
@@ -7079,7 +7087,7 @@ function setTextContent(nextText, dom, node) {
7079
7087
  const firstChild = dom.firstChild;
7080
7088
  const isComposing = node.isComposing(); // Always add a suffix if we're composing a node
7081
7089
 
7082
- const suffix = isComposing ? NO_BREAK_SPACE_CHAR : '';
7090
+ const suffix = isComposing ? ZERO_WIDTH_CHAR : '';
7083
7091
  const text = nextText + suffix;
7084
7092
 
7085
7093
  if (firstChild == null) {
@@ -7903,6 +7911,7 @@ class BaseLexicalEditor {
7903
7911
  command: [new Set(), new Set(), new Set(), new Set(), new Set()],
7904
7912
  decorator: new Set(),
7905
7913
  mutation: new Map(),
7914
+ readonly: new Set(),
7906
7915
  root: new Set(),
7907
7916
  textcontent: new Set(),
7908
7917
  update: new Set()
@@ -7927,6 +7936,7 @@ class BaseLexicalEditor {
7927
7936
  this._key = generateRandomKey();
7928
7937
  this._onError = onError;
7929
7938
  this._htmlConversions = htmlConversions;
7939
+ this._readOnly = false;
7930
7940
  }
7931
7941
 
7932
7942
  isComposing() {
@@ -8165,6 +8175,21 @@ class BaseLexicalEditor {
8165
8175
  if (rootElement !== null) {
8166
8176
  rootElement.blur();
8167
8177
  }
8178
+
8179
+ const domSelection = window.getSelection();
8180
+
8181
+ if (domSelection !== null) {
8182
+ domSelection.removeAllRanges();
8183
+ }
8184
+ }
8185
+
8186
+ isReadOnly() {
8187
+ return this._readOnly;
8188
+ }
8189
+
8190
+ setReadOnly(isReadOnly) {
8191
+ this._readOnly = isReadOnly;
8192
+ triggerListeners('readonly', getSelf(this), true, isReadOnly);
8168
8193
  }
8169
8194
 
8170
8195
  } // We export this to make the addListener types work properly.
@@ -8179,7 +8204,7 @@ class BaseLexicalEditor {
8179
8204
  *
8180
8205
  *
8181
8206
  */
8182
- const VERSION = '0.1.10';
8207
+ const VERSION = '0.1.11';
8183
8208
 
8184
8209
  /**
8185
8210
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -8232,6 +8257,7 @@ exports.$createNodeSelection = $createEmptyObjectSelection;
8232
8257
  exports.$createParagraphNode = $createParagraphNode;
8233
8258
  exports.$createRangeSelection = $createEmptyRangeSelection;
8234
8259
  exports.$createTextNode = $createTextNode;
8260
+ exports.$getDecoratorNode = $getDecoratorNode;
8235
8261
  exports.$getNearestNodeFromDOMNode = $getNearestNodeFromDOMNode;
8236
8262
  exports.$getNodeByKey = $getNodeByKey;
8237
8263
  exports.$getPreviousSelection = $getPreviousSelection;