@lexical/react 0.3.9 → 0.4.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 (36) hide show
  1. package/DEPRECATED_useLexical.dev.js +2 -2
  2. package/DEPRECATED_useLexical.prod.js +2 -2
  3. package/DEPRECATED_useLexicalCanShowPlaceholder.dev.js +2 -2
  4. package/DEPRECATED_useLexicalCanShowPlaceholder.prod.js +2 -2
  5. package/DEPRECATED_useLexicalEditor.dev.js +2 -2
  6. package/DEPRECATED_useLexicalEditor.prod.js +2 -2
  7. package/LexicalAutoEmbedPlugin.d.ts +1 -1
  8. package/LexicalAutoEmbedPlugin.dev.js +3 -337
  9. package/LexicalAutoEmbedPlugin.js.flow +72 -0
  10. package/LexicalAutoEmbedPlugin.prod.js +5 -15
  11. package/LexicalCheckListPlugin.dev.js +2 -2
  12. package/LexicalCheckListPlugin.prod.js +2 -2
  13. package/LexicalCollaborationPlugin.dev.js +2 -1
  14. package/LexicalCollaborationPlugin.js.flow +1 -16
  15. package/LexicalComposer.d.ts +1 -1
  16. package/LexicalComposer.dev.js +3 -3
  17. package/LexicalComposer.prod.js +1 -1
  18. package/LexicalContentEditable.dev.js +11 -11
  19. package/LexicalContentEditable.prod.js +2 -2
  20. package/LexicalHorizontalRuleNode.dev.js +47 -3
  21. package/LexicalHorizontalRuleNode.prod.js +5 -3
  22. package/LexicalMarkdownShortcutPlugin.d.ts +1 -0
  23. package/LexicalMarkdownShortcutPlugin.dev.js +2 -0
  24. package/LexicalMarkdownShortcutPlugin.js.flow +2 -0
  25. package/LexicalMarkdownShortcutPlugin.prod.js +2 -2
  26. package/LexicalOnChangePlugin.dev.js +1 -1
  27. package/LexicalOnChangePlugin.js.flow +1 -1
  28. package/LexicalPlainTextPlugin.dev.js +2 -2
  29. package/LexicalPlainTextPlugin.prod.js +2 -2
  30. package/LexicalRichTextPlugin.dev.js +2 -2
  31. package/LexicalRichTextPlugin.prod.js +2 -2
  32. package/LexicalTypeaheadMenuPlugin.d.ts +3 -3
  33. package/LexicalTypeaheadMenuPlugin.dev.js +12 -13
  34. package/LexicalTypeaheadMenuPlugin.js.flow +99 -0
  35. package/LexicalTypeaheadMenuPlugin.prod.js +14 -14
  36. package/package.json +19 -19
@@ -39,7 +39,7 @@ var useLayoutEffect = useLayoutEffectImpl;
39
39
  */
40
40
 
41
41
  function canShowPlaceholderFromCurrentEditorState(editor) {
42
- const currentCanShowPlaceholder = editor.getEditorState().read(text.$canShowPlaceholderCurry(editor.isComposing(), editor.isReadOnly()));
42
+ const currentCanShowPlaceholder = editor.getEditorState().read(text.$canShowPlaceholderCurry(editor.isComposing(), editor.isEditable()));
43
43
  return currentCanShowPlaceholder;
44
44
  }
45
45
 
@@ -54,7 +54,7 @@ function useCanShowPlaceholder(editor) {
54
54
  resetCanShowPlaceholder();
55
55
  return utils.mergeRegister(editor.registerUpdateListener(() => {
56
56
  resetCanShowPlaceholder();
57
- }), editor.registerReadOnlyListener(() => {
57
+ }), editor.registerEditableListener(() => {
58
58
  resetCanShowPlaceholder();
59
59
  }));
60
60
  }, [editor]);
@@ -4,5 +4,5 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- 'use strict';var c=require("lexical"),f=require("react"),g=require("@lexical/text"),h=require("@lexical/utils"),k="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?f.useLayoutEffect:f.useEffect;function l(a){return a.getEditorState().read(g.$canShowPlaceholderCurry(a.isComposing(),a.isReadOnly()))}
8
- function m(a){let [b,d]=f.useState(()=>l(a));k(()=>{function e(){let n=l(a);d(n)}e();return h.mergeRegister(a.registerUpdateListener(()=>{e()}),a.registerReadOnlyListener(()=>{e()}))},[a]);return b}function p(a){let b=m(a);return[f.useCallback(d=>{a.setRootElement(d)},[a]),b]}exports.useLexical=function(a){let b=f.useMemo(()=>c.createEditor(a),[]),[d,e]=p(b);return[b,d,e]}
7
+ 'use strict';var c=require("lexical"),f=require("react"),g=require("@lexical/text"),h=require("@lexical/utils"),k="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?f.useLayoutEffect:f.useEffect;function l(a){return a.getEditorState().read(g.$canShowPlaceholderCurry(a.isComposing(),a.isEditable()))}
8
+ function m(a){let [b,d]=f.useState(()=>l(a));k(()=>{function e(){let n=l(a);d(n)}e();return h.mergeRegister(a.registerUpdateListener(()=>{e()}),a.registerEditableListener(()=>{e()}))},[a]);return b}function p(a){let b=m(a);return[f.useCallback(d=>{a.setRootElement(d)},[a]),b]}exports.useLexical=function(a){let b=f.useMemo(()=>c.createEditor(a),[]),[d,e]=p(b);return[b,d,e]}
@@ -38,7 +38,7 @@ var useLayoutEffect = useLayoutEffectImpl;
38
38
  */
39
39
 
40
40
  function canShowPlaceholderFromCurrentEditorState(editor) {
41
- const currentCanShowPlaceholder = editor.getEditorState().read(text.$canShowPlaceholderCurry(editor.isComposing(), editor.isReadOnly()));
41
+ const currentCanShowPlaceholder = editor.getEditorState().read(text.$canShowPlaceholderCurry(editor.isComposing(), editor.isEditable()));
42
42
  return currentCanShowPlaceholder;
43
43
  }
44
44
 
@@ -53,7 +53,7 @@ function useCanShowPlaceholder(editor) {
53
53
  resetCanShowPlaceholder();
54
54
  return utils.mergeRegister(editor.registerUpdateListener(() => {
55
55
  resetCanShowPlaceholder();
56
- }), editor.registerReadOnlyListener(() => {
56
+ }), editor.registerEditableListener(() => {
57
57
  resetCanShowPlaceholder();
58
58
  }));
59
59
  }, [editor]);
@@ -4,5 +4,5 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- 'use strict';var b=require("@lexical/text"),d=require("@lexical/utils"),e=require("react"),f="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?e.useLayoutEffect:e.useEffect;function g(a){return a.getEditorState().read(b.$canShowPlaceholderCurry(a.isComposing(),a.isReadOnly()))}
8
- function h(a){let [k,l]=e.useState(()=>g(a));f(()=>{function c(){let m=g(a);l(m)}c();return d.mergeRegister(a.registerUpdateListener(()=>{c()}),a.registerReadOnlyListener(()=>{c()}))},[a]);return k}exports.useLexicalCanShowPlaceholder=function(a){return h(a)}
7
+ 'use strict';var b=require("@lexical/text"),d=require("@lexical/utils"),e=require("react"),f="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?e.useLayoutEffect:e.useEffect;function g(a){return a.getEditorState().read(b.$canShowPlaceholderCurry(a.isComposing(),a.isEditable()))}
8
+ function h(a){let [k,l]=e.useState(()=>g(a));f(()=>{function c(){let m=g(a);l(m)}c();return d.mergeRegister(a.registerUpdateListener(()=>{c()}),a.registerEditableListener(()=>{c()}))},[a]);return k}exports.useLexicalCanShowPlaceholder=function(a){return h(a)}
@@ -38,7 +38,7 @@ var useLayoutEffect = useLayoutEffectImpl;
38
38
  */
39
39
 
40
40
  function canShowPlaceholderFromCurrentEditorState(editor) {
41
- const currentCanShowPlaceholder = editor.getEditorState().read(text.$canShowPlaceholderCurry(editor.isComposing(), editor.isReadOnly()));
41
+ const currentCanShowPlaceholder = editor.getEditorState().read(text.$canShowPlaceholderCurry(editor.isComposing(), editor.isEditable()));
42
42
  return currentCanShowPlaceholder;
43
43
  }
44
44
 
@@ -53,7 +53,7 @@ function useCanShowPlaceholder(editor) {
53
53
  resetCanShowPlaceholder();
54
54
  return utils.mergeRegister(editor.registerUpdateListener(() => {
55
55
  resetCanShowPlaceholder();
56
- }), editor.registerReadOnlyListener(() => {
56
+ }), editor.registerEditableListener(() => {
57
57
  resetCanShowPlaceholder();
58
58
  }));
59
59
  }, [editor]);
@@ -4,5 +4,5 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- 'use strict';var b=require("react"),f=require("@lexical/text"),g=require("@lexical/utils"),h="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?b.useLayoutEffect:b.useEffect;function k(a){return a.getEditorState().read(f.$canShowPlaceholderCurry(a.isComposing(),a.isReadOnly()))}
8
- function l(a){let [c,d]=b.useState(()=>k(a));h(()=>{function e(){let m=k(a);d(m)}e();return g.mergeRegister(a.registerUpdateListener(()=>{e()}),a.registerReadOnlyListener(()=>{e()}))},[a]);return c}exports.useLexicalEditor=function(a){let c=l(a);return[b.useCallback(d=>{a.setRootElement(d)},[a]),c]}
7
+ 'use strict';var b=require("react"),f=require("@lexical/text"),g=require("@lexical/utils"),h="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?b.useLayoutEffect:b.useEffect;function k(a){return a.getEditorState().read(f.$canShowPlaceholderCurry(a.isComposing(),a.isEditable()))}
8
+ function l(a){let [c,d]=b.useState(()=>k(a));h(()=>{function e(){let m=k(a);d(m)}e();return g.mergeRegister(a.registerUpdateListener(()=>{e()}),a.registerEditableListener(()=>{e()}))},[a]);return c}exports.useLexicalEditor=function(a){let c=l(a);return[b.useCallback(d=>{a.setRootElement(d)},[a]),c]}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  */
8
8
  import type { LexicalNode } from 'lexical';
9
- import { TypeaheadOption } from '@lexical/react/src/LexicalTypeaheadMenuPlugin';
9
+ import { TypeaheadOption } from '@lexical/react/LexicalTypeaheadMenuPlugin';
10
10
  import { LexicalCommand, LexicalEditor } from 'lexical';
11
11
  import * as React from 'react';
12
12
  export declare type EmbedMatchResult = {
@@ -8,346 +8,12 @@
8
8
 
9
9
  var link = require('@lexical/link');
10
10
  var LexicalComposerContext = require('@lexical/react/LexicalComposerContext');
11
+ var LexicalTypeaheadMenuPlugin = require('@lexical/react/LexicalTypeaheadMenuPlugin');
11
12
  var utils = require('@lexical/utils');
12
13
  var lexical = require('lexical');
13
14
  var React = require('react');
14
15
  var ReactDOM = require('react-dom');
15
16
 
16
- /**
17
- * Copyright (c) Meta Platforms, Inc. and affiliates.
18
- *
19
- * This source code is licensed under the MIT license found in the
20
- * LICENSE file in the root directory of this source tree.
21
- *
22
- */
23
- const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
24
-
25
- /**
26
- * Copyright (c) Meta Platforms, Inc. and affiliates.
27
- *
28
- * This source code is licensed under the MIT license found in the
29
- * LICENSE file in the root directory of this source tree.
30
- *
31
- */
32
- const useLayoutEffectImpl = CAN_USE_DOM ? React.useLayoutEffect : React.useEffect;
33
- var useLayoutEffect = useLayoutEffectImpl;
34
-
35
- /**
36
- * Copyright (c) Meta Platforms, Inc. and affiliates.
37
- *
38
- * This source code is licensed under the MIT license found in the
39
- * LICENSE file in the root directory of this source tree.
40
- *
41
- */
42
- class TypeaheadOption {
43
- constructor(key) {
44
- this.key = key;
45
- this.ref = {
46
- current: null
47
- };
48
- this.setRefElement = this.setRefElement.bind(this);
49
- }
50
-
51
- setRefElement(element) {
52
- this.ref = {
53
- current: element
54
- };
55
- }
56
-
57
- }
58
-
59
- const scrollIntoViewIfNeeded = target => {
60
- const container = document.getElementById('typeahead-menu');
61
-
62
- if (container) {
63
- const containerRect = container.getBoundingClientRect();
64
- const targetRect = target.getBoundingClientRect();
65
-
66
- if (targetRect.bottom > containerRect.bottom) {
67
- target.scrollIntoView(false);
68
- } else if (targetRect.top < containerRect.top) {
69
- target.scrollIntoView();
70
- }
71
- }
72
- };
73
- /**
74
- * Walk backwards along user input and forward through entity title to try
75
- * and replace more of the user's text with entity.
76
- */
77
-
78
-
79
- function getFullMatchOffset(documentText, entryText, offset) {
80
- let triggerOffset = offset;
81
-
82
- for (let i = triggerOffset; i <= entryText.length; i++) {
83
- if (documentText.substr(-i) === entryText.substr(0, i)) {
84
- triggerOffset = i;
85
- }
86
- }
87
-
88
- return triggerOffset;
89
- }
90
- /**
91
- * Split Lexical TextNode and return a new TextNode only containing matched text.
92
- * Common use cases include: removing the node, replacing with a new node.
93
- */
94
-
95
-
96
- function splitNodeContainingQuery(editor, match) {
97
- const selection = lexical.$getSelection();
98
-
99
- if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed()) {
100
- return null;
101
- }
102
-
103
- const anchor = selection.anchor;
104
-
105
- if (anchor.type !== 'text') {
106
- return null;
107
- }
108
-
109
- const anchorNode = anchor.getNode();
110
-
111
- if (!anchorNode.isSimpleText()) {
112
- return null;
113
- }
114
-
115
- const selectionOffset = anchor.offset;
116
- const textContent = anchorNode.getTextContent().slice(0, selectionOffset);
117
- const characterOffset = match.replaceableString.length;
118
- const queryOffset = getFullMatchOffset(textContent, match.matchingString, characterOffset);
119
- const startOffset = selectionOffset - queryOffset;
120
-
121
- if (startOffset < 0) {
122
- return null;
123
- }
124
-
125
- let newNode;
126
-
127
- if (startOffset === 0) {
128
- [newNode] = anchorNode.splitText(selectionOffset);
129
- } else {
130
- [, newNode] = anchorNode.splitText(startOffset, selectionOffset);
131
- }
132
-
133
- return newNode;
134
- }
135
-
136
- function startTransition(callback) {
137
- if (React.startTransition) {
138
- React.startTransition(callback);
139
- } else {
140
- callback();
141
- }
142
- }
143
-
144
- function LexicalPopoverMenu({
145
- close,
146
- editor,
147
- anchorElement,
148
- resolution,
149
- options,
150
- menuRenderFn,
151
- onSelectOption
152
- }) {
153
- const [selectedIndex, setHighlightedIndex] = React.useState(null);
154
- React.useEffect(() => {
155
- setHighlightedIndex(0);
156
- }, [resolution.match.matchingString]);
157
- const selectOptionAndCleanUp = React.useCallback(async selectedEntry => {
158
- editor.update(() => {
159
- const textNodeContainingQuery = splitNodeContainingQuery(editor, resolution.match);
160
- onSelectOption(selectedEntry, textNodeContainingQuery, close, resolution.match.matchingString);
161
- });
162
- }, [close, editor, resolution.match, onSelectOption]);
163
- const updateSelectedIndex = React.useCallback(index => {
164
- const rootElem = editor.getRootElement();
165
-
166
- if (rootElem !== null) {
167
- rootElem.setAttribute('aria-activedescendant', 'typeahead-item-' + index);
168
- setHighlightedIndex(index);
169
- }
170
- }, [editor]);
171
- React.useEffect(() => {
172
- return () => {
173
- const rootElem = editor.getRootElement();
174
-
175
- if (rootElem !== null) {
176
- rootElem.removeAttribute('aria-activedescendant');
177
- }
178
- };
179
- }, [editor]);
180
- useLayoutEffect(() => {
181
- if (options === null) {
182
- setHighlightedIndex(null);
183
- } else if (selectedIndex === null) {
184
- updateSelectedIndex(0);
185
- }
186
- }, [options, selectedIndex, updateSelectedIndex]);
187
- React.useEffect(() => {
188
- return utils.mergeRegister(editor.registerCommand(lexical.KEY_ARROW_DOWN_COMMAND, payload => {
189
- const event = payload;
190
-
191
- if (options !== null && options.length && selectedIndex !== null) {
192
- const newSelectedIndex = selectedIndex !== options.length - 1 ? selectedIndex + 1 : 0;
193
- updateSelectedIndex(newSelectedIndex);
194
- const option = options[newSelectedIndex];
195
-
196
- if (option.ref != null && option.ref.current) {
197
- scrollIntoViewIfNeeded(option.ref.current);
198
- }
199
-
200
- event.preventDefault();
201
- event.stopImmediatePropagation();
202
- }
203
-
204
- return true;
205
- }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ARROW_UP_COMMAND, payload => {
206
- const event = payload;
207
-
208
- if (options !== null && options.length && selectedIndex !== null) {
209
- const newSelectedIndex = selectedIndex !== 0 ? selectedIndex - 1 : options.length - 1;
210
- updateSelectedIndex(newSelectedIndex);
211
- const option = options[newSelectedIndex];
212
-
213
- if (option.ref != null && option.ref.current) {
214
- scrollIntoViewIfNeeded(option.ref.current);
215
- }
216
-
217
- event.preventDefault();
218
- event.stopImmediatePropagation();
219
- }
220
-
221
- return true;
222
- }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ESCAPE_COMMAND, payload => {
223
- const event = payload;
224
- event.preventDefault();
225
- event.stopImmediatePropagation();
226
- close();
227
- return true;
228
- }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_TAB_COMMAND, payload => {
229
- const event = payload;
230
-
231
- if (options === null || selectedIndex === null || options[selectedIndex] == null) {
232
- return false;
233
- }
234
-
235
- event.preventDefault();
236
- event.stopImmediatePropagation();
237
- selectOptionAndCleanUp(options[selectedIndex]);
238
- return true;
239
- }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ENTER_COMMAND, event => {
240
- if (options === null || selectedIndex === null || options[selectedIndex] == null) {
241
- return false;
242
- }
243
-
244
- if (event !== null) {
245
- event.preventDefault();
246
- event.stopImmediatePropagation();
247
- }
248
-
249
- selectOptionAndCleanUp(options[selectedIndex]);
250
- return true;
251
- }, lexical.COMMAND_PRIORITY_LOW));
252
- }, [selectOptionAndCleanUp, close, editor, options, selectedIndex, updateSelectedIndex]);
253
- const listItemProps = React.useMemo(() => ({
254
- selectOptionAndCleanUp,
255
- selectedIndex,
256
- setHighlightedIndex
257
- }), [selectOptionAndCleanUp, selectedIndex]);
258
- return menuRenderFn(anchorElement, listItemProps, resolution.match.matchingString);
259
- }
260
-
261
- function useAnchorElementRef(resolution, options) {
262
- const [editor] = LexicalComposerContext.useLexicalComposerContext();
263
- const anchorElementRef = React.useRef(document.createElement('div'));
264
- React.useEffect(() => {
265
- const rootElement = editor.getRootElement();
266
-
267
- function positionMenu() {
268
- const containerDiv = anchorElementRef.current;
269
- containerDiv.setAttribute('aria-label', 'Typeahead menu');
270
- containerDiv.setAttribute('id', 'typeahead-menu');
271
- containerDiv.setAttribute('role', 'listbox');
272
-
273
- if (rootElement !== null && resolution !== null) {
274
- const {
275
- left,
276
- top,
277
- height,
278
- width
279
- } = resolution.getRect();
280
- containerDiv.style.top = `${top + height + window.pageYOffset}px`;
281
- containerDiv.style.left = `${left + width + window.pageXOffset}px`;
282
- containerDiv.style.display = 'block';
283
- containerDiv.style.position = 'absolute';
284
-
285
- if (!containerDiv.isConnected) {
286
- document.body.append(containerDiv);
287
- }
288
-
289
- anchorElementRef.current = containerDiv;
290
- rootElement.setAttribute('aria-controls', 'typeahead-menu');
291
- }
292
- }
293
-
294
- if (resolution !== null) {
295
- positionMenu();
296
- window.addEventListener('resize', positionMenu);
297
- return () => {
298
- window.removeEventListener('resize', positionMenu);
299
-
300
- if (rootElement !== null) {
301
- rootElement.removeAttribute('aria-controls');
302
- }
303
- };
304
- }
305
- }, [editor, resolution, options]);
306
- return anchorElementRef;
307
- }
308
- function LexicalNodeMenuPlugin({
309
- options,
310
- nodeKey,
311
- onClose,
312
- onSelectOption,
313
- menuRenderFn
314
- }) {
315
- const [editor] = LexicalComposerContext.useLexicalComposerContext();
316
- const [resolution, setResolution] = React.useState(null);
317
- const anchorElementRef = useAnchorElementRef(resolution, options);
318
- React.useEffect(() => {
319
- if (nodeKey && resolution == null) {
320
- editor.update(() => {
321
- const node = lexical.$getNodeByKey(nodeKey);
322
- const domElement = editor.getElementByKey(nodeKey);
323
-
324
- if (node != null && domElement != null) {
325
- const text = node.getTextContent();
326
- startTransition(() => setResolution({
327
- getRect: () => domElement.getBoundingClientRect(),
328
- match: {
329
- leadOffset: text.length,
330
- matchingString: text,
331
- replaceableString: text
332
- }
333
- }));
334
- }
335
- });
336
- } else if (nodeKey == null && resolution != null) {
337
- setResolution(null);
338
- }
339
- }, [editor, nodeKey, resolution]);
340
- return resolution === null || editor === null ? null : /*#__PURE__*/React.createElement(LexicalPopoverMenu, {
341
- close: onClose,
342
- resolution: resolution,
343
- editor: editor,
344
- anchorElement: anchorElementRef.current,
345
- options: options,
346
- menuRenderFn: menuRenderFn,
347
- onSelectOption: onSelectOption
348
- });
349
- }
350
-
351
17
  /**
352
18
  * Copyright (c) Meta Platforms, Inc. and affiliates.
353
19
  *
@@ -357,7 +23,7 @@ function LexicalNodeMenuPlugin({
357
23
  */
358
24
  const URL_MATCHER = /((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
359
25
  const INSERT_EMBED_COMMAND = lexical.createCommand();
360
- class AutoEmbedOption extends TypeaheadOption {
26
+ class AutoEmbedOption extends LexicalTypeaheadMenuPlugin.TypeaheadOption {
361
27
  constructor(title, options) {
362
28
  super(title);
363
29
  this.title = title;
@@ -461,7 +127,7 @@ function LexicalAutoEmbedPlugin({
461
127
  closeMenu();
462
128
  });
463
129
  }, [editor]);
464
- return nodeKey != null ? /*#__PURE__*/React.createElement(LexicalNodeMenuPlugin, {
130
+ return nodeKey != null ? /*#__PURE__*/React.createElement(LexicalTypeaheadMenuPlugin.LexicalNodeMenuPlugin, {
465
131
  nodeKey: nodeKey,
466
132
  onClose: reset,
467
133
  onSelectOption: onSelectOption,
@@ -0,0 +1,72 @@
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, MutationListener} from 'lexical';
11
+
12
+ import {TypeaheadOption} from '@lexical/react/LexicalTypeaheadMenuPlugin';
13
+ import type {LexicalCommand, LexicalEditor, NodeKey, TextNode} from 'lexical';
14
+ import * as React from 'react';
15
+ import {createCommand} from 'lexical';
16
+
17
+ export type EmbedMatchResult = {
18
+ url: string,
19
+ id: string,
20
+ };
21
+
22
+ export interface EmbedConfig {
23
+ // Used to identify this config e.g. youtube, tweet, google-maps.
24
+ type: string;
25
+ // Determine if a given URL is a match and return url data.
26
+ parseUrl: (text: string) => EmbedMatchResult | null;
27
+ // Create the Lexical embed node from the url data.
28
+ insertNode: (editor: LexicalEditor, result: EmbedMatchResult) => void;
29
+ }
30
+
31
+ export const URL_MATCHER: RegExp =
32
+ /((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
33
+
34
+ export const INSERT_EMBED_COMMAND: LexicalCommand<EmbedConfig['type']> =
35
+ createCommand();
36
+
37
+ export type EmbedMenuProps = {
38
+ selectedItemIndex: number | null,
39
+ onOptionClick: (option: AutoEmbedOption, index: number) => void,
40
+ onOptionMouseEnter: (index: number) => void,
41
+ options: Array<AutoEmbedOption>,
42
+ };
43
+
44
+ export type EmbedMenuComponent = React.ComponentType<EmbedMenuProps>;
45
+
46
+ type LexicalAutoEmbedPluginProps<TEmbedConfig> = {
47
+ embedConfigs: Array<TEmbedConfig>,
48
+ onOpenEmbedModalForConfig: (embedConfig: TEmbedConfig) => void,
49
+ menuComponent: EmbedMenuComponent,
50
+ getMenuOptions: (
51
+ activeEmbedConfig: TEmbedConfig,
52
+ embedFn: () => void,
53
+ dismissFn: () => void,
54
+ ) => Array<AutoEmbedOption>,
55
+ };
56
+
57
+ declare export class AutoEmbedOption extends TypeaheadOption {
58
+ title: string;
59
+ icon: React.MixedElement;
60
+ onSelect: (targetNode: LexicalNode | null) => void;
61
+ constructor(
62
+ title: string,
63
+ options: {
64
+ icon: React.MixedElement,
65
+ onSelect: (targetNode: LexicalNode | null) => void,
66
+ },
67
+ ): void;
68
+ }
69
+
70
+ declare export function LexicalAutoEmbedPlugin<TEmbedConfig>(
71
+ LexicalAutoEmbedPluginProps<TEmbedConfig>,
72
+ ): React.MixedElement | null;
@@ -4,18 +4,8 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- 'use strict';var f=require("@lexical/link"),n=require("@lexical/react/LexicalComposerContext"),z=require("@lexical/utils"),A=require("lexical"),C=require("react"),D=require("react-dom"),E="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?C.useLayoutEffect:C.useEffect;class F{constructor(b){this.key=b;this.ref={current:null};this.setRefElement=this.setRefElement.bind(this)}setRefElement(b){this.ref={current:b}}}
8
- let G=b=>{var a=document.getElementById("typeahead-menu");if(a){a=a.getBoundingClientRect();const k=b.getBoundingClientRect();k.bottom>a.bottom?b.scrollIntoView(!1):k.top<a.top&&b.scrollIntoView()}};
9
- function H(b,a){b=A.$getSelection();if(!A.$isRangeSelection(b)||!b.isCollapsed())return null;var k=b.anchor;if("text"!==k.type)return null;b=k.getNode();if(!b.isSimpleText())return null;k=k.offset;let q=b.getTextContent().slice(0,k);var c=a.matchingString;a=a.replaceableString.length;for(let e=a;e<=c.length;e++)q.substr(-e)===c.substr(0,e)&&(a=e);a=k-a;if(0>a)return null;let g;0===a?[g]=b.splitText(k):[,g]=b.splitText(a,k);return g}function I(b){C.startTransition?C.startTransition(b):b()}
10
- function J({close:b,editor:a,anchorElement:k,resolution:q,options:c,menuRenderFn:g,onSelectOption:e}){let [d,v]=C.useState(null);C.useEffect(()=>{v(0)},[q.match.matchingString]);let p=C.useCallback(async h=>{a.update(()=>{const l=H(a,q.match);e(h,l,b,q.match.matchingString)})},[b,a,q.match,e]),t=C.useCallback(h=>{const l=a.getRootElement();null!==l&&(l.setAttribute("aria-activedescendant","typeahead-item-"+h),v(h))},[a]);C.useEffect(()=>()=>{let h=a.getRootElement();null!==h&&h.removeAttribute("aria-activedescendant")},
11
- [a]);E(()=>{null===c?v(null):null===d&&t(0)},[c,d,t]);C.useEffect(()=>z.mergeRegister(a.registerCommand(A.KEY_ARROW_DOWN_COMMAND,h=>{if(null!==c&&c.length&&null!==d){var l=d!==c.length-1?d+1:0;t(l);l=c[l];null!=l.ref&&l.ref.current&&G(l.ref.current);h.preventDefault();h.stopImmediatePropagation()}return!0},A.COMMAND_PRIORITY_LOW),a.registerCommand(A.KEY_ARROW_UP_COMMAND,h=>{if(null!==c&&c.length&&null!==d){var l=0!==d?d-1:c.length-1;t(l);l=c[l];null!=l.ref&&l.ref.current&&G(l.ref.current);h.preventDefault();
12
- h.stopImmediatePropagation()}return!0},A.COMMAND_PRIORITY_LOW),a.registerCommand(A.KEY_ESCAPE_COMMAND,h=>{h.preventDefault();h.stopImmediatePropagation();b();return!0},A.COMMAND_PRIORITY_LOW),a.registerCommand(A.KEY_TAB_COMMAND,h=>{if(null===c||null===d||null==c[d])return!1;h.preventDefault();h.stopImmediatePropagation();p(c[d]);return!0},A.COMMAND_PRIORITY_LOW),a.registerCommand(A.KEY_ENTER_COMMAND,h=>{if(null===c||null===d||null==c[d])return!1;null!==h&&(h.preventDefault(),h.stopImmediatePropagation());
13
- p(c[d]);return!0},A.COMMAND_PRIORITY_LOW)),[p,b,a,c,d,t]);let w=C.useMemo(()=>({selectOptionAndCleanUp:p,selectedIndex:d,setHighlightedIndex:v}),[p,d]);return g(k,w,q.match.matchingString)}
14
- function K(b,a){let [k]=n.useLexicalComposerContext(),q=C.useRef(document.createElement("div"));C.useEffect(()=>{function c(){let e=q.current;e.setAttribute("aria-label","Typeahead menu");e.setAttribute("id","typeahead-menu");e.setAttribute("role","listbox");if(null!==g&&null!==b){let {left:d,top:v,height:p,width:t}=b.getRect();e.style.top=`${v+p+window.pageYOffset}px`;e.style.left=`${d+t+window.pageXOffset}px`;e.style.display="block";e.style.position="absolute";e.isConnected||document.body.append(e);
15
- q.current=e;g.setAttribute("aria-controls","typeahead-menu")}}let g=k.getRootElement();if(null!==b)return c(),window.addEventListener("resize",c),()=>{window.removeEventListener("resize",c);null!==g&&g.removeAttribute("aria-controls")}},[k,b,a]);return q}
16
- function L({options:b,nodeKey:a,onClose:k,onSelectOption:q,menuRenderFn:c}){let [g]=n.useLexicalComposerContext(),[e,d]=C.useState(null),v=K(e,b);C.useEffect(()=>{a&&null==e?g.update(()=>{let p=A.$getNodeByKey(a),t=g.getElementByKey(a);if(null!=p&&null!=t){let w=p.getTextContent();I(()=>d({getRect:()=>t.getBoundingClientRect(),match:{leadOffset:w.length,matchingString:w,replaceableString:w}}))}}):null==a&&null!=e&&d(null)},[g,a,e]);return null===e||null===g?null:C.createElement(J,{close:k,resolution:e,
17
- editor:g,anchorElement:v.current,options:b,menuRenderFn:c,onSelectOption:q})}let M=A.createCommand();class N extends F{constructor(b,a){super(b);this.title=b;this.icon=a.icon;this.onSelect=a.onSelect.bind(this)}}exports.AutoEmbedOption=N;exports.INSERT_EMBED_COMMAND=M;
18
- exports.LexicalAutoEmbedPlugin=function({embedConfigs:b,onOpenEmbedModalForConfig:a,getMenuOptions:k,menuComponent:q}){let [c]=n.useLexicalComposerContext(),[g,e]=C.useState(null),[d,v]=C.useState(null),p=C.useCallback(()=>{e(null);v(null)},[]),t=C.useCallback(r=>{c.getEditorState().read(()=>{const m=A.$getNodeByKey(r);if(f.$isLinkNode(m)){const u=b.find(x=>x.parseUrl(m.__url));null!=u&&(v(u),e(m.getKey()))}})},[c,b]);C.useEffect(()=>{let r=(m,{updateTags:u,dirtyLeaves:x})=>{for(const [y,B]of m)"created"===
19
- B&&u.has("paste")&&1===x.size?t(y):y===g&&p()};return z.mergeRegister(...[f.LinkNode,f.AutoLinkNode].map(m=>c.registerMutationListener(m,(...u)=>r(...u))))},[t,c,b,g,p]);C.useEffect(()=>c.registerCommand(M,r=>{let m=b.find(({type:u})=>u===r);return m?(a(m),!0):!1},A.COMMAND_PRIORITY_EDITOR),[c,b,a]);let w=C.useCallback(()=>{if(null!=d&&null!=g){const r=c.getEditorState().read(()=>{const m=A.$getNodeByKey(g);return f.$isLinkNode(m)?m:null});if(f.$isLinkNode(r)){const m=d.parseUrl(r.__url);null!=m&&
20
- (c.update(()=>{d.insertNode(c,m)}),r.isAttached()&&c.update(()=>{r.remove()}))}}},[d,c,g]),h=C.useMemo(()=>null!=d&&null!=g?k(d,w,p):[],[d,w,k,g,p]),l=C.useCallback((r,m,u)=>{c.update(()=>{r.onSelect(m);u()})},[c]);return null!=g?C.createElement(L,{nodeKey:g,onClose:p,onSelectOption:l,options:h,menuRenderFn:(r,{selectedIndex:m,selectOptionAndCleanUp:u,setHighlightedIndex:x})=>r&&null!=g?D.createPortal(C.createElement(q,{options:h,selectedItemIndex:m,onOptionClick:(y,B)=>{x(B);u(y)},onOptionMouseEnter:y=>
21
- {x(y)}}),r):null}):null};exports.URL_MATCHER=/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
7
+ 'use strict';var e=require("@lexical/link"),m=require("@lexical/react/LexicalComposerContext"),q=require("@lexical/react/LexicalTypeaheadMenuPlugin"),t=require("@lexical/utils"),u=require("lexical"),B=require("react"),C=require("react-dom");let D=u.createCommand();class E extends q.TypeaheadOption{constructor(g,n){super(g);this.title=g;this.icon=n.icon;this.onSelect=n.onSelect.bind(this)}}exports.AutoEmbedOption=E;exports.INSERT_EMBED_COMMAND=D;
8
+ exports.LexicalAutoEmbedPlugin=function({embedConfigs:g,onOpenEmbedModalForConfig:n,getMenuOptions:v,menuComponent:F}){let [c]=m.useLexicalComposerContext(),[f,w]=B.useState(null),[h,x]=B.useState(null),p=B.useCallback(()=>{w(null);x(null)},[]),y=B.useCallback(b=>{c.getEditorState().read(()=>{const a=u.$getNodeByKey(b);if(e.$isLinkNode(a)){const d=g.find(k=>k.parseUrl(a.__url));null!=d&&(x(d),w(a.getKey()))}})},[c,g]);B.useEffect(()=>{let b=(a,{updateTags:d,dirtyLeaves:k})=>{for(const [l,r]of a)"created"===
9
+ r&&d.has("paste")&&1===k.size?y(l):l===f&&p()};return t.mergeRegister(...[e.LinkNode,e.AutoLinkNode].map(a=>c.registerMutationListener(a,(...d)=>b(...d))))},[y,c,g,f,p]);B.useEffect(()=>c.registerCommand(D,b=>{let a=g.find(({type:d})=>d===b);return a?(n(a),!0):!1},u.COMMAND_PRIORITY_EDITOR),[c,g,n]);let z=B.useCallback(()=>{if(null!=h&&null!=f){const b=c.getEditorState().read(()=>{const a=u.$getNodeByKey(f);return e.$isLinkNode(a)?a:null});if(e.$isLinkNode(b)){const a=h.parseUrl(b.__url);null!=a&&
10
+ (c.update(()=>{h.insertNode(c,a)}),b.isAttached()&&c.update(()=>{b.remove()}))}}},[h,c,f]),A=B.useMemo(()=>null!=h&&null!=f?v(h,z,p):[],[h,z,v,f,p]),G=B.useCallback((b,a,d)=>{c.update(()=>{b.onSelect(a);d()})},[c]);return null!=f?B.createElement(q.LexicalNodeMenuPlugin,{nodeKey:f,onClose:p,onSelectOption:G,options:A,menuRenderFn:(b,{selectedIndex:a,selectOptionAndCleanUp:d,setHighlightedIndex:k})=>b&&null!=f?C.createPortal(B.createElement(F,{options:A,selectedItemIndex:a,onOptionClick:(l,r)=>{k(r);
11
+ d(l)},onOptionMouseEnter:l=>{k(l)}}),b):null}):null};exports.URL_MATCHER=/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
@@ -46,7 +46,7 @@ function CheckListPlugin() {
46
46
  }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_SPACE_COMMAND, event => {
47
47
  const activeItem = getActiveCheckListItem();
48
48
 
49
- if (activeItem != null && !editor.isReadOnly()) {
49
+ if (activeItem != null && editor.isEditable()) {
50
50
  editor.update(() => {
51
51
  const listItemNode = lexical.$getNearestNodeFromDOMNode(activeItem);
52
52
 
@@ -140,7 +140,7 @@ function handleClick(event) {
140
140
  const domNode = event.target;
141
141
  const editor = findEditor(domNode);
142
142
 
143
- if (editor != null && !editor.isReadOnly()) {
143
+ if (editor != null && editor.isEditable()) {
144
144
  editor.update(() => {
145
145
  if (event.target) {
146
146
  const node = lexical.$getNearestNodeFromDOMNode(domNode);
@@ -6,9 +6,9 @@
6
6
  */
7
7
  'use strict';var e=require("@lexical/list"),g=require("@lexical/react/LexicalComposerContext"),k=require("@lexical/utils"),l=require("lexical"),m=require("react");let n=0;function p(){0===n++&&(document.addEventListener("click",q),document.addEventListener("pointerdown",r));return()=>{0===--n&&(document.removeEventListener("click",q),document.removeEventListener("pointerdown",r))}}
8
8
  function t(a,d){let b=a.target;if(b instanceof HTMLElement){var c=b.firstChild;null!=c&&c instanceof HTMLElement&&("UL"===c.tagName||"OL"===c.tagName)||(c=b.parentNode)&&"check"===c.__lexicalListType&&(a=a.pageX,c=b.getBoundingClientRect(),("rtl"===b.dir?a<c.right&&a>c.right-20:a>c.left&&a<c.left+20)&&d())}}
9
- function q(a){t(a,()=>{let d=a.target,b=u(d);null==b||b.isReadOnly()||b.update(()=>{if(a.target){let c=l.$getNearestNodeFromDOMNode(d);e.$isListItemNode(c)&&(d.focus(),c.toggleChecked())}})})}function r(a){t(a,()=>{a.preventDefault()})}function u(a){for(;a;){if(a.__lexicalEditor)return a.__lexicalEditor;a=a.parentNode}return null}function v(){let a=document.activeElement;return null!=a&&"LI"===a.tagName&&null!=a.parentNode&&"check"===a.parentNode.__lexicalListType?a:null}
9
+ function q(a){t(a,()=>{let d=a.target,b=u(d);null!=b&&b.isEditable()&&b.update(()=>{if(a.target){let c=l.$getNearestNodeFromDOMNode(d);e.$isListItemNode(c)&&(d.focus(),c.toggleChecked())}})})}function r(a){t(a,()=>{a.preventDefault()})}function u(a){for(;a;){if(a.__lexicalEditor)return a.__lexicalEditor;a=a.parentNode}return null}function v(){let a=document.activeElement;return null!=a&&"LI"===a.tagName&&null!=a.parentNode&&"check"===a.parentNode.__lexicalListType?a:null}
10
10
  function w(a,d){let b=d?a.getPreviousSibling():a.getNextSibling();for(;null==b&&e.$isListItemNode(a);)a=a.getParentOrThrow().getParent(),null!=a&&(b=d?a.getPreviousSibling():a.getNextSibling());for(;e.$isListItemNode(b);){a=d?b.getLastChild():b.getFirstChild();if(!e.$isListNode(a))return b;b=d?a.getLastChild():a.getFirstChild()}return null}
11
11
  function x(a,d,b){let c=v();null!=c&&d.update(()=>{var f=l.$getNearestNodeFromDOMNode(c);if(e.$isListItemNode(f)&&(f=w(f,b),null!=f)){f.selectStart();let h=d.getElementByKey(f.__key);null!=h&&(a.preventDefault(),setTimeout(()=>{h.focus()},0))}});return!1}
12
12
  exports.CheckListPlugin=function(){let [a]=g.useLexicalComposerContext();m.useEffect(()=>k.mergeRegister(a.registerCommand(e.INSERT_CHECK_LIST_COMMAND,()=>{e.insertList(a,"check");return!0},l.COMMAND_PRIORITY_LOW),a.registerCommand(l.KEY_ARROW_DOWN_COMMAND,d=>x(d,a,!1),l.COMMAND_PRIORITY_LOW),a.registerCommand(l.KEY_ARROW_UP_COMMAND,d=>x(d,a,!0),l.COMMAND_PRIORITY_LOW),a.registerCommand(l.KEY_ESCAPE_COMMAND,()=>{if(null!=v()){let d=a.getRootElement();null!=d&&d.focus();return!0}return!1},l.COMMAND_PRIORITY_LOW),
13
- a.registerCommand(l.KEY_SPACE_COMMAND,d=>{let b=v();return null==b||a.isReadOnly()?!1:(a.update(()=>{let c=l.$getNearestNodeFromDOMNode(b);e.$isListItemNode(c)&&(d.preventDefault(),c.toggleChecked())}),!0)},l.COMMAND_PRIORITY_LOW),a.registerCommand(l.KEY_ARROW_LEFT_COMMAND,d=>a.getEditorState().read(()=>{var b=l.$getSelection();if(l.$isRangeSelection(b)&&b.isCollapsed()){var {anchor:c}=b;if((b="element"===c.type)||0===c.offset){c=c.getNode();let f=k.$findMatchingParent(c,h=>l.$isElementNode(h)&&!h.isInline());
13
+ a.registerCommand(l.KEY_SPACE_COMMAND,d=>{let b=v();return null!=b&&a.isEditable()?(a.update(()=>{let c=l.$getNearestNodeFromDOMNode(b);e.$isListItemNode(c)&&(d.preventDefault(),c.toggleChecked())}),!0):!1},l.COMMAND_PRIORITY_LOW),a.registerCommand(l.KEY_ARROW_LEFT_COMMAND,d=>a.getEditorState().read(()=>{var b=l.$getSelection();if(l.$isRangeSelection(b)&&b.isCollapsed()){var {anchor:c}=b;if((b="element"===c.type)||0===c.offset){c=c.getNode();let f=k.$findMatchingParent(c,h=>l.$isElementNode(h)&&!h.isInline());
14
14
  if(e.$isListItemNode(f)&&(b||f.getFirstDescendant()===c)&&(b=a.getElementByKey(f.__key),null!=b&&document.activeElement!==b))return b.focus(),d.preventDefault(),!0}}return!1}),l.COMMAND_PRIORITY_LOW),p()));return null}
@@ -78,7 +78,8 @@ function useYjsCollaboration(editor, id, provider, docMap, name, color, shouldBo
78
78
  provider.on('reload', onProviderDocReload);
79
79
  provider.on('status', onStatus);
80
80
  provider.on('sync', onSync);
81
- awareness.on('update', onAwarenessUpdate);
81
+ awareness.on('update', onAwarenessUpdate); // This updates the local editor state when we recieve updates from other clients
82
+
82
83
  root.getSharedType().observeDeep(onYjsTreeChanges);
83
84
  const removeListener = editor.registerUpdateListener(({
84
85
  prevEditorState,
@@ -7,24 +7,9 @@
7
7
  * @flow strict
8
8
  */
9
9
 
10
+ import type {ProviderAwareness} from '@lexical/yjs';
10
11
  import type {Doc, RelativePosition} from 'yjs';
11
12
 
12
- export type UserState = {
13
- anchorPos: null | RelativePosition,
14
- focusPos: null | RelativePosition,
15
- name: string,
16
- color: string,
17
- focusing: boolean,
18
- };
19
-
20
- export type ProviderAwareness = {
21
- getLocalState: () => UserState | null,
22
- setLocalState: (UserState) => void,
23
- getStates: () => Map<number, UserState>,
24
- on: (type: 'update', cb: () => void) => void,
25
- off: (type: 'update', cb: () => void) => void,
26
- };
27
-
28
13
  export interface Provider {
29
14
  connect(): void | Promise<void>;
30
15
  disconnect(): void;
@@ -15,7 +15,7 @@ declare type Props = {
15
15
  namespace: string;
16
16
  nodes?: ReadonlyArray<Klass<LexicalNode>>;
17
17
  onError: (error: Error, editor: LexicalEditor) => void;
18
- readOnly?: boolean;
18
+ editable?: boolean;
19
19
  theme?: EditorThemeClasses;
20
20
  editorState?: InitialEditorStateType;
21
21
  }>;