@lexical/react 0.10.0 → 0.11.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.
Files changed (45) hide show
  1. package/LexicalAutoEmbedPlugin.d.ts +2 -2
  2. package/LexicalAutoEmbedPlugin.dev.js +4 -4
  3. package/LexicalAutoEmbedPlugin.js.flow +2 -2
  4. package/LexicalAutoEmbedPlugin.prod.js +2 -2
  5. package/LexicalCharacterLimitPlugin.dev.js +4 -3
  6. package/LexicalCharacterLimitPlugin.prod.js +6 -6
  7. package/LexicalClickableLinkPlugin.dev.js +12 -9
  8. package/LexicalClickableLinkPlugin.prod.js +3 -4
  9. package/LexicalCollaborationPlugin.d.ts +2 -1
  10. package/LexicalCollaborationPlugin.dev.js +11 -10
  11. package/LexicalCollaborationPlugin.js.flow +13 -3
  12. package/LexicalCollaborationPlugin.prod.js +9 -9
  13. package/LexicalContentEditable.d.ts +1 -1
  14. package/LexicalContentEditable.dev.js +21 -10
  15. package/LexicalContentEditable.prod.js +3 -3
  16. package/LexicalContextMenuPlugin.d.ts +29 -0
  17. package/LexicalContextMenuPlugin.dev.js +508 -0
  18. package/LexicalContextMenuPlugin.js +9 -0
  19. package/LexicalContextMenuPlugin.prod.js +21 -0
  20. package/LexicalDecoratorBlockNode.d.ts +1 -0
  21. package/LexicalDecoratorBlockNode.dev.js +4 -0
  22. package/LexicalDecoratorBlockNode.prod.js +1 -1
  23. package/LexicalNodeEventPlugin.d.ts +1 -1
  24. package/LexicalNodeEventPlugin.dev.js +26 -33
  25. package/LexicalNodeEventPlugin.prod.js +2 -2
  26. package/LexicalNodeMenuPlugin.d.ts +22 -0
  27. package/LexicalNodeMenuPlugin.dev.js +521 -0
  28. package/LexicalNodeMenuPlugin.js +9 -0
  29. package/LexicalNodeMenuPlugin.js.flow +64 -0
  30. package/LexicalNodeMenuPlugin.prod.js +21 -0
  31. package/LexicalPlainTextPlugin.dev.js +1 -1
  32. package/LexicalPlainTextPlugin.prod.js +4 -4
  33. package/LexicalRichTextPlugin.dev.js +1 -1
  34. package/LexicalRichTextPlugin.prod.js +4 -4
  35. package/LexicalTabIndentationPlugin.d.ts +2 -0
  36. package/LexicalTabIndentationPlugin.dev.js +69 -10
  37. package/LexicalTabIndentationPlugin.js.flow +6 -0
  38. package/LexicalTabIndentationPlugin.prod.js +3 -1
  39. package/LexicalTypeaheadMenuPlugin.d.ts +10 -40
  40. package/LexicalTypeaheadMenuPlugin.dev.js +237 -210
  41. package/LexicalTypeaheadMenuPlugin.js.flow +7 -26
  42. package/LexicalTypeaheadMenuPlugin.prod.js +20 -20
  43. package/package.json +19 -19
  44. package/shared/LexicalMenu.d.ts +49 -0
  45. package/shared/useYjsCollaboration.d.ts +2 -2
@@ -0,0 +1,508 @@
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
+ 'use strict';
8
+
9
+ var LexicalComposerContext = require('@lexical/react/LexicalComposerContext');
10
+ var React = require('react');
11
+ var utils = require('@lexical/utils');
12
+ var lexical = require('lexical');
13
+
14
+ /**
15
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
16
+ *
17
+ * This source code is licensed under the MIT license found in the
18
+ * LICENSE file in the root directory of this source tree.
19
+ *
20
+ */
21
+ const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
22
+
23
+ /**
24
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
25
+ *
26
+ * This source code is licensed under the MIT license found in the
27
+ * LICENSE file in the root directory of this source tree.
28
+ *
29
+ */
30
+ const useLayoutEffectImpl = CAN_USE_DOM ? React.useLayoutEffect : React.useEffect;
31
+ var useLayoutEffect = useLayoutEffectImpl;
32
+
33
+ /**
34
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
35
+ *
36
+ * This source code is licensed under the MIT license found in the
37
+ * LICENSE file in the root directory of this source tree.
38
+ *
39
+ */
40
+ class MenuOption {
41
+ constructor(key) {
42
+ this.key = key;
43
+ this.ref = {
44
+ current: null
45
+ };
46
+ this.setRefElement = this.setRefElement.bind(this);
47
+ }
48
+
49
+ setRefElement(element) {
50
+ this.ref = {
51
+ current: element
52
+ };
53
+ }
54
+
55
+ }
56
+
57
+ const scrollIntoViewIfNeeded = target => {
58
+ const typeaheadContainerNode = document.getElementById('typeahead-menu');
59
+ if (!typeaheadContainerNode) return;
60
+ const typeaheadRect = typeaheadContainerNode.getBoundingClientRect();
61
+
62
+ if (typeaheadRect.top + typeaheadRect.height > window.innerHeight) {
63
+ typeaheadContainerNode.scrollIntoView({
64
+ block: 'center'
65
+ });
66
+ }
67
+
68
+ if (typeaheadRect.top < 0) {
69
+ typeaheadContainerNode.scrollIntoView({
70
+ block: 'center'
71
+ });
72
+ }
73
+
74
+ target.scrollIntoView({
75
+ block: 'nearest'
76
+ });
77
+ };
78
+ /**
79
+ * Walk backwards along user input and forward through entity title to try
80
+ * and replace more of the user's text with entity.
81
+ */
82
+
83
+
84
+ function getFullMatchOffset(documentText, entryText, offset) {
85
+ let triggerOffset = offset;
86
+
87
+ for (let i = triggerOffset; i <= entryText.length; i++) {
88
+ if (documentText.substr(-i) === entryText.substr(0, i)) {
89
+ triggerOffset = i;
90
+ }
91
+ }
92
+
93
+ return triggerOffset;
94
+ }
95
+ /**
96
+ * Split Lexical TextNode and return a new TextNode only containing matched text.
97
+ * Common use cases include: removing the node, replacing with a new node.
98
+ */
99
+
100
+
101
+ function $splitNodeContainingQuery(match) {
102
+ const selection = lexical.$getSelection();
103
+
104
+ if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed()) {
105
+ return null;
106
+ }
107
+
108
+ const anchor = selection.anchor;
109
+
110
+ if (anchor.type !== 'text') {
111
+ return null;
112
+ }
113
+
114
+ const anchorNode = anchor.getNode();
115
+
116
+ if (!anchorNode.isSimpleText()) {
117
+ return null;
118
+ }
119
+
120
+ const selectionOffset = anchor.offset;
121
+ const textContent = anchorNode.getTextContent().slice(0, selectionOffset);
122
+ const characterOffset = match.replaceableString.length;
123
+ const queryOffset = getFullMatchOffset(textContent, match.matchingString, characterOffset);
124
+ const startOffset = selectionOffset - queryOffset;
125
+
126
+ if (startOffset < 0) {
127
+ return null;
128
+ }
129
+
130
+ let newNode;
131
+
132
+ if (startOffset === 0) {
133
+ [newNode] = anchorNode.splitText(selectionOffset);
134
+ } else {
135
+ [, newNode] = anchorNode.splitText(startOffset, selectionOffset);
136
+ }
137
+
138
+ return newNode;
139
+ } // Got from https://stackoverflow.com/a/42543908/2013580
140
+
141
+
142
+ function getScrollParent(element, includeHidden) {
143
+ let style = getComputedStyle(element);
144
+ const excludeStaticParent = style.position === 'absolute';
145
+ const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
146
+
147
+ if (style.position === 'fixed') {
148
+ return document.body;
149
+ }
150
+
151
+ for (let parent = element; parent = parent.parentElement;) {
152
+ style = getComputedStyle(parent);
153
+
154
+ if (excludeStaticParent && style.position === 'static') {
155
+ continue;
156
+ }
157
+
158
+ if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) {
159
+ return parent;
160
+ }
161
+ }
162
+
163
+ return document.body;
164
+ }
165
+
166
+ function isTriggerVisibleInNearestScrollContainer(targetElement, containerElement) {
167
+ const tRect = targetElement.getBoundingClientRect();
168
+ const cRect = containerElement.getBoundingClientRect();
169
+ return tRect.top > cRect.top && tRect.top < cRect.bottom;
170
+ } // Reposition the menu on scroll, window resize, and element resize.
171
+
172
+
173
+ function useDynamicPositioning(resolution, targetElement, onReposition, onVisibilityChange) {
174
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
175
+ React.useEffect(() => {
176
+ if (targetElement != null && resolution != null) {
177
+ const rootElement = editor.getRootElement();
178
+ const rootScrollParent = rootElement != null ? getScrollParent(rootElement, false) : document.body;
179
+ let ticking = false;
180
+ let previousIsInView = isTriggerVisibleInNearestScrollContainer(targetElement, rootScrollParent);
181
+
182
+ const handleScroll = function () {
183
+ if (!ticking) {
184
+ window.requestAnimationFrame(function () {
185
+ onReposition();
186
+ ticking = false;
187
+ });
188
+ ticking = true;
189
+ }
190
+
191
+ const isInView = isTriggerVisibleInNearestScrollContainer(targetElement, rootScrollParent);
192
+
193
+ if (isInView !== previousIsInView) {
194
+ previousIsInView = isInView;
195
+
196
+ if (onVisibilityChange != null) {
197
+ onVisibilityChange(isInView);
198
+ }
199
+ }
200
+ };
201
+
202
+ const resizeObserver = new ResizeObserver(onReposition);
203
+ window.addEventListener('resize', onReposition);
204
+ document.addEventListener('scroll', handleScroll, {
205
+ capture: true,
206
+ passive: true
207
+ });
208
+ resizeObserver.observe(targetElement);
209
+ return () => {
210
+ resizeObserver.unobserve(targetElement);
211
+ window.removeEventListener('resize', onReposition);
212
+ document.removeEventListener('scroll', handleScroll);
213
+ };
214
+ }
215
+ }, [targetElement, editor, onVisibilityChange, onReposition, resolution]);
216
+ }
217
+ const SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND = lexical.createCommand('SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND');
218
+ function LexicalMenu({
219
+ close,
220
+ editor,
221
+ anchorElementRef,
222
+ resolution,
223
+ options,
224
+ menuRenderFn,
225
+ onSelectOption,
226
+ shouldSplitNodeWithQuery = false
227
+ }) {
228
+ const [selectedIndex, setHighlightedIndex] = React.useState(null);
229
+ const matchingString = resolution.match && resolution.match.matchingString;
230
+ React.useEffect(() => {
231
+ setHighlightedIndex(0);
232
+ }, [matchingString]);
233
+ const selectOptionAndCleanUp = React.useCallback(selectedEntry => {
234
+ editor.update(() => {
235
+ const textNodeContainingQuery = resolution.match != null && shouldSplitNodeWithQuery ? $splitNodeContainingQuery(resolution.match) : null;
236
+ onSelectOption(selectedEntry, textNodeContainingQuery, close, resolution.match ? resolution.match.matchingString : '');
237
+ });
238
+ }, [editor, shouldSplitNodeWithQuery, resolution.match, onSelectOption, close]);
239
+ const updateSelectedIndex = React.useCallback(index => {
240
+ const rootElem = editor.getRootElement();
241
+
242
+ if (rootElem !== null) {
243
+ rootElem.setAttribute('aria-activedescendant', 'typeahead-item-' + index);
244
+ setHighlightedIndex(index);
245
+ }
246
+ }, [editor]);
247
+ React.useEffect(() => {
248
+ return () => {
249
+ const rootElem = editor.getRootElement();
250
+
251
+ if (rootElem !== null) {
252
+ rootElem.removeAttribute('aria-activedescendant');
253
+ }
254
+ };
255
+ }, [editor]);
256
+ useLayoutEffect(() => {
257
+ if (options === null) {
258
+ setHighlightedIndex(null);
259
+ } else if (selectedIndex === null) {
260
+ updateSelectedIndex(0);
261
+ }
262
+ }, [options, selectedIndex, updateSelectedIndex]);
263
+ React.useEffect(() => {
264
+ return utils.mergeRegister(editor.registerCommand(SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND, ({
265
+ option
266
+ }) => {
267
+ if (option.ref && option.ref.current != null) {
268
+ scrollIntoViewIfNeeded(option.ref.current);
269
+ return true;
270
+ }
271
+
272
+ return false;
273
+ }, lexical.COMMAND_PRIORITY_LOW));
274
+ }, [editor, updateSelectedIndex]);
275
+ React.useEffect(() => {
276
+ return utils.mergeRegister(editor.registerCommand(lexical.KEY_ARROW_DOWN_COMMAND, payload => {
277
+ const event = payload;
278
+
279
+ if (options !== null && options.length && selectedIndex !== null) {
280
+ const newSelectedIndex = selectedIndex !== options.length - 1 ? selectedIndex + 1 : 0;
281
+ updateSelectedIndex(newSelectedIndex);
282
+ const option = options[newSelectedIndex];
283
+
284
+ if (option.ref != null && option.ref.current) {
285
+ editor.dispatchCommand(SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND, {
286
+ index: newSelectedIndex,
287
+ option
288
+ });
289
+ }
290
+
291
+ event.preventDefault();
292
+ event.stopImmediatePropagation();
293
+ }
294
+
295
+ return true;
296
+ }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ARROW_UP_COMMAND, payload => {
297
+ const event = payload;
298
+
299
+ if (options !== null && options.length && selectedIndex !== null) {
300
+ const newSelectedIndex = selectedIndex !== 0 ? selectedIndex - 1 : options.length - 1;
301
+ updateSelectedIndex(newSelectedIndex);
302
+ const option = options[newSelectedIndex];
303
+
304
+ if (option.ref != null && option.ref.current) {
305
+ scrollIntoViewIfNeeded(option.ref.current);
306
+ }
307
+
308
+ event.preventDefault();
309
+ event.stopImmediatePropagation();
310
+ }
311
+
312
+ return true;
313
+ }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ESCAPE_COMMAND, payload => {
314
+ const event = payload;
315
+ event.preventDefault();
316
+ event.stopImmediatePropagation();
317
+ close();
318
+ return true;
319
+ }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_TAB_COMMAND, payload => {
320
+ const event = payload;
321
+
322
+ if (options === null || selectedIndex === null || options[selectedIndex] == null) {
323
+ return false;
324
+ }
325
+
326
+ event.preventDefault();
327
+ event.stopImmediatePropagation();
328
+ selectOptionAndCleanUp(options[selectedIndex]);
329
+ return true;
330
+ }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ENTER_COMMAND, event => {
331
+ if (options === null || selectedIndex === null || options[selectedIndex] == null) {
332
+ return false;
333
+ }
334
+
335
+ if (event !== null) {
336
+ event.preventDefault();
337
+ event.stopImmediatePropagation();
338
+ }
339
+
340
+ selectOptionAndCleanUp(options[selectedIndex]);
341
+ return true;
342
+ }, lexical.COMMAND_PRIORITY_LOW));
343
+ }, [selectOptionAndCleanUp, close, editor, options, selectedIndex, updateSelectedIndex]);
344
+ const listItemProps = React.useMemo(() => ({
345
+ options,
346
+ selectOptionAndCleanUp,
347
+ selectedIndex,
348
+ setHighlightedIndex
349
+ }), [selectOptionAndCleanUp, selectedIndex, options]);
350
+ return menuRenderFn(anchorElementRef, listItemProps, resolution.match ? resolution.match.matchingString : '');
351
+ }
352
+ function useMenuAnchorRef(resolution, setResolution, className) {
353
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
354
+ const anchorElementRef = React.useRef(document.createElement('div'));
355
+ const positionMenu = React.useCallback(() => {
356
+ const rootElement = editor.getRootElement();
357
+ const containerDiv = anchorElementRef.current;
358
+ const menuEle = containerDiv.firstChild;
359
+
360
+ if (rootElement !== null && resolution !== null) {
361
+ const {
362
+ left,
363
+ top,
364
+ width,
365
+ height
366
+ } = resolution.getRect();
367
+ containerDiv.style.top = `${top + window.pageYOffset}px`;
368
+ containerDiv.style.left = `${left + window.pageXOffset}px`;
369
+ containerDiv.style.height = `${height}px`;
370
+ containerDiv.style.width = `${width}px`;
371
+
372
+ if (menuEle !== null) {
373
+ const menuRect = menuEle.getBoundingClientRect();
374
+ const menuHeight = menuRect.height;
375
+ const menuWidth = menuRect.width;
376
+ const rootElementRect = rootElement.getBoundingClientRect();
377
+
378
+ if (left + menuWidth > rootElementRect.right) {
379
+ containerDiv.style.left = `${left - menuWidth + window.pageXOffset}px`;
380
+ }
381
+
382
+ const margin = 10;
383
+
384
+ if ((top + menuHeight > window.innerHeight || top + menuHeight > rootElementRect.bottom) && top - rootElementRect.top > menuHeight) {
385
+ containerDiv.style.top = `${top - menuHeight + window.pageYOffset - (height + margin)}px`;
386
+ }
387
+ }
388
+
389
+ if (!containerDiv.isConnected) {
390
+ if (className != null) {
391
+ containerDiv.className = className;
392
+ }
393
+
394
+ containerDiv.setAttribute('aria-label', 'Typeahead menu');
395
+ containerDiv.setAttribute('id', 'typeahead-menu');
396
+ containerDiv.setAttribute('role', 'listbox');
397
+ containerDiv.style.display = 'block';
398
+ containerDiv.style.position = 'absolute';
399
+ document.body.append(containerDiv);
400
+ }
401
+
402
+ anchorElementRef.current = containerDiv;
403
+ rootElement.setAttribute('aria-controls', 'typeahead-menu');
404
+ }
405
+ }, [editor, resolution, className]);
406
+ React.useEffect(() => {
407
+ const rootElement = editor.getRootElement();
408
+
409
+ if (resolution !== null) {
410
+ positionMenu();
411
+ return () => {
412
+ if (rootElement !== null) {
413
+ rootElement.removeAttribute('aria-controls');
414
+ }
415
+
416
+ const containerDiv = anchorElementRef.current;
417
+
418
+ if (containerDiv !== null && containerDiv.isConnected) {
419
+ containerDiv.remove();
420
+ }
421
+ };
422
+ }
423
+ }, [editor, positionMenu, resolution]);
424
+ const onVisibilityChange = React.useCallback(isInView => {
425
+ if (resolution !== null) {
426
+ if (!isInView) {
427
+ setResolution(null);
428
+ }
429
+ }
430
+ }, [resolution, setResolution]);
431
+ useDynamicPositioning(resolution, anchorElementRef.current, positionMenu, onVisibilityChange);
432
+ return anchorElementRef;
433
+ }
434
+
435
+ /**
436
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
437
+ *
438
+ * This source code is licensed under the MIT license found in the
439
+ * LICENSE file in the root directory of this source tree.
440
+ *
441
+ */
442
+ const PRE_PORTAL_DIV_SIZE = 1;
443
+ function LexicalContextMenuPlugin({
444
+ options,
445
+ onClose,
446
+ onOpen,
447
+ onSelectOption,
448
+ menuRenderFn: contextMenuRenderFn,
449
+ anchorClassName
450
+ }) {
451
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
452
+ const [resolution, setResolution] = React.useState(null);
453
+ const menuRef = React.useRef(null);
454
+ const anchorElementRef = useMenuAnchorRef(resolution, setResolution, anchorClassName);
455
+ const closeNodeMenu = React.useCallback(() => {
456
+ setResolution(null);
457
+
458
+ if (onClose != null && resolution !== null) {
459
+ onClose();
460
+ }
461
+ }, [onClose, resolution]);
462
+ const openNodeMenu = React.useCallback(res => {
463
+ setResolution(res);
464
+
465
+ if (onOpen != null && resolution === null) {
466
+ onOpen(res);
467
+ }
468
+ }, [onOpen, resolution]);
469
+ const handleContextMenu = React.useCallback(event => {
470
+ event.preventDefault();
471
+ openNodeMenu({
472
+ getRect: () => new DOMRect(event.clientX, event.clientY, PRE_PORTAL_DIV_SIZE, PRE_PORTAL_DIV_SIZE)
473
+ });
474
+ }, [openNodeMenu]);
475
+ const handleClick = React.useCallback(event => {
476
+ if (resolution !== null && menuRef.current != null && event.target != null && !menuRef.current.contains(event.target)) {
477
+ closeNodeMenu();
478
+ }
479
+ }, [closeNodeMenu, resolution]);
480
+ React.useEffect(() => {
481
+ const editorElement = editor.getRootElement();
482
+
483
+ if (editorElement) {
484
+ editorElement.addEventListener('contextmenu', handleContextMenu);
485
+ return () => editorElement.removeEventListener('contextmenu', handleContextMenu);
486
+ }
487
+ }, [editor, handleContextMenu]);
488
+ React.useEffect(() => {
489
+ document.addEventListener('click', handleClick);
490
+ return () => document.removeEventListener('click', handleClick);
491
+ }, [editor, handleClick]);
492
+ return resolution === null || editor === null ? null : /*#__PURE__*/React.createElement(LexicalMenu, {
493
+ close: closeNodeMenu,
494
+ resolution: resolution,
495
+ editor: editor,
496
+ anchorElementRef: anchorElementRef,
497
+ options: options,
498
+ menuRenderFn: (anchorRef, itemProps) => contextMenuRenderFn(anchorRef, itemProps, {
499
+ setMenuRef: ref => {
500
+ menuRef.current = ref;
501
+ }
502
+ }),
503
+ onSelectOption: onSelectOption
504
+ });
505
+ }
506
+
507
+ exports.LexicalContextMenuPlugin = LexicalContextMenuPlugin;
508
+ exports.MenuOption = MenuOption;
@@ -0,0 +1,9 @@
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
+ 'use strict'
8
+ const LexicalContextMenuPlugin = process.env.NODE_ENV === 'development' ? require('./LexicalContextMenuPlugin.dev.js') : require('./LexicalContextMenuPlugin.prod.js')
9
+ module.exports = LexicalContextMenuPlugin;
@@ -0,0 +1,21 @@
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
+ 'use strict';var k=require("@lexical/react/LexicalComposerContext"),t=require("react"),w=require("@lexical/utils"),x=require("lexical"),y="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?t.useLayoutEffect:t.useEffect;class z{constructor(b){this.key=b;this.ref={current:null};this.setRefElement=this.setRefElement.bind(this)}setRefElement(b){this.ref={current:b}}}
8
+ let A=b=>{const a=document.getElementById("typeahead-menu");if(a){var d=a.getBoundingClientRect();d.top+d.height>window.innerHeight&&a.scrollIntoView({block:"center"});0>d.top&&a.scrollIntoView({block:"center"});b.scrollIntoView({block:"nearest"})}};
9
+ function B(b){var a=x.$getSelection();if(!x.$isRangeSelection(a)||!a.isCollapsed())return null;var d=a.anchor;if("text"!==d.type)return null;a=d.getNode();if(!a.isSimpleText())return null;d=d.offset;let l=a.getTextContent().slice(0,d);var f=b.matchingString;b=b.replaceableString.length;for(let m=b;m<=f.length;m++)l.substr(-m)===f.substr(0,m)&&(b=m);b=d-b;if(0>b)return null;let r;0===b?[r]=a.splitText(d):[,r]=a.splitText(b,d);return r}
10
+ function C(b,a){let d=getComputedStyle(b),l="absolute"===d.position;a=a?/(auto|scroll|hidden)/:/(auto|scroll)/;if("fixed"===d.position)return document.body;for(;b=b.parentElement;)if(d=getComputedStyle(b),(!l||"static"!==d.position)&&a.test(d.overflow+d.overflowY+d.overflowX))return b;return document.body}function D(b,a){b=b.getBoundingClientRect();a=a.getBoundingClientRect();return b.top>a.top&&b.top<a.bottom}
11
+ function E(b,a,d,l){let [f]=k.useLexicalComposerContext();t.useEffect(()=>{if(null!=a&&null!=b){let r=f.getRootElement(),m=null!=r?C(r,!1):document.body,h=!1,c=D(a,m),n=function(){h||(window.requestAnimationFrame(function(){d();h=!1}),h=!0);const q=D(a,m);q!==c&&(c=q,null!=l&&l(q))},p=new ResizeObserver(d);window.addEventListener("resize",d);document.addEventListener("scroll",n,{capture:!0,passive:!0});p.observe(a);return()=>{p.unobserve(a);window.removeEventListener("resize",d);document.removeEventListener("scroll",
12
+ n)}}},[a,f,l,d,b])}let F=x.createCommand("SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND");
13
+ function G({close:b,editor:a,anchorElementRef:d,resolution:l,options:f,menuRenderFn:r,onSelectOption:m,shouldSplitNodeWithQuery:h=!1}){let [c,n]=t.useState(null);t.useEffect(()=>{n(0)},[l.match&&l.match.matchingString]);let p=t.useCallback(g=>{a.update(()=>{const e=null!=l.match&&h?B(l.match):null;m(g,e,b,l.match?l.match.matchingString:"")})},[a,h,l.match,m,b]),q=t.useCallback(g=>{const e=a.getRootElement();null!==e&&(e.setAttribute("aria-activedescendant","typeahead-item-"+g),n(g))},[a]);t.useEffect(()=>
14
+ ()=>{let g=a.getRootElement();null!==g&&g.removeAttribute("aria-activedescendant")},[a]);y(()=>{null===f?n(null):null===c&&q(0)},[f,c,q]);t.useEffect(()=>w.mergeRegister(a.registerCommand(F,({option:g})=>g.ref&&null!=g.ref.current?(A(g.ref.current),!0):!1,x.COMMAND_PRIORITY_LOW)),[a,q]);t.useEffect(()=>w.mergeRegister(a.registerCommand(x.KEY_ARROW_DOWN_COMMAND,g=>{if(null!==f&&f.length&&null!==c){let e=c!==f.length-1?c+1:0;q(e);let v=f[e];null!=v.ref&&v.ref.current&&a.dispatchCommand(F,{index:e,option:v});
15
+ g.preventDefault();g.stopImmediatePropagation()}return!0},x.COMMAND_PRIORITY_LOW),a.registerCommand(x.KEY_ARROW_UP_COMMAND,g=>{if(null!==f&&f.length&&null!==c){var e=0!==c?c-1:f.length-1;q(e);e=f[e];null!=e.ref&&e.ref.current&&A(e.ref.current);g.preventDefault();g.stopImmediatePropagation()}return!0},x.COMMAND_PRIORITY_LOW),a.registerCommand(x.KEY_ESCAPE_COMMAND,g=>{g.preventDefault();g.stopImmediatePropagation();b();return!0},x.COMMAND_PRIORITY_LOW),a.registerCommand(x.KEY_TAB_COMMAND,g=>{if(null===
16
+ f||null===c||null==f[c])return!1;g.preventDefault();g.stopImmediatePropagation();p(f[c]);return!0},x.COMMAND_PRIORITY_LOW),a.registerCommand(x.KEY_ENTER_COMMAND,g=>{if(null===f||null===c||null==f[c])return!1;null!==g&&(g.preventDefault(),g.stopImmediatePropagation());p(f[c]);return!0},x.COMMAND_PRIORITY_LOW)),[p,b,a,f,c,q]);let u=t.useMemo(()=>({options:f,selectOptionAndCleanUp:p,selectedIndex:c,setHighlightedIndex:n}),[p,c,f]);return r(d,u,l.match?l.match.matchingString:"")}
17
+ function H(b,a,d){let [l]=k.useLexicalComposerContext(),f=t.useRef(document.createElement("div")),r=t.useCallback(()=>{const h=l.getRootElement(),c=f.current;var n=c.firstChild;if(null!==h&&null!==b){const {left:q,top:u,width:g,height:e}=b.getRect();c.style.top=`${u+window.pageYOffset}px`;c.style.left=`${q+window.pageXOffset}px`;c.style.height=`${e}px`;c.style.width=`${g}px`;if(null!==n){var p=n.getBoundingClientRect();n=p.height;p=p.width;const v=h.getBoundingClientRect();q+p>v.right&&(c.style.left=
18
+ `${q-p+window.pageXOffset}px`);(u+n>window.innerHeight||u+n>v.bottom)&&u-v.top>n&&(c.style.top=`${u-n+window.pageYOffset-(e+10)}px`)}c.isConnected||(null!=d&&(c.className=d),c.setAttribute("aria-label","Typeahead menu"),c.setAttribute("id","typeahead-menu"),c.setAttribute("role","listbox"),c.style.display="block",c.style.position="absolute",document.body.append(c));f.current=c;h.setAttribute("aria-controls","typeahead-menu")}},[l,b,d]);t.useEffect(()=>{let h=l.getRootElement();if(null!==b)return r(),
19
+ ()=>{null!==h&&h.removeAttribute("aria-controls");let c=f.current;null!==c&&c.isConnected&&c.remove()}},[l,r,b]);let m=t.useCallback(h=>{null!==b&&(h||a(null))},[b,a]);E(b,f.current,r,m);return f}
20
+ exports.LexicalContextMenuPlugin=function({options:b,onClose:a,onOpen:d,onSelectOption:l,menuRenderFn:f,anchorClassName:r}){let [m]=k.useLexicalComposerContext(),[h,c]=t.useState(null),n=t.useRef(null);r=H(h,c,r);let p=t.useCallback(()=>{c(null);null!=a&&null!==h&&a()},[a,h]),q=t.useCallback(e=>{c(e);null!=d&&null===h&&d(e)},[d,h]),u=t.useCallback(e=>{e.preventDefault();q({getRect:()=>new DOMRect(e.clientX,e.clientY,1,1)})},[q]),g=t.useCallback(e=>{null===h||null==n.current||null==e.target||n.current.contains(e.target)||
21
+ p()},[p,h]);t.useEffect(()=>{let e=m.getRootElement();if(e)return e.addEventListener("contextmenu",u),()=>e.removeEventListener("contextmenu",u)},[m,u]);t.useEffect(()=>{document.addEventListener("click",g);return()=>document.removeEventListener("click",g)},[m,g]);return null===h||null===m?null:t.createElement(G,{close:p,resolution:h,editor:m,anchorElementRef:r,options:b,menuRenderFn:(e,v)=>f(e,v,{setMenuRef:I=>{n.current=I}}),onSelectOption:l})};exports.MenuOption=z
@@ -18,5 +18,6 @@ export declare class DecoratorBlockNode extends DecoratorNode<JSX.Element> {
18
18
  createDOM(): HTMLElement;
19
19
  updateDOM(): false;
20
20
  setFormat(format: ElementFormatType): void;
21
+ isInline(): false;
21
22
  }
22
23
  export declare function $isDecoratorBlockNode(node: LexicalNode | null | undefined): node is DecoratorBlockNode;
@@ -42,6 +42,10 @@ class DecoratorBlockNode extends lexical.DecoratorNode {
42
42
  self.__format = format;
43
43
  }
44
44
 
45
+ isInline() {
46
+ return false;
47
+ }
48
+
45
49
  }
46
50
  function $isDecoratorBlockNode(node) {
47
51
  return node instanceof DecoratorBlockNode;
@@ -4,4 +4,4 @@
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");class c extends b.DecoratorNode{constructor(a,d){super(d);this.__format=a||""}exportJSON(){return{format:this.__format||"",type:"decorator-block",version:1}}createDOM(){return document.createElement("div")}updateDOM(){return!1}setFormat(a){this.getWritable().__format=a}}exports.$isDecoratorBlockNode=function(a){return a instanceof c};exports.DecoratorBlockNode=c
7
+ 'use strict';var b=require("lexical");class c extends b.DecoratorNode{constructor(a,d){super(d);this.__format=a||""}exportJSON(){return{format:this.__format||"",type:"decorator-block",version:1}}createDOM(){return document.createElement("div")}updateDOM(){return!1}setFormat(a){this.getWritable().__format=a}isInline(){return!1}}exports.$isDecoratorBlockNode=function(a){return a instanceof c};exports.DecoratorBlockNode=c
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  */
8
- import { type Klass, type LexicalEditor, type LexicalNode, type NodeKey } from 'lexical';
8
+ import type { Klass, LexicalEditor, LexicalNode, NodeKey } from 'lexical';
9
9
  export declare function NodeEventPlugin({ nodeType, eventType, eventListener, }: {
10
10
  nodeType: Klass<LexicalNode>;
11
11
  eventType: string;
@@ -7,6 +7,8 @@
7
7
  'use strict';
8
8
 
9
9
  var LexicalComposerContext = require('@lexical/react/LexicalComposerContext');
10
+ var utils = require('@lexical/utils');
11
+ var lexical = require('lexical');
10
12
  var react = require('react');
11
13
 
12
14
  /**
@@ -16,25 +18,7 @@ var react = require('react');
16
18
  * LICENSE file in the root directory of this source tree.
17
19
  *
18
20
  */
19
- const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
20
-
21
- /**
22
- * Copyright (c) Meta Platforms, Inc. and affiliates.
23
- *
24
- * This source code is licensed under the MIT license found in the
25
- * LICENSE file in the root directory of this source tree.
26
- *
27
- */
28
- const useLayoutEffectImpl = CAN_USE_DOM ? react.useLayoutEffect : react.useEffect;
29
- var useLayoutEffect = useLayoutEffectImpl;
30
-
31
- /**
32
- * Copyright (c) Meta Platforms, Inc. and affiliates.
33
- *
34
- * This source code is licensed under the MIT license found in the
35
- * LICENSE file in the root directory of this source tree.
36
- *
37
- */
21
+ const capturedEvents = new Set(['mouseenter', 'mouseleave']);
38
22
  function NodeEventPlugin({
39
23
  nodeType,
40
24
  eventType,
@@ -43,24 +27,33 @@ function NodeEventPlugin({
43
27
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
44
28
  const listenerRef = react.useRef(eventListener);
45
29
  listenerRef.current = eventListener;
46
- useLayoutEffect(() => {
47
- const registedElements = new WeakSet();
48
- return editor.registerMutationListener(nodeType, mutations => {
49
- editor.getEditorState().read(() => {
50
- for (const [key, mutation] of mutations) {
51
- const element = editor.getElementByKey(key);
30
+ react.useEffect(() => {
31
+ const isCaptured = capturedEvents.has(eventType);
32
+
33
+ const onEvent = event => {
34
+ editor.update(() => {
35
+ const nearestNode = lexical.$getNearestNodeFromDOMNode(event.target);
36
+
37
+ if (nearestNode !== null) {
38
+ const targetNode = isCaptured ? nearestNode instanceof nodeType ? nearestNode : null : utils.$findMatchingParent(nearestNode, node => node instanceof nodeType);
52
39
 
53
- if ( // Updated might be a move, so that might mean a new DOM element
54
- // is created. In this case, we need to add and event listener too.
55
- (mutation === 'created' || mutation === 'updated') && element !== null && !registedElements.has(element)) {
56
- registedElements.add(element);
57
- element.addEventListener(eventType, event => {
58
- listenerRef.current(event, editor, key);
59
- });
40
+ if (targetNode !== null) {
41
+ listenerRef.current(event, editor, targetNode.getKey());
42
+ return;
60
43
  }
61
44
  }
62
45
  });
63
- }); // wW intentionally don't respect changes to eventType.
46
+ };
47
+
48
+ return editor.registerRootListener((rootElement, prevRootElement) => {
49
+ if (rootElement) {
50
+ rootElement.addEventListener(eventType, onEvent, isCaptured);
51
+ }
52
+
53
+ if (prevRootElement) {
54
+ prevRootElement.removeEventListener(eventType, onEvent, isCaptured);
55
+ }
56
+ }); // We intentionally don't respect changes to eventType.
64
57
  // eslint-disable-next-line react-hooks/exhaustive-deps
65
58
  }, [editor, nodeType]);
66
59
  return null;
@@ -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/react/LexicalComposerContext"),l=require("react"),m="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?l.useLayoutEffect:l.useEffect;
8
- exports.NodeEventPlugin=function({nodeType:d,eventType:n,eventListener:e}){let [a]=c.useLexicalComposerContext(),f=l.useRef(e);f.current=e;m(()=>{let g=new WeakSet;return a.registerMutationListener(d,p=>{a.getEditorState().read(()=>{for(let [h,k]of p){let b=a.getElementByKey(h);"created"!==k&&"updated"!==k||null===b||g.has(b)||(g.add(b),b.addEventListener(n,q=>{f.current(q,a,h)}))}})})},[a,d]);return null}
7
+ 'use strict';var g=require("@lexical/react/LexicalComposerContext"),m=require("@lexical/utils"),n=require("lexical"),p=require("react");let q=new Set(["mouseenter","mouseleave"]);
8
+ exports.NodeEventPlugin=function({nodeType:d,eventType:e,eventListener:h}){let [c]=g.useLexicalComposerContext(),k=p.useRef(h);k.current=h;p.useEffect(()=>{let f=q.has(e),l=b=>{c.update(()=>{var a=n.$getNearestNodeFromDOMNode(b.target);null!==a&&(a=f?a instanceof d?a:null:m.$findMatchingParent(a,r=>r instanceof d),null!==a&&k.current(b,c,a.getKey()))})};return c.registerRootListener((b,a)=>{b&&b.addEventListener(e,l,f);a&&a.removeEventListener(e,l,f)})},[c,d]);return null}