@lexical/react 0.7.8 → 0.8.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.
Files changed (49) hide show
  1. package/DEPRECATED_useLexical.dev.js +3 -4
  2. package/DEPRECATED_useLexicalCanShowPlaceholder.dev.js +1 -3
  3. package/DEPRECATED_useLexicalCharacterLimit.dev.js +7 -39
  4. package/DEPRECATED_useLexicalEditor.dev.js +1 -3
  5. package/DEPRECATED_useLexicalPlainText.dev.js +4 -1
  6. package/DEPRECATED_useLexicalRichText.dev.js +4 -1
  7. package/LexicalAutoEmbedPlugin.dev.js +0 -11
  8. package/LexicalAutoFocusPlugin.dev.js +0 -1
  9. package/LexicalAutoLinkPlugin.dev.js +7 -50
  10. package/LexicalBlockWithAlignableContents.d.ts +1 -1
  11. package/LexicalBlockWithAlignableContents.dev.js +0 -11
  12. package/LexicalCharacterLimitPlugin.dev.js +7 -46
  13. package/LexicalCheckListPlugin.dev.js +10 -48
  14. package/LexicalClearEditorPlugin.dev.js +1 -1
  15. package/LexicalCollaborationContext.dev.js +0 -3
  16. package/LexicalCollaborationPlugin.dev.js +11 -36
  17. package/LexicalCollaborationPlugin.prod.js +9 -9
  18. package/LexicalComposer.dev.js +6 -10
  19. package/LexicalComposerContext.dev.js +0 -6
  20. package/LexicalContentEditable.d.ts +16 -25
  21. package/LexicalContentEditable.dev.js +10 -14
  22. package/LexicalContentEditable.js.flow +15 -20
  23. package/LexicalContentEditable.prod.js +3 -3
  24. package/LexicalDecoratorBlockNode.dev.js +0 -5
  25. package/LexicalHashtagPlugin.dev.js +73 -43
  26. package/LexicalHorizontalRuleNode.dev.js +0 -22
  27. package/LexicalHorizontalRulePlugin.dev.js +0 -4
  28. package/LexicalLinkPlugin.dev.js +4 -10
  29. package/LexicalListPlugin.dev.js +1 -9
  30. package/LexicalListPlugin.prod.js +2 -2
  31. package/LexicalMarkdownShortcutPlugin.dev.js +2 -2
  32. package/LexicalNestedComposer.dev.js +8 -11
  33. package/LexicalNestedComposer.prod.js +3 -3
  34. package/LexicalNodeEventPlugin.dev.js +5 -3
  35. package/LexicalOnChangePlugin.dev.js +1 -1
  36. package/LexicalPlainTextPlugin.dev.js +8 -12
  37. package/LexicalRichTextPlugin.dev.js +8 -12
  38. package/LexicalTabIndentationPlugin.dev.js +1 -3
  39. package/LexicalTableOfContents__EXPERIMENTAL.dev.js +5 -33
  40. package/LexicalTablePlugin.dev.js +5 -19
  41. package/LexicalTreeView.dev.js +13 -62
  42. package/LexicalTreeView.prod.js +13 -13
  43. package/LexicalTypeaheadMenuPlugin.dev.js +7 -96
  44. package/package.json +19 -19
  45. package/shared/useYjsCollaboration.d.ts +1 -1
  46. package/useLexicalEditable.dev.js +1 -5
  47. package/useLexicalIsTextContentEmpty.dev.js +1 -0
  48. package/useLexicalNodeSelection.dev.js +0 -7
  49. package/useLexicalSubscription.dev.js +1 -3
@@ -22,6 +22,7 @@ var plainText = require('@lexical/plain-text');
22
22
  * LICENSE file in the root directory of this source tree.
23
23
  *
24
24
  */
25
+
25
26
  const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
26
27
 
27
28
  /**
@@ -41,12 +42,10 @@ var useLayoutEffect = useLayoutEffectImpl;
41
42
  * LICENSE file in the root directory of this source tree.
42
43
  *
43
44
  */
44
-
45
45
  function canShowPlaceholderFromCurrentEditorState(editor) {
46
46
  const currentCanShowPlaceholder = editor.getEditorState().read(text.$canShowPlaceholderCurry(editor.isComposing()));
47
47
  return currentCanShowPlaceholder;
48
48
  }
49
-
50
49
  function useCanShowPlaceholder(editor) {
51
50
  const [canShowPlaceholder, setCanShowPlaceholder] = React.useState(() => canShowPlaceholderFromCurrentEditorState(editor));
52
51
  useLayoutEffect(() => {
@@ -54,7 +53,6 @@ function useCanShowPlaceholder(editor) {
54
53
  const currentCanShowPlaceholder = canShowPlaceholderFromCurrentEditorState(editor);
55
54
  setCanShowPlaceholder(currentCanShowPlaceholder);
56
55
  }
57
-
58
56
  resetCanShowPlaceholder();
59
57
  return utils.mergeRegister(editor.registerUpdateListener(() => {
60
58
  resetCanShowPlaceholder();
@@ -73,8 +71,9 @@ function useCanShowPlaceholder(editor) {
73
71
  *
74
72
  */
75
73
  function useDecorators(editor, ErrorBoundary) {
76
- const [decorators, setDecorators] = React.useState(() => editor.getDecorators()); // Subscribe to changes
74
+ const [decorators, setDecorators] = React.useState(() => editor.getDecorators());
77
75
 
76
+ // Subscribe to changes
78
77
  useLayoutEffect(() => {
79
78
  return editor.registerDecoratorListener(nextDecorators => {
80
79
  reactDom.flushSync(() => {
@@ -87,12 +86,12 @@ function useDecorators(editor, ErrorBoundary) {
87
86
  // nothing will be rendered on initial pass. We can get around that by
88
87
  // ensuring that we set the value.
89
88
  setDecorators(editor.getDecorators());
90
- }, [editor]); // Return decorators defined as React Portals
89
+ }, [editor]);
91
90
 
91
+ // Return decorators defined as React Portals
92
92
  return React.useMemo(() => {
93
93
  const decoratedPortals = [];
94
94
  const decoratorKeys = Object.keys(decorators);
95
-
96
95
  for (let i = 0; i < decoratorKeys.length; i++) {
97
96
  const nodeKey = decoratorKeys[i];
98
97
  const reactDecorator = /*#__PURE__*/React.createElement(ErrorBoundary, {
@@ -101,12 +100,10 @@ function useDecorators(editor, ErrorBoundary) {
101
100
  fallback: null
102
101
  }, decorators[nodeKey]));
103
102
  const element = editor.getElementByKey(nodeKey);
104
-
105
103
  if (element !== null) {
106
104
  decoratedPortals.push( /*#__PURE__*/reactDom.createPortal(reactDecorator, element));
107
105
  }
108
106
  }
109
-
110
107
  return decoratedPortals;
111
108
  }, [ErrorBoundary, decorators, editor]);
112
109
  }
@@ -120,7 +117,9 @@ function useDecorators(editor, ErrorBoundary) {
120
117
  */
121
118
  function usePlainTextSetup(editor) {
122
119
  useLayoutEffect(() => {
123
- return utils.mergeRegister(plainText.registerPlainText(editor), dragon.registerDragonSupport(editor)); // We only do this for init
120
+ return utils.mergeRegister(plainText.registerPlainText(editor), dragon.registerDragonSupport(editor));
121
+
122
+ // We only do this for init
124
123
  // eslint-disable-next-line react-hooks/exhaustive-deps
125
124
  }, [editor]);
126
125
  }
@@ -144,18 +143,15 @@ function PlainTextPlugin({
144
143
  content: placeholder
145
144
  }), decorators);
146
145
  }
147
-
148
146
  function Placeholder({
149
147
  content
150
148
  }) {
151
149
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
152
150
  const showPlaceholder = useCanShowPlaceholder(editor);
153
151
  const editable = useLexicalEditable();
154
-
155
152
  if (!showPlaceholder) {
156
153
  return null;
157
154
  }
158
-
159
155
  if (typeof content === 'function') {
160
156
  return content(editable);
161
157
  } else {
@@ -22,6 +22,7 @@ var richText = require('@lexical/rich-text');
22
22
  * LICENSE file in the root directory of this source tree.
23
23
  *
24
24
  */
25
+
25
26
  const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
26
27
 
27
28
  /**
@@ -41,12 +42,10 @@ var useLayoutEffect = useLayoutEffectImpl;
41
42
  * LICENSE file in the root directory of this source tree.
42
43
  *
43
44
  */
44
-
45
45
  function canShowPlaceholderFromCurrentEditorState(editor) {
46
46
  const currentCanShowPlaceholder = editor.getEditorState().read(text.$canShowPlaceholderCurry(editor.isComposing()));
47
47
  return currentCanShowPlaceholder;
48
48
  }
49
-
50
49
  function useCanShowPlaceholder(editor) {
51
50
  const [canShowPlaceholder, setCanShowPlaceholder] = React.useState(() => canShowPlaceholderFromCurrentEditorState(editor));
52
51
  useLayoutEffect(() => {
@@ -54,7 +53,6 @@ function useCanShowPlaceholder(editor) {
54
53
  const currentCanShowPlaceholder = canShowPlaceholderFromCurrentEditorState(editor);
55
54
  setCanShowPlaceholder(currentCanShowPlaceholder);
56
55
  }
57
-
58
56
  resetCanShowPlaceholder();
59
57
  return utils.mergeRegister(editor.registerUpdateListener(() => {
60
58
  resetCanShowPlaceholder();
@@ -73,8 +71,9 @@ function useCanShowPlaceholder(editor) {
73
71
  *
74
72
  */
75
73
  function useDecorators(editor, ErrorBoundary) {
76
- const [decorators, setDecorators] = React.useState(() => editor.getDecorators()); // Subscribe to changes
74
+ const [decorators, setDecorators] = React.useState(() => editor.getDecorators());
77
75
 
76
+ // Subscribe to changes
78
77
  useLayoutEffect(() => {
79
78
  return editor.registerDecoratorListener(nextDecorators => {
80
79
  reactDom.flushSync(() => {
@@ -87,12 +86,12 @@ function useDecorators(editor, ErrorBoundary) {
87
86
  // nothing will be rendered on initial pass. We can get around that by
88
87
  // ensuring that we set the value.
89
88
  setDecorators(editor.getDecorators());
90
- }, [editor]); // Return decorators defined as React Portals
89
+ }, [editor]);
91
90
 
91
+ // Return decorators defined as React Portals
92
92
  return React.useMemo(() => {
93
93
  const decoratedPortals = [];
94
94
  const decoratorKeys = Object.keys(decorators);
95
-
96
95
  for (let i = 0; i < decoratorKeys.length; i++) {
97
96
  const nodeKey = decoratorKeys[i];
98
97
  const reactDecorator = /*#__PURE__*/React.createElement(ErrorBoundary, {
@@ -101,12 +100,10 @@ function useDecorators(editor, ErrorBoundary) {
101
100
  fallback: null
102
101
  }, decorators[nodeKey]));
103
102
  const element = editor.getElementByKey(nodeKey);
104
-
105
103
  if (element !== null) {
106
104
  decoratedPortals.push( /*#__PURE__*/reactDom.createPortal(reactDecorator, element));
107
105
  }
108
106
  }
109
-
110
107
  return decoratedPortals;
111
108
  }, [ErrorBoundary, decorators, editor]);
112
109
  }
@@ -120,7 +117,9 @@ function useDecorators(editor, ErrorBoundary) {
120
117
  */
121
118
  function useRichTextSetup(editor) {
122
119
  useLayoutEffect(() => {
123
- return utils.mergeRegister(richText.registerRichText(editor), dragon.registerDragonSupport(editor)); // We only do this for init
120
+ return utils.mergeRegister(richText.registerRichText(editor), dragon.registerDragonSupport(editor));
121
+
122
+ // We only do this for init
124
123
  // eslint-disable-next-line react-hooks/exhaustive-deps
125
124
  }, [editor]);
126
125
  }
@@ -144,18 +143,15 @@ function RichTextPlugin({
144
143
  content: placeholder
145
144
  }), decorators);
146
145
  }
147
-
148
146
  function Placeholder({
149
147
  content
150
148
  }) {
151
149
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
152
150
  const showPlaceholder = useCanShowPlaceholder(editor);
153
151
  const editable = useLexicalEditable();
154
-
155
152
  if (!showPlaceholder) {
156
153
  return null;
157
154
  }
158
-
159
155
  if (typeof content === 'function') {
160
156
  return content(editable);
161
157
  } else {
@@ -17,22 +17,20 @@ var react = require('react');
17
17
  * LICENSE file in the root directory of this source tree.
18
18
  *
19
19
  */
20
+
20
21
  /**
21
22
  * This plugin adds the ability to indent content using the tab key. Generally, we don't
22
23
  * recommend using this plugin as it could negatively affect acessibility for keyboard
23
24
  * users, causing focus to become trapped within the editor.
24
25
  */
25
-
26
26
  function TabIndentationPlugin() {
27
27
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
28
28
  react.useEffect(() => {
29
29
  return editor.registerCommand(lexical.KEY_TAB_COMMAND, event => {
30
30
  const selection = lexical.$getSelection();
31
-
32
31
  if (!lexical.$isRangeSelection(selection)) {
33
32
  return false;
34
33
  }
35
-
36
34
  event.preventDefault();
37
35
  return editor.dispatchCommand(event.shiftKey ? lexical.OUTDENT_CONTENT_COMMAND : lexical.INDENT_CONTENT_COMMAND, undefined);
38
36
  }, lexical.COMMAND_PRIORITY_EDITOR);
@@ -18,50 +18,39 @@ var react = require('react');
18
18
  * LICENSE file in the root directory of this source tree.
19
19
  *
20
20
  */
21
-
22
21
  function toEntry(heading) {
23
22
  return [heading.getKey(), heading.getTextContent(), heading.getTag()];
24
23
  }
25
-
26
24
  function $insertHeadingIntoTableOfContents(prevHeading, newHeading, currentTableOfContents) {
27
25
  if (newHeading === null) {
28
26
  return currentTableOfContents;
29
27
  }
30
-
31
28
  const newEntry = toEntry(newHeading);
32
29
  let newTableOfContents = [];
33
-
34
30
  if (prevHeading === null) {
35
31
  newTableOfContents = [newEntry, ...currentTableOfContents];
36
32
  } else {
37
33
  for (let i = 0; i < currentTableOfContents.length; i++) {
38
34
  const key = currentTableOfContents[i][0];
39
35
  newTableOfContents.push(currentTableOfContents[i]);
40
-
41
36
  if (key === prevHeading.getKey() && key !== newHeading.getKey()) {
42
37
  newTableOfContents.push(newEntry);
43
38
  }
44
39
  }
45
40
  }
46
-
47
41
  return newTableOfContents;
48
42
  }
49
-
50
43
  function $deleteHeadingFromTableOfContents(key, currentTableOfContents) {
51
44
  const newTableOfContents = [];
52
-
53
45
  for (const heading of currentTableOfContents) {
54
46
  if (heading[0] !== key) {
55
47
  newTableOfContents.push(heading);
56
48
  }
57
49
  }
58
-
59
50
  return newTableOfContents;
60
51
  }
61
-
62
52
  function $updateHeadingInTableOfContents(heading, currentTableOfContents) {
63
53
  const newTableOfContents = [];
64
-
65
54
  for (const oldHeading of currentTableOfContents) {
66
55
  if (oldHeading[0] === heading.getKey()) {
67
56
  newTableOfContents.push(toEntry(heading));
@@ -69,38 +58,30 @@ function $updateHeadingInTableOfContents(heading, currentTableOfContents) {
69
58
  newTableOfContents.push(oldHeading);
70
59
  }
71
60
  }
72
-
73
61
  return newTableOfContents;
74
62
  }
63
+
75
64
  /**
76
65
  * Returns the updated table of contents, placing the given `heading` before the given `prevHeading`. If `prevHeading`
77
66
  * is undefined, `heading` is placed at the start of table of contents
78
67
  */
79
-
80
-
81
68
  function $updateHeadingPosition(prevHeading, heading, currentTableOfContents) {
82
69
  const newTableOfContents = [];
83
70
  const newEntry = toEntry(heading);
84
-
85
71
  if (!prevHeading) {
86
72
  newTableOfContents.push(newEntry);
87
73
  }
88
-
89
74
  for (const oldHeading of currentTableOfContents) {
90
75
  if (oldHeading[0] === heading.getKey()) {
91
76
  continue;
92
77
  }
93
-
94
78
  newTableOfContents.push(oldHeading);
95
-
96
79
  if (prevHeading && oldHeading[0] === prevHeading.getKey()) {
97
80
  newTableOfContents.push(newEntry);
98
81
  }
99
82
  }
100
-
101
83
  return newTableOfContents;
102
84
  }
103
-
104
85
  function LexicalTableOfContentsPlugin({
105
86
  children
106
87
  }) {
@@ -112,61 +93,52 @@ function LexicalTableOfContentsPlugin({
112
93
  editor.getEditorState().read(() => {
113
94
  const root = lexical.$getRoot();
114
95
  const rootChildren = root.getChildren();
115
-
116
96
  for (const child of rootChildren) {
117
97
  if (richText.$isHeadingNode(child)) {
118
98
  currentTableOfContents.push([child.getKey(), child.getTextContent(), child.getTag()]);
119
99
  }
120
100
  }
121
-
122
101
  setTableOfContents(currentTableOfContents);
123
- }); // Listen to updates to heading mutations and update state
102
+ });
124
103
 
104
+ // Listen to updates to heading mutations and update state
125
105
  const removeHeaderMutationListener = editor.registerMutationListener(richText.HeadingNode, mutatedNodes => {
126
106
  editor.getEditorState().read(() => {
127
107
  for (const [nodeKey, mutation] of mutatedNodes) {
128
108
  if (mutation === 'created') {
129
109
  const newHeading = lexical.$getNodeByKey(nodeKey);
130
-
131
110
  if (newHeading !== null) {
132
111
  let prevHeading = newHeading.getPreviousSibling();
133
-
134
112
  while (prevHeading !== null && !richText.$isHeadingNode(prevHeading)) {
135
113
  prevHeading = prevHeading.getPreviousSibling();
136
114
  }
137
-
138
115
  currentTableOfContents = $insertHeadingIntoTableOfContents(prevHeading, newHeading, currentTableOfContents);
139
116
  }
140
117
  } else if (mutation === 'destroyed') {
141
118
  currentTableOfContents = $deleteHeadingFromTableOfContents(nodeKey, currentTableOfContents);
142
119
  } else if (mutation === 'updated') {
143
120
  const newHeading = lexical.$getNodeByKey(nodeKey);
144
-
145
121
  if (newHeading !== null) {
146
122
  let prevHeading = newHeading.getPreviousSibling();
147
-
148
123
  while (prevHeading !== null && !richText.$isHeadingNode(prevHeading)) {
149
124
  prevHeading = prevHeading.getPreviousSibling();
150
125
  }
151
-
152
126
  currentTableOfContents = $updateHeadingPosition(prevHeading, newHeading, currentTableOfContents);
153
127
  }
154
128
  }
155
129
  }
156
-
157
130
  setTableOfContents(currentTableOfContents);
158
131
  });
159
- }); // Listen to text node mutation updates
132
+ });
160
133
 
134
+ // Listen to text node mutation updates
161
135
  const removeTextNodeMutationListener = editor.registerMutationListener(lexical.TextNode, mutatedNodes => {
162
136
  editor.getEditorState().read(() => {
163
137
  for (const [nodeKey, mutation] of mutatedNodes) {
164
138
  if (mutation === 'updated') {
165
139
  const currNode = lexical.$getNodeByKey(nodeKey);
166
-
167
140
  if (currNode !== null) {
168
141
  const parentNode = currNode.getParentOrThrow();
169
-
170
142
  if (richText.$isHeadingNode(parentNode)) {
171
143
  currentTableOfContents = $updateHeadingInTableOfContents(parentNode, currentTableOfContents);
172
144
  setTableOfContents(currentTableOfContents);
@@ -26,65 +26,53 @@ function TablePlugin() {
26
26
  throw Error(`TablePlugin: TableNode, TableCellNode or TableRowNode not registered on editor`);
27
27
  }
28
28
  }
29
-
30
29
  return editor.registerCommand(table.INSERT_TABLE_COMMAND, ({
31
30
  columns,
32
31
  rows,
33
32
  includeHeaders
34
33
  }) => {
35
34
  const selection = lexical.$getSelection();
36
-
37
35
  if (!lexical.$isRangeSelection(selection)) {
38
36
  return true;
39
37
  }
40
-
41
38
  const focus = selection.focus;
42
39
  const focusNode = focus.getNode();
43
-
44
40
  if (focusNode !== null) {
45
41
  const tableNode = table.$createTableNodeWithDimensions(Number(rows), Number(columns), includeHeaders);
46
-
47
42
  if (lexical.$isRootOrShadowRoot(focusNode)) {
48
43
  const target = focusNode.getChildAtIndex(focus.offset);
49
-
50
44
  if (target !== null) {
51
45
  target.insertBefore(tableNode);
52
46
  } else {
53
47
  focusNode.append(tableNode);
54
48
  }
55
-
56
49
  tableNode.insertBefore(lexical.$createParagraphNode());
57
50
  } else {
58
51
  const topLevelNode = focusNode.getTopLevelElementOrThrow();
59
52
  topLevelNode.insertAfter(tableNode);
60
53
  }
61
-
62
54
  tableNode.insertAfter(lexical.$createParagraphNode());
63
55
  const firstCell = tableNode.getFirstChildOrThrow().getFirstChildOrThrow();
64
56
  firstCell.select();
65
57
  }
66
-
67
58
  return true;
68
59
  }, lexical.COMMAND_PRIORITY_EDITOR);
69
60
  }, [editor]);
70
61
  react.useEffect(() => {
71
62
  const tableSelections = new Map();
72
-
73
63
  const initializeTableNode = tableNode => {
74
64
  const nodeKey = tableNode.getKey();
75
65
  const tableElement = editor.getElementByKey(nodeKey);
76
-
77
66
  if (tableElement && !tableSelections.has(nodeKey)) {
78
67
  const tableSelection = table.applyTableHandlers(tableNode, tableElement, editor);
79
68
  tableSelections.set(nodeKey, tableSelection);
80
69
  }
81
- }; // Plugins might be loaded _after_ initial content is set, hence existing table nodes
82
- // won't be initialized from mutation[create] listener. Instead doing it here,
83
-
70
+ };
84
71
 
72
+ // Plugins might be loaded _after_ initial content is set, hence existing table nodes
73
+ // won't be initialized from mutation[create] listener. Instead doing it here,
85
74
  editor.getEditorState().read(() => {
86
75
  const tableNodes = lexical.$nodesOfType(table.TableNode);
87
-
88
76
  for (const tableNode of tableNodes) {
89
77
  if (table.$isTableNode(tableNode)) {
90
78
  initializeTableNode(tableNode);
@@ -96,14 +84,12 @@ function TablePlugin() {
96
84
  if (mutation === 'created') {
97
85
  editor.getEditorState().read(() => {
98
86
  const tableNode = lexical.$getNodeByKey(nodeKey);
99
-
100
87
  if (table.$isTableNode(tableNode)) {
101
88
  initializeTableNode(tableNode);
102
89
  }
103
90
  });
104
91
  } else if (mutation === 'destroyed') {
105
92
  const tableSelection = tableSelections.get(nodeKey);
106
-
107
93
  if (tableSelection !== undefined) {
108
94
  tableSelection.removeListeners();
109
95
  tableSelections.delete(nodeKey);
@@ -112,9 +98,9 @@ function TablePlugin() {
112
98
  }
113
99
  });
114
100
  return () => {
115
- unregisterMutationListener(); // Hook might be called multiple times so cleaning up tables listeners as well,
101
+ unregisterMutationListener();
102
+ // Hook might be called multiple times so cleaning up tables listeners as well,
116
103
  // as it'll be reinitialized during recurring call
117
-
118
104
  for (const [, tableSelection] of tableSelections) {
119
105
  tableSelection.removeListeners();
120
106
  }