@lexical/react 0.38.3-nightly.20251120.0 → 0.38.3-nightly.20251124.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.
@@ -12,7 +12,6 @@ var LexicalComposerContext = require('@lexical/react/LexicalComposerContext');
12
12
  var lexical = require('lexical');
13
13
  var React = require('react');
14
14
  var utils = require('@lexical/utils');
15
- var ReactDOM = require('react-dom');
16
15
  var jsxRuntime = require('react/jsx-runtime');
17
16
 
18
17
  /**
@@ -71,8 +70,6 @@ const useLayoutEffectImpl = CAN_USE_DOM ? React.useLayoutEffect : React.useEffec
71
70
  class MenuOption {
72
71
  key;
73
72
  ref;
74
- icon;
75
- title;
76
73
  constructor(key) {
77
74
  this.key = key;
78
75
  this.ref = {
@@ -222,38 +219,13 @@ function useDynamicPositioning(resolution, targetElement, onReposition, onVisibi
222
219
  }, [targetElement, editor, onVisibilityChange, onReposition, resolution]);
223
220
  }
224
221
  const SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND = lexical.createCommand('SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND');
225
- function MenuItem({
226
- index,
227
- isSelected,
228
- onClick,
229
- onMouseEnter,
230
- option
231
- }) {
232
- let className = 'item';
233
- if (isSelected) {
234
- className += ' selected';
235
- }
236
- return /*#__PURE__*/jsxRuntime.jsxs("li", {
237
- tabIndex: -1,
238
- className: className,
239
- ref: option.setRefElement,
240
- role: "option",
241
- "aria-selected": isSelected,
242
- id: 'typeahead-item-' + index,
243
- onMouseEnter: onMouseEnter,
244
- onClick: onClick,
245
- children: [option.icon, /*#__PURE__*/jsxRuntime.jsx("span", {
246
- className: "text",
247
- children: option.title
248
- })]
249
- }, option.key);
250
- }
251
222
  function LexicalMenu({
252
223
  close,
253
224
  editor,
254
225
  anchorElementRef,
255
226
  resolution,
256
227
  options,
228
+ menuRenderFn,
257
229
  onSelectOption,
258
230
  shouldSplitNodeWithQuery = false,
259
231
  commandPriority = lexical.COMMAND_PRIORITY_LOW,
@@ -281,25 +253,6 @@ function LexicalMenu({
281
253
  setHighlightedIndex(index);
282
254
  }
283
255
  }, [editor]);
284
- const menuRenderFn = React.useCallback(() => {
285
- return anchorElementRef.current && options.length ? /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/jsxRuntime.jsx("div", {
286
- className: "typeahead-popover mentions-menu",
287
- children: /*#__PURE__*/jsxRuntime.jsx("ul", {
288
- children: options.map((option, i) => /*#__PURE__*/jsxRuntime.jsx(MenuItem, {
289
- index: i,
290
- isSelected: selectedIndex === i,
291
- onClick: () => {
292
- setHighlightedIndex(i);
293
- selectOptionAndCleanUp(option);
294
- },
295
- onMouseEnter: () => {
296
- setHighlightedIndex(i);
297
- },
298
- option: option
299
- }, option.key))
300
- })
301
- }), anchorElementRef.current) : null;
302
- }, [anchorElementRef, options, selectedIndex, selectOptionAndCleanUp, setHighlightedIndex]);
303
256
  React.useEffect(() => {
304
257
  return () => {
305
258
  const rootElem = editor.getRootElement();
@@ -395,7 +348,13 @@ function LexicalMenu({
395
348
  return true;
396
349
  }, commandPriority));
397
350
  }, [selectOptionAndCleanUp, close, editor, options, selectedIndex, updateSelectedIndex, commandPriority]);
398
- return menuRenderFn();
351
+ const listItemProps = React.useMemo(() => ({
352
+ options,
353
+ selectOptionAndCleanUp,
354
+ selectedIndex,
355
+ setHighlightedIndex
356
+ }), [selectOptionAndCleanUp, selectedIndex, options]);
357
+ return menuRenderFn(anchorElementRef, listItemProps, resolution.match ? resolution.match.matchingString : '');
399
358
  }
400
359
  function setContainerDivAttributes(containerDiv, className) {
401
360
  if (className != null) {
@@ -501,6 +460,7 @@ function LexicalNodeMenuPlugin({
501
460
  onClose,
502
461
  onOpen,
503
462
  onSelectOption,
463
+ menuRenderFn,
504
464
  anchorClassName,
505
465
  commandPriority = lexical.COMMAND_PRIORITY_LOW,
506
466
  parent
@@ -557,6 +517,7 @@ function LexicalNodeMenuPlugin({
557
517
  editor: editor,
558
518
  anchorElementRef: anchorElementRef,
559
519
  options: options,
520
+ menuRenderFn: menuRenderFn,
560
521
  onSelectOption: onSelectOption,
561
522
  commandPriority: commandPriority
562
523
  });
@@ -8,10 +8,9 @@
8
8
 
9
9
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
10
10
  import { createCommand, COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ESCAPE_COMMAND, KEY_TAB_COMMAND, KEY_ENTER_COMMAND, $getSelection, $isRangeSelection, $getNodeByKey } from 'lexical';
11
- import React, { useLayoutEffect, useEffect, useRef, useCallback, useState } from 'react';
11
+ import React, { useLayoutEffect, useEffect, useRef, useCallback, useState, useMemo } from 'react';
12
12
  import { mergeRegister } from '@lexical/utils';
13
- import ReactDOM from 'react-dom';
14
- import { jsx, jsxs } from 'react/jsx-runtime';
13
+ import { jsx } from 'react/jsx-runtime';
15
14
 
16
15
  /**
17
16
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -69,8 +68,6 @@ const useLayoutEffectImpl = CAN_USE_DOM ? useLayoutEffect : useEffect;
69
68
  class MenuOption {
70
69
  key;
71
70
  ref;
72
- icon;
73
- title;
74
71
  constructor(key) {
75
72
  this.key = key;
76
73
  this.ref = {
@@ -220,38 +217,13 @@ function useDynamicPositioning(resolution, targetElement, onReposition, onVisibi
220
217
  }, [targetElement, editor, onVisibilityChange, onReposition, resolution]);
221
218
  }
222
219
  const SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND = createCommand('SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND');
223
- function MenuItem({
224
- index,
225
- isSelected,
226
- onClick,
227
- onMouseEnter,
228
- option
229
- }) {
230
- let className = 'item';
231
- if (isSelected) {
232
- className += ' selected';
233
- }
234
- return /*#__PURE__*/jsxs("li", {
235
- tabIndex: -1,
236
- className: className,
237
- ref: option.setRefElement,
238
- role: "option",
239
- "aria-selected": isSelected,
240
- id: 'typeahead-item-' + index,
241
- onMouseEnter: onMouseEnter,
242
- onClick: onClick,
243
- children: [option.icon, /*#__PURE__*/jsx("span", {
244
- className: "text",
245
- children: option.title
246
- })]
247
- }, option.key);
248
- }
249
220
  function LexicalMenu({
250
221
  close,
251
222
  editor,
252
223
  anchorElementRef,
253
224
  resolution,
254
225
  options,
226
+ menuRenderFn,
255
227
  onSelectOption,
256
228
  shouldSplitNodeWithQuery = false,
257
229
  commandPriority = COMMAND_PRIORITY_LOW,
@@ -279,25 +251,6 @@ function LexicalMenu({
279
251
  setHighlightedIndex(index);
280
252
  }
281
253
  }, [editor]);
282
- const menuRenderFn = useCallback(() => {
283
- return anchorElementRef.current && options.length ? /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/jsx("div", {
284
- className: "typeahead-popover mentions-menu",
285
- children: /*#__PURE__*/jsx("ul", {
286
- children: options.map((option, i) => /*#__PURE__*/jsx(MenuItem, {
287
- index: i,
288
- isSelected: selectedIndex === i,
289
- onClick: () => {
290
- setHighlightedIndex(i);
291
- selectOptionAndCleanUp(option);
292
- },
293
- onMouseEnter: () => {
294
- setHighlightedIndex(i);
295
- },
296
- option: option
297
- }, option.key))
298
- })
299
- }), anchorElementRef.current) : null;
300
- }, [anchorElementRef, options, selectedIndex, selectOptionAndCleanUp, setHighlightedIndex]);
301
254
  useEffect(() => {
302
255
  return () => {
303
256
  const rootElem = editor.getRootElement();
@@ -393,7 +346,13 @@ function LexicalMenu({
393
346
  return true;
394
347
  }, commandPriority));
395
348
  }, [selectOptionAndCleanUp, close, editor, options, selectedIndex, updateSelectedIndex, commandPriority]);
396
- return menuRenderFn();
349
+ const listItemProps = useMemo(() => ({
350
+ options,
351
+ selectOptionAndCleanUp,
352
+ selectedIndex,
353
+ setHighlightedIndex
354
+ }), [selectOptionAndCleanUp, selectedIndex, options]);
355
+ return menuRenderFn(anchorElementRef, listItemProps, resolution.match ? resolution.match.matchingString : '');
397
356
  }
398
357
  function setContainerDivAttributes(containerDiv, className) {
399
358
  if (className != null) {
@@ -499,6 +458,7 @@ function LexicalNodeMenuPlugin({
499
458
  onClose,
500
459
  onOpen,
501
460
  onSelectOption,
461
+ menuRenderFn,
502
462
  anchorClassName,
503
463
  commandPriority = COMMAND_PRIORITY_LOW,
504
464
  parent
@@ -555,6 +515,7 @@ function LexicalNodeMenuPlugin({
555
515
  editor: editor,
556
516
  anchorElementRef: anchorElementRef,
557
517
  options: options,
518
+ menuRenderFn: menuRenderFn,
558
519
  onSelectOption: onSelectOption,
559
520
  commandPriority: commandPriority
560
521
  });
@@ -28,6 +28,17 @@ declare export class MenuOption {
28
28
  setRefElement(element: HTMLElement | null): void;
29
29
  }
30
30
 
31
+ export type MenuRenderFn<TOption> = (
32
+ anchorElementRef: {current: HTMLElement | null},
33
+ itemProps: {
34
+ selectedIndex: number | null,
35
+ selectOptionAndCleanUp: (option: TOption) => void,
36
+ setHighlightedIndex: (index: number) => void,
37
+ options: Array<TOption>,
38
+ },
39
+ matchingString: string,
40
+ ) => React.Portal | React.MixedElement | null;
41
+
31
42
  export type TriggerFn = (
32
43
  text: string,
33
44
  editor: LexicalEditor,
@@ -44,6 +55,7 @@ type NodeMenuPluginProps<TOption> = {
44
55
  nodeKey: NodeKey | null,
45
56
  onClose?: () => void,
46
57
  onOpen?: (resolution: MenuResolution) => void,
58
+ menuRenderFn: MenuRenderFn<TOption>,
47
59
  anchorClassName?: string,
48
60
  };
49
61
 
@@ -6,4 +6,4 @@
6
6
  *
7
7
  */
8
8
 
9
- "use strict";var e=require("@lexical/react/LexicalComposerContext"),t=require("lexical"),n=require("react"),l=require("@lexical/utils"),o=require("react-dom"),r=require("react/jsx-runtime");const i="startTransition";const s="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement,u=s?n.useLayoutEffect:n.useEffect;const c=e=>{const t=document.getElementById("typeahead-menu");if(!t)return;const n=t.getBoundingClientRect();n.top+n.height>window.innerHeight&&t.scrollIntoView({block:"center"}),n.top<0&&t.scrollIntoView({block:"center"}),e.scrollIntoView({block:"nearest"})};function a(e,t){const n=e.getBoundingClientRect(),l=t.getBoundingClientRect();return n.top>=l.top-6&&n.top<=l.bottom+6}function d(t,l,o,r){const[i]=e.useLexicalComposerContext();n.useEffect(()=>{if(null!=l&&null!=t){const e=i.getRootElement(),t=null!=e?function(e){let t=getComputedStyle(e);const n="absolute"===t.position,l=/(auto|scroll)/;if("fixed"===t.position)return document.body;for(let o=e;o=o.parentElement;)if(t=getComputedStyle(o),(!n||"static"!==t.position)&&l.test(t.overflow+t.overflowY+t.overflowX))return o;return document.body}(e):document.body;let n=!1,s=a(l,t);const u=function(){n||(window.requestAnimationFrame(function(){o(),n=!1}),n=!0);const e=a(l,t);e!==s&&(s=e,null!=r&&r(e))},c=new ResizeObserver(o);return window.addEventListener("resize",o),document.addEventListener("scroll",u,{capture:!0,passive:!0}),c.observe(l),()=>{c.unobserve(l),window.removeEventListener("resize",o),document.removeEventListener("scroll",u,!0)}}},[l,i,r,o,t])}const m=t.createCommand("SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND");function p({index:e,isSelected:t,onClick:n,onMouseEnter:l,option:o}){let i="item";return t&&(i+=" selected"),r.jsxs("li",{tabIndex:-1,className:i,ref:o.setRefElement,role:"option","aria-selected":t,id:"typeahead-item-"+e,onMouseEnter:l,onClick:n,children:[o.icon,r.jsx("span",{className:"text",children:o.title})]},o.key)}function f({close:e,editor:i,anchorElementRef:s,resolution:a,options:d,onSelectOption:f,shouldSplitNodeWithQuery:g=!1,commandPriority:h=t.COMMAND_PRIORITY_LOW,preselectFirstItem:C=!0}){const[E,y]=n.useState(null),x=null!==E?Math.min(d.length-1,E):null,b=a.match&&a.match.matchingString;n.useEffect(()=>{C&&y(0)},[b,C]);const v=n.useCallback(n=>{i.update(()=>{const l=null!=a.match&&g?function(e){const n=t.$getSelection();if(!t.$isRangeSelection(n)||!n.isCollapsed())return null;const l=n.anchor;if("text"!==l.type)return null;const o=l.getNode();if(!o.isSimpleText())return null;const r=l.offset,i=o.getTextContent().slice(0,r),s=e.replaceableString.length,u=r-function(e,t,n){let l=n;for(let n=l;n<=t.length;n++)e.slice(-n)===t.substring(0,n)&&(l=n);return l}(i,e.matchingString,s);if(u<0)return null;let c;return 0===u?[c]=o.splitText(r):[,c]=o.splitText(u,r),c}(a.match):null;f(n,l,e,a.match?a.match.matchingString:"")})},[i,g,a.match,f,e]),w=n.useCallback(e=>{const t=i.getRootElement();null!==t&&(t.setAttribute("aria-activedescendant","typeahead-item-"+e),y(e))},[i]),R=n.useCallback(()=>s.current&&d.length?o.createPortal(r.jsx("div",{className:"typeahead-popover mentions-menu",children:r.jsx("ul",{children:d.map((e,t)=>r.jsx(p,{index:t,isSelected:x===t,onClick:()=>{y(t),v(e)},onMouseEnter:()=>{y(t)},option:e},e.key))})}),s.current):null,[s,d,x,v,y]);return n.useEffect(()=>()=>{const e=i.getRootElement();null!==e&&e.removeAttribute("aria-activedescendant")},[i]),u(()=>{null===d?y(null):null===x&&C&&w(0)},[d,x,w,C]),n.useEffect(()=>l.mergeRegister(i.registerCommand(m,({option:e})=>!(!e.ref||null==e.ref.current)&&(c(e.ref.current),!0),h)),[i,w,h]),n.useEffect(()=>l.mergeRegister(i.registerCommand(t.KEY_ARROW_DOWN_COMMAND,e=>{const t=e;if(null!==d&&d.length){const e=null===x?0:x!==d.length-1?x+1:0;w(e);const n=d[e];if(!n)return w(-1),t.preventDefault(),t.stopImmediatePropagation(),!0;n.ref&&n.ref.current&&i.dispatchCommand(m,{index:e,option:n}),t.preventDefault(),t.stopImmediatePropagation()}return!0},h),i.registerCommand(t.KEY_ARROW_UP_COMMAND,e=>{const t=e;if(null!==d&&d.length){const e=null===x?d.length-1:0!==x?x-1:d.length-1;w(e);const n=d[e];if(!n)return w(-1),t.preventDefault(),t.stopImmediatePropagation(),!0;n.ref&&n.ref.current&&c(n.ref.current),t.preventDefault(),t.stopImmediatePropagation()}return!0},h),i.registerCommand(t.KEY_ESCAPE_COMMAND,t=>{const n=t;return n.preventDefault(),n.stopImmediatePropagation(),e(),!0},h),i.registerCommand(t.KEY_TAB_COMMAND,e=>{const t=e;return null!==d&&null!==x&&null!=d[x]&&(t.preventDefault(),t.stopImmediatePropagation(),v(d[x]),!0)},h),i.registerCommand(t.KEY_ENTER_COMMAND,e=>null!==d&&null!==x&&null!=d[x]&&(null!==e&&(e.preventDefault(),e.stopImmediatePropagation()),v(d[x]),!0),h)),[v,e,i,d,x,w,h]),R()}function g(e,t){null!=t&&(e.className=t),e.setAttribute("aria-label","Typeahead menu"),e.setAttribute("role","listbox"),e.style.display="block",e.style.position="absolute"}exports.LexicalNodeMenuPlugin=function({options:l,nodeKey:o,onClose:u,onOpen:c,onSelectOption:a,anchorClassName:m,commandPriority:p=t.COMMAND_PRIORITY_LOW,parent:h}){const[C]=e.useLexicalComposerContext(),[E,y]=n.useState(null),x=function(t,l,o,r=(s?document.body:void 0),i=!0){const[u]=e.useLexicalComposerContext(),c=s?document.createElement("div"):null,a=n.useRef(c),m=n.useCallback(()=>{if(null===a.current||void 0===r)return;a.current.style.top=a.current.style.bottom;const e=u.getRootElement(),n=a.current,l=n.firstChild;if(null!==e&&null!==t){const{left:s,top:u,width:c,height:d}=t.getRect(),m=a.current.offsetHeight;if(n.style.top=`${u+m+3+(i?window.pageYOffset:0)}px`,n.style.left=`${s+window.pageXOffset}px`,n.style.height=`${d}px`,n.style.width=`${c}px`,null!==l){l.style.top=`${u}`;const t=l.getBoundingClientRect(),o=t.height,r=t.width,c=e.getBoundingClientRect();s+r>c.right&&(n.style.left=`${c.right-r+window.pageXOffset}px`),(u+o>window.innerHeight||u+o>c.bottom)&&u-c.top>o+d&&(n.style.top=`${u-o-d+(i?window.pageYOffset:0)}px`)}n.isConnected||(g(n,o),r.append(n)),n.setAttribute("id","typeahead-menu"),e.setAttribute("aria-controls","typeahead-menu")}},[u,t,i,o,r]);n.useEffect(()=>{const e=u.getRootElement();return null!==t&&m(),()=>{null!==e&&e.removeAttribute("aria-controls");const t=a.current;null!==t&&t.isConnected&&(t.remove(),t.removeAttribute("id"))}},[u,m,t]);const p=n.useCallback(e=>{null!==t&&(e||l(null))},[t,l]);return d(t,a.current,m,p),null!=c&&c===a.current&&(g(c,o),null!=r&&r.append(c)),a}(E,y,m,h),b=n.useCallback(()=>{y(null),null!=u&&null!==E&&u()},[u,E]),v=n.useCallback(e=>{y(e),null!=c&&null===E&&c(e)},[c,E]),w=n.useCallback(()=>{o?C.update(()=>{const e=t.$getNodeByKey(o),l=C.getElementByKey(o);var r;null!=e&&null!=l&&null==E&&(r=()=>v({getRect:()=>l.getBoundingClientRect()}),i in n?n[i](r):r())}):null==o&&null!=E&&b()},[b,C,o,v,E]);return n.useEffect(()=>{w()},[w,o]),n.useEffect(()=>{if(null!=o)return C.registerUpdateListener(({dirtyElements:e})=>{e.get(o)&&w()})},[C,w,o]),null===x.current||null===E||null===C?null:r.jsx(f,{close:b,resolution:E,editor:C,anchorElementRef:x,options:l,onSelectOption:a,commandPriority:p})},exports.MenuOption=class{key;ref;icon;title;constructor(e){this.key=e,this.ref={current:null},this.setRefElement=this.setRefElement.bind(this)}setRefElement(e){this.ref={current:e}}};
9
+ "use strict";var e=require("@lexical/react/LexicalComposerContext"),t=require("lexical"),n=require("react"),o=require("@lexical/utils"),l=require("react/jsx-runtime");const r="startTransition";const i="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement,u=i?n.useLayoutEffect:n.useEffect;const s=e=>{const t=document.getElementById("typeahead-menu");if(!t)return;const n=t.getBoundingClientRect();n.top+n.height>window.innerHeight&&t.scrollIntoView({block:"center"}),n.top<0&&t.scrollIntoView({block:"center"}),e.scrollIntoView({block:"nearest"})};function c(e,t){const n=e.getBoundingClientRect(),o=t.getBoundingClientRect();return n.top>=o.top-6&&n.top<=o.bottom+6}function a(t,o,l,r){const[i]=e.useLexicalComposerContext();n.useEffect(()=>{if(null!=o&&null!=t){const e=i.getRootElement(),t=null!=e?function(e){let t=getComputedStyle(e);const n="absolute"===t.position,o=/(auto|scroll)/;if("fixed"===t.position)return document.body;for(let l=e;l=l.parentElement;)if(t=getComputedStyle(l),(!n||"static"!==t.position)&&o.test(t.overflow+t.overflowY+t.overflowX))return l;return document.body}(e):document.body;let n=!1,u=c(o,t);const s=function(){n||(window.requestAnimationFrame(function(){l(),n=!1}),n=!0);const e=c(o,t);e!==u&&(u=e,null!=r&&r(e))},a=new ResizeObserver(l);return window.addEventListener("resize",l),document.addEventListener("scroll",s,{capture:!0,passive:!0}),a.observe(o),()=>{a.unobserve(o),window.removeEventListener("resize",l),document.removeEventListener("scroll",s,!0)}}},[o,i,r,l,t])}const d=t.createCommand("SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND");function m({close:e,editor:l,anchorElementRef:r,resolution:i,options:c,menuRenderFn:a,onSelectOption:m,shouldSplitNodeWithQuery:f=!1,commandPriority:p=t.COMMAND_PRIORITY_LOW,preselectFirstItem:g=!0}){const[h,C]=n.useState(null),E=null!==h?Math.min(c.length-1,h):null,y=i.match&&i.match.matchingString;n.useEffect(()=>{g&&C(0)},[y,g]);const b=n.useCallback(n=>{l.update(()=>{const o=null!=i.match&&f?function(e){const n=t.$getSelection();if(!t.$isRangeSelection(n)||!n.isCollapsed())return null;const o=n.anchor;if("text"!==o.type)return null;const l=o.getNode();if(!l.isSimpleText())return null;const r=o.offset,i=l.getTextContent().slice(0,r),u=e.replaceableString.length,s=r-function(e,t,n){let o=n;for(let n=o;n<=t.length;n++)e.slice(-n)===t.substring(0,n)&&(o=n);return o}(i,e.matchingString,u);if(s<0)return null;let c;return 0===s?[c]=l.splitText(r):[,c]=l.splitText(s,r),c}(i.match):null;m(n,o,e,i.match?i.match.matchingString:"")})},[l,f,i.match,m,e]),R=n.useCallback(e=>{const t=l.getRootElement();null!==t&&(t.setAttribute("aria-activedescendant","typeahead-item-"+e),C(e))},[l]);n.useEffect(()=>()=>{const e=l.getRootElement();null!==e&&e.removeAttribute("aria-activedescendant")},[l]),u(()=>{null===c?C(null):null===E&&g&&R(0)},[c,E,R,g]),n.useEffect(()=>o.mergeRegister(l.registerCommand(d,({option:e})=>!(!e.ref||null==e.ref.current)&&(s(e.ref.current),!0),p)),[l,R,p]),n.useEffect(()=>o.mergeRegister(l.registerCommand(t.KEY_ARROW_DOWN_COMMAND,e=>{const t=e;if(null!==c&&c.length){const e=null===E?0:E!==c.length-1?E+1:0;R(e);const n=c[e];if(!n)return R(-1),t.preventDefault(),t.stopImmediatePropagation(),!0;n.ref&&n.ref.current&&l.dispatchCommand(d,{index:e,option:n}),t.preventDefault(),t.stopImmediatePropagation()}return!0},p),l.registerCommand(t.KEY_ARROW_UP_COMMAND,e=>{const t=e;if(null!==c&&c.length){const e=null===E?c.length-1:0!==E?E-1:c.length-1;R(e);const n=c[e];if(!n)return R(-1),t.preventDefault(),t.stopImmediatePropagation(),!0;n.ref&&n.ref.current&&s(n.ref.current),t.preventDefault(),t.stopImmediatePropagation()}return!0},p),l.registerCommand(t.KEY_ESCAPE_COMMAND,t=>{const n=t;return n.preventDefault(),n.stopImmediatePropagation(),e(),!0},p),l.registerCommand(t.KEY_TAB_COMMAND,e=>{const t=e;return null!==c&&null!==E&&null!=c[E]&&(t.preventDefault(),t.stopImmediatePropagation(),b(c[E]),!0)},p),l.registerCommand(t.KEY_ENTER_COMMAND,e=>null!==c&&null!==E&&null!=c[E]&&(null!==e&&(e.preventDefault(),e.stopImmediatePropagation()),b(c[E]),!0),p)),[b,e,l,c,E,R,p]);return a(r,n.useMemo(()=>({options:c,selectOptionAndCleanUp:b,selectedIndex:E,setHighlightedIndex:C}),[b,E,c]),i.match?i.match.matchingString:"")}function f(e,t){null!=t&&(e.className=t),e.setAttribute("aria-label","Typeahead menu"),e.setAttribute("role","listbox"),e.style.display="block",e.style.position="absolute"}exports.LexicalNodeMenuPlugin=function({options:o,nodeKey:u,onClose:s,onOpen:c,onSelectOption:d,menuRenderFn:p,anchorClassName:g,commandPriority:h=t.COMMAND_PRIORITY_LOW,parent:C}){const[E]=e.useLexicalComposerContext(),[y,b]=n.useState(null),R=function(t,o,l,r=(i?document.body:void 0),u=!0){const[s]=e.useLexicalComposerContext(),c=i?document.createElement("div"):null,d=n.useRef(c),m=n.useCallback(()=>{if(null===d.current||void 0===r)return;d.current.style.top=d.current.style.bottom;const e=s.getRootElement(),n=d.current,o=n.firstChild;if(null!==e&&null!==t){const{left:i,top:s,width:c,height:a}=t.getRect(),m=d.current.offsetHeight;if(n.style.top=`${s+m+3+(u?window.pageYOffset:0)}px`,n.style.left=`${i+window.pageXOffset}px`,n.style.height=`${a}px`,n.style.width=`${c}px`,null!==o){o.style.top=`${s}`;const t=o.getBoundingClientRect(),l=t.height,r=t.width,c=e.getBoundingClientRect();i+r>c.right&&(n.style.left=`${c.right-r+window.pageXOffset}px`),(s+l>window.innerHeight||s+l>c.bottom)&&s-c.top>l+a&&(n.style.top=`${s-l-a+(u?window.pageYOffset:0)}px`)}n.isConnected||(f(n,l),r.append(n)),n.setAttribute("id","typeahead-menu"),e.setAttribute("aria-controls","typeahead-menu")}},[s,t,u,l,r]);n.useEffect(()=>{const e=s.getRootElement();return null!==t&&m(),()=>{null!==e&&e.removeAttribute("aria-controls");const t=d.current;null!==t&&t.isConnected&&(t.remove(),t.removeAttribute("id"))}},[s,m,t]);const p=n.useCallback(e=>{null!==t&&(e||o(null))},[t,o]);return a(t,d.current,m,p),null!=c&&c===d.current&&(f(c,l),null!=r&&r.append(c)),d}(y,b,g,C),w=n.useCallback(()=>{b(null),null!=s&&null!==y&&s()},[s,y]),v=n.useCallback(e=>{b(e),null!=c&&null===y&&c(e)},[c,y]),x=n.useCallback(()=>{u?E.update(()=>{const e=t.$getNodeByKey(u),o=E.getElementByKey(u);var l;null!=e&&null!=o&&null==y&&(l=()=>v({getRect:()=>o.getBoundingClientRect()}),r in n?n[r](l):l())}):null==u&&null!=y&&w()},[w,E,u,v,y]);return n.useEffect(()=>{x()},[x,u]),n.useEffect(()=>{if(null!=u)return E.registerUpdateListener(({dirtyElements:e})=>{e.get(u)&&x()})},[E,x,u]),null===R.current||null===y||null===E?null:l.jsx(m,{close:w,resolution:y,editor:E,anchorElementRef:R,options:o,menuRenderFn:p,onSelectOption:d,commandPriority:h})},exports.MenuOption=class{key;ref;constructor(e){this.key=e,this.ref={current:null},this.setRefElement=this.setRefElement.bind(this)}setRefElement(e){this.ref={current:e}}};
@@ -6,4 +6,4 @@
6
6
  *
7
7
  */
8
8
 
9
- import{useLexicalComposerContext as e}from"@lexical/react/LexicalComposerContext";import{createCommand as t,COMMAND_PRIORITY_LOW as n,KEY_ARROW_DOWN_COMMAND as o,KEY_ARROW_UP_COMMAND as l,KEY_ESCAPE_COMMAND as r,KEY_TAB_COMMAND as i,KEY_ENTER_COMMAND as u,$getSelection as s,$isRangeSelection as c,$getNodeByKey as a}from"lexical";import m,{useLayoutEffect as d,useEffect as p,useRef as f,useCallback as g,useState as h}from"react";import{mergeRegister as y}from"@lexical/utils";import v from"react-dom";import{jsx as w,jsxs as b}from"react/jsx-runtime";const C="startTransition";const E="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement,x=E?d:p;class R{key;ref;icon;title;constructor(e){this.key=e,this.ref={current:null},this.setRefElement=this.setRefElement.bind(this)}setRefElement(e){this.ref={current:e}}}const I=e=>{const t=document.getElementById("typeahead-menu");if(!t)return;const n=t.getBoundingClientRect();n.top+n.height>window.innerHeight&&t.scrollIntoView({block:"center"}),n.top<0&&t.scrollIntoView({block:"center"}),e.scrollIntoView({block:"nearest"})};function O(e,t){const n=e.getBoundingClientRect(),o=t.getBoundingClientRect();return n.top>=o.top-6&&n.top<=o.bottom+6}function S(t,n,o,l){const[r]=e();p(()=>{if(null!=n&&null!=t){const e=r.getRootElement(),t=null!=e?function(e){let t=getComputedStyle(e);const n="absolute"===t.position,o=/(auto|scroll)/;if("fixed"===t.position)return document.body;for(let l=e;l=l.parentElement;)if(t=getComputedStyle(l),(!n||"static"!==t.position)&&o.test(t.overflow+t.overflowY+t.overflowX))return l;return document.body}(e):document.body;let i=!1,u=O(n,t);const s=function(){i||(window.requestAnimationFrame(function(){o(),i=!1}),i=!0);const e=O(n,t);e!==u&&(u=e,null!=l&&l(e))},c=new ResizeObserver(o);return window.addEventListener("resize",o),document.addEventListener("scroll",s,{capture:!0,passive:!0}),c.observe(n),()=>{c.unobserve(n),window.removeEventListener("resize",o),document.removeEventListener("scroll",s,!0)}}},[n,r,l,o,t])}const P=t("SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND");function A({index:e,isSelected:t,onClick:n,onMouseEnter:o,option:l}){let r="item";return t&&(r+=" selected"),b("li",{tabIndex:-1,className:r,ref:l.setRefElement,role:"option","aria-selected":t,id:"typeahead-item-"+e,onMouseEnter:o,onClick:n,children:[l.icon,w("span",{className:"text",children:l.title})]},l.key)}function k({close:e,editor:t,anchorElementRef:a,resolution:m,options:d,onSelectOption:f,shouldSplitNodeWithQuery:b=!1,commandPriority:C=n,preselectFirstItem:E=!0}){const[R,O]=h(null),S=null!==R?Math.min(d.length-1,R):null,k=m.match&&m.match.matchingString;p(()=>{E&&O(0)},[k,E]);const N=g(n=>{t.update(()=>{const t=null!=m.match&&b?function(e){const t=s();if(!c(t)||!t.isCollapsed())return null;const n=t.anchor;if("text"!==n.type)return null;const o=n.getNode();if(!o.isSimpleText())return null;const l=n.offset,r=o.getTextContent().slice(0,l),i=e.replaceableString.length,u=l-function(e,t,n){let o=n;for(let n=o;n<=t.length;n++)e.slice(-n)===t.substring(0,n)&&(o=n);return o}(r,e.matchingString,i);if(u<0)return null;let a;return 0===u?[a]=o.splitText(l):[,a]=o.splitText(u,l),a}(m.match):null;f(n,t,e,m.match?m.match.matchingString:"")})},[t,b,m.match,f,e]),D=g(e=>{const n=t.getRootElement();null!==n&&(n.setAttribute("aria-activedescendant","typeahead-item-"+e),O(e))},[t]),T=g(()=>a.current&&d.length?v.createPortal(w("div",{className:"typeahead-popover mentions-menu",children:w("ul",{children:d.map((e,t)=>w(A,{index:t,isSelected:S===t,onClick:()=>{O(t),N(e)},onMouseEnter:()=>{O(t)},option:e},e.key))})}),a.current):null,[a,d,S,N,O]);return p(()=>()=>{const e=t.getRootElement();null!==e&&e.removeAttribute("aria-activedescendant")},[t]),x(()=>{null===d?O(null):null===S&&E&&D(0)},[d,S,D,E]),p(()=>y(t.registerCommand(P,({option:e})=>!(!e.ref||null==e.ref.current)&&(I(e.ref.current),!0),C)),[t,D,C]),p(()=>y(t.registerCommand(o,e=>{const n=e;if(null!==d&&d.length){const e=null===S?0:S!==d.length-1?S+1:0;D(e);const o=d[e];if(!o)return D(-1),n.preventDefault(),n.stopImmediatePropagation(),!0;o.ref&&o.ref.current&&t.dispatchCommand(P,{index:e,option:o}),n.preventDefault(),n.stopImmediatePropagation()}return!0},C),t.registerCommand(l,e=>{const t=e;if(null!==d&&d.length){const e=null===S?d.length-1:0!==S?S-1:d.length-1;D(e);const n=d[e];if(!n)return D(-1),t.preventDefault(),t.stopImmediatePropagation(),!0;n.ref&&n.ref.current&&I(n.ref.current),t.preventDefault(),t.stopImmediatePropagation()}return!0},C),t.registerCommand(r,t=>{const n=t;return n.preventDefault(),n.stopImmediatePropagation(),e(),!0},C),t.registerCommand(i,e=>{const t=e;return null!==d&&null!==S&&null!=d[S]&&(t.preventDefault(),t.stopImmediatePropagation(),N(d[S]),!0)},C),t.registerCommand(u,e=>null!==d&&null!==S&&null!=d[S]&&(null!==e&&(e.preventDefault(),e.stopImmediatePropagation()),N(d[S]),!0),C)),[N,e,t,d,S,D,C]),T()}function N(e,t){null!=t&&(e.className=t),e.setAttribute("aria-label","Typeahead menu"),e.setAttribute("role","listbox"),e.style.display="block",e.style.position="absolute"}function D({options:t,nodeKey:o,onClose:l,onOpen:r,onSelectOption:i,anchorClassName:u,commandPriority:s=n,parent:c}){const[d]=e(),[y,v]=h(null),b=function(t,n,o,l=(E?document.body:void 0),r=!0){const[i]=e(),u=E?document.createElement("div"):null,s=f(u),c=g(()=>{if(null===s.current||void 0===l)return;s.current.style.top=s.current.style.bottom;const e=i.getRootElement(),n=s.current,u=n.firstChild;if(null!==e&&null!==t){const{left:i,top:c,width:a,height:m}=t.getRect(),d=s.current.offsetHeight;if(n.style.top=`${c+d+3+(r?window.pageYOffset:0)}px`,n.style.left=`${i+window.pageXOffset}px`,n.style.height=`${m}px`,n.style.width=`${a}px`,null!==u){u.style.top=`${c}`;const t=u.getBoundingClientRect(),o=t.height,l=t.width,s=e.getBoundingClientRect();i+l>s.right&&(n.style.left=`${s.right-l+window.pageXOffset}px`),(c+o>window.innerHeight||c+o>s.bottom)&&c-s.top>o+m&&(n.style.top=`${c-o-m+(r?window.pageYOffset:0)}px`)}n.isConnected||(N(n,o),l.append(n)),n.setAttribute("id","typeahead-menu"),e.setAttribute("aria-controls","typeahead-menu")}},[i,t,r,o,l]);p(()=>{const e=i.getRootElement();return null!==t&&c(),()=>{null!==e&&e.removeAttribute("aria-controls");const t=s.current;null!==t&&t.isConnected&&(t.remove(),t.removeAttribute("id"))}},[i,c,t]);const a=g(e=>{null!==t&&(e||n(null))},[t,n]);return S(t,s.current,c,a),null!=u&&u===s.current&&(N(u,o),null!=l&&l.append(u)),s}(y,v,u,c),x=g(()=>{v(null),null!=l&&null!==y&&l()},[l,y]),R=g(e=>{v(e),null!=r&&null===y&&r(e)},[r,y]),I=g(()=>{o?d.update(()=>{const e=a(o),t=d.getElementByKey(o);var n;null!=e&&null!=t&&null==y&&(n=()=>R({getRect:()=>t.getBoundingClientRect()}),C in m?m[C](n):n())}):null==o&&null!=y&&x()},[x,d,o,R,y]);return p(()=>{I()},[I,o]),p(()=>{if(null!=o)return d.registerUpdateListener(({dirtyElements:e})=>{e.get(o)&&I()})},[d,I,o]),null===b.current||null===y||null===d?null:w(k,{close:x,resolution:y,editor:d,anchorElementRef:b,options:t,onSelectOption:i,commandPriority:s})}export{D as LexicalNodeMenuPlugin,R as MenuOption};
9
+ import{useLexicalComposerContext as t}from"@lexical/react/LexicalComposerContext";import{createCommand as e,COMMAND_PRIORITY_LOW as n,KEY_ARROW_DOWN_COMMAND as o,KEY_ARROW_UP_COMMAND as l,KEY_ESCAPE_COMMAND as r,KEY_TAB_COMMAND as i,KEY_ENTER_COMMAND as u,$getSelection as s,$isRangeSelection as c,$getNodeByKey as a}from"lexical";import m,{useLayoutEffect as d,useEffect as p,useRef as f,useCallback as g,useState as h,useMemo as y}from"react";import{mergeRegister as w}from"@lexical/utils";import{jsx as v}from"react/jsx-runtime";const b="startTransition";const C="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement,x=C?d:p;class E{key;ref;constructor(t){this.key=t,this.ref={current:null},this.setRefElement=this.setRefElement.bind(this)}setRefElement(t){this.ref={current:t}}}const R=t=>{const e=document.getElementById("typeahead-menu");if(!e)return;const n=e.getBoundingClientRect();n.top+n.height>window.innerHeight&&e.scrollIntoView({block:"center"}),n.top<0&&e.scrollIntoView({block:"center"}),t.scrollIntoView({block:"nearest"})};function I(t,e){const n=t.getBoundingClientRect(),o=e.getBoundingClientRect();return n.top>=o.top-6&&n.top<=o.bottom+6}function O(e,n,o,l){const[r]=t();p(()=>{if(null!=n&&null!=e){const t=r.getRootElement(),e=null!=t?function(t){let e=getComputedStyle(t);const n="absolute"===e.position,o=/(auto|scroll)/;if("fixed"===e.position)return document.body;for(let l=t;l=l.parentElement;)if(e=getComputedStyle(l),(!n||"static"!==e.position)&&o.test(e.overflow+e.overflowY+e.overflowX))return l;return document.body}(t):document.body;let i=!1,u=I(n,e);const s=function(){i||(window.requestAnimationFrame(function(){o(),i=!1}),i=!0);const t=I(n,e);t!==u&&(u=t,null!=l&&l(t))},c=new ResizeObserver(o);return window.addEventListener("resize",o),document.addEventListener("scroll",s,{capture:!0,passive:!0}),c.observe(n),()=>{c.unobserve(n),window.removeEventListener("resize",o),document.removeEventListener("scroll",s,!0)}}},[n,r,l,o,e])}const A=e("SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND");function S({close:t,editor:e,anchorElementRef:a,resolution:m,options:d,menuRenderFn:f,onSelectOption:v,shouldSplitNodeWithQuery:b=!1,commandPriority:C=n,preselectFirstItem:E=!0}){const[I,O]=h(null),S=null!==I?Math.min(d.length-1,I):null,P=m.match&&m.match.matchingString;p(()=>{E&&O(0)},[P,E]);const D=g(n=>{e.update(()=>{const e=null!=m.match&&b?function(t){const e=s();if(!c(e)||!e.isCollapsed())return null;const n=e.anchor;if("text"!==n.type)return null;const o=n.getNode();if(!o.isSimpleText())return null;const l=n.offset,r=o.getTextContent().slice(0,l),i=t.replaceableString.length,u=l-function(t,e,n){let o=n;for(let n=o;n<=e.length;n++)t.slice(-n)===e.substring(0,n)&&(o=n);return o}(r,t.matchingString,i);if(u<0)return null;let a;return 0===u?[a]=o.splitText(l):[,a]=o.splitText(u,l),a}(m.match):null;v(n,e,t,m.match?m.match.matchingString:"")})},[e,b,m.match,v,t]),T=g(t=>{const n=e.getRootElement();null!==n&&(n.setAttribute("aria-activedescendant","typeahead-item-"+t),O(t))},[e]);p(()=>()=>{const t=e.getRootElement();null!==t&&t.removeAttribute("aria-activedescendant")},[e]),x(()=>{null===d?O(null):null===S&&E&&T(0)},[d,S,T,E]),p(()=>w(e.registerCommand(A,({option:t})=>!(!t.ref||null==t.ref.current)&&(R(t.ref.current),!0),C)),[e,T,C]),p(()=>w(e.registerCommand(o,t=>{const n=t;if(null!==d&&d.length){const t=null===S?0:S!==d.length-1?S+1:0;T(t);const o=d[t];if(!o)return T(-1),n.preventDefault(),n.stopImmediatePropagation(),!0;o.ref&&o.ref.current&&e.dispatchCommand(A,{index:t,option:o}),n.preventDefault(),n.stopImmediatePropagation()}return!0},C),e.registerCommand(l,t=>{const e=t;if(null!==d&&d.length){const t=null===S?d.length-1:0!==S?S-1:d.length-1;T(t);const n=d[t];if(!n)return T(-1),e.preventDefault(),e.stopImmediatePropagation(),!0;n.ref&&n.ref.current&&R(n.ref.current),e.preventDefault(),e.stopImmediatePropagation()}return!0},C),e.registerCommand(r,e=>{const n=e;return n.preventDefault(),n.stopImmediatePropagation(),t(),!0},C),e.registerCommand(i,t=>{const e=t;return null!==d&&null!==S&&null!=d[S]&&(e.preventDefault(),e.stopImmediatePropagation(),D(d[S]),!0)},C),e.registerCommand(u,t=>null!==d&&null!==S&&null!=d[S]&&(null!==t&&(t.preventDefault(),t.stopImmediatePropagation()),D(d[S]),!0),C)),[D,t,e,d,S,T,C]);return f(a,y(()=>({options:d,selectOptionAndCleanUp:D,selectedIndex:S,setHighlightedIndex:O}),[D,S,d]),m.match?m.match.matchingString:"")}function P(t,e){null!=e&&(t.className=e),t.setAttribute("aria-label","Typeahead menu"),t.setAttribute("role","listbox"),t.style.display="block",t.style.position="absolute"}function D({options:e,nodeKey:o,onClose:l,onOpen:r,onSelectOption:i,menuRenderFn:u,anchorClassName:s,commandPriority:c=n,parent:d}){const[y]=t(),[w,x]=h(null),E=function(e,n,o,l=(C?document.body:void 0),r=!0){const[i]=t(),u=C?document.createElement("div"):null,s=f(u),c=g(()=>{if(null===s.current||void 0===l)return;s.current.style.top=s.current.style.bottom;const t=i.getRootElement(),n=s.current,u=n.firstChild;if(null!==t&&null!==e){const{left:i,top:c,width:a,height:m}=e.getRect(),d=s.current.offsetHeight;if(n.style.top=`${c+d+3+(r?window.pageYOffset:0)}px`,n.style.left=`${i+window.pageXOffset}px`,n.style.height=`${m}px`,n.style.width=`${a}px`,null!==u){u.style.top=`${c}`;const e=u.getBoundingClientRect(),o=e.height,l=e.width,s=t.getBoundingClientRect();i+l>s.right&&(n.style.left=`${s.right-l+window.pageXOffset}px`),(c+o>window.innerHeight||c+o>s.bottom)&&c-s.top>o+m&&(n.style.top=`${c-o-m+(r?window.pageYOffset:0)}px`)}n.isConnected||(P(n,o),l.append(n)),n.setAttribute("id","typeahead-menu"),t.setAttribute("aria-controls","typeahead-menu")}},[i,e,r,o,l]);p(()=>{const t=i.getRootElement();return null!==e&&c(),()=>{null!==t&&t.removeAttribute("aria-controls");const e=s.current;null!==e&&e.isConnected&&(e.remove(),e.removeAttribute("id"))}},[i,c,e]);const a=g(t=>{null!==e&&(t||n(null))},[e,n]);return O(e,s.current,c,a),null!=u&&u===s.current&&(P(u,o),null!=l&&l.append(u)),s}(w,x,s,d),R=g(()=>{x(null),null!=l&&null!==w&&l()},[l,w]),I=g(t=>{x(t),null!=r&&null===w&&r(t)},[r,w]),A=g(()=>{o?y.update(()=>{const t=a(o),e=y.getElementByKey(o);var n;null!=t&&null!=e&&null==w&&(n=()=>I({getRect:()=>e.getBoundingClientRect()}),b in m?m[b](n):n())}):null==o&&null!=w&&R()},[R,y,o,I,w]);return p(()=>{A()},[A,o]),p(()=>{if(null!=o)return y.registerUpdateListener(({dirtyElements:t})=>{t.get(o)&&A()})},[y,A,o]),null===E.current||null===w||null===y?null:v(S,{close:R,resolution:w,editor:y,anchorElementRef:E,options:e,menuRenderFn:u,onSelectOption:i,commandPriority:c})}export{D as LexicalNodeMenuPlugin,E as MenuOption};
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  */
8
- import type { MenuResolution, MenuTextMatch, TriggerFn } from './shared/LexicalMenu';
8
+ import type { MenuRenderFn, MenuResolution, MenuTextMatch, TriggerFn } from './shared/LexicalMenu';
9
9
  import type { JSX } from 'react';
10
10
  import { CommandListenerPriority, LexicalCommand, TextNode } from 'lexical';
11
11
  import { MenuOption } from './shared/LexicalMenu';
@@ -26,6 +26,7 @@ export type TypeaheadMenuPluginProps<TOption extends MenuOption> = {
26
26
  onQueryChange: (matchingString: string | null) => void;
27
27
  onSelectOption: (option: TOption, textNodeContainingQuery: TextNode | null, closeMenu: () => void, matchingString: string) => void;
28
28
  options: Array<TOption>;
29
+ menuRenderFn: MenuRenderFn<TOption>;
29
30
  triggerFn: TriggerFn;
30
31
  onOpen?: (resolution: MenuResolution) => void;
31
32
  onClose?: () => void;
@@ -35,5 +36,5 @@ export type TypeaheadMenuPluginProps<TOption extends MenuOption> = {
35
36
  preselectFirstItem?: boolean;
36
37
  ignoreEntityBoundary?: boolean;
37
38
  };
38
- export declare function LexicalTypeaheadMenuPlugin<TOption extends MenuOption>({ options, onQueryChange, onSelectOption, onOpen, onClose, triggerFn, anchorClassName, commandPriority, parent, preselectFirstItem, ignoreEntityBoundary, }: TypeaheadMenuPluginProps<TOption>): JSX.Element | null;
39
- export { MenuOption, MenuResolution, MenuTextMatch, TriggerFn };
39
+ export declare function LexicalTypeaheadMenuPlugin<TOption extends MenuOption>({ options, onQueryChange, onSelectOption, onOpen, onClose, menuRenderFn, triggerFn, anchorClassName, commandPriority, parent, preselectFirstItem, ignoreEntityBoundary, }: TypeaheadMenuPluginProps<TOption>): JSX.Element | null;
40
+ export { MenuOption, MenuRenderFn, MenuResolution, MenuTextMatch, TriggerFn };
@@ -12,7 +12,6 @@ var LexicalComposerContext = require('@lexical/react/LexicalComposerContext');
12
12
  var lexical = require('lexical');
13
13
  var React = require('react');
14
14
  var utils = require('@lexical/utils');
15
- var ReactDOM = require('react-dom');
16
15
  var jsxRuntime = require('react/jsx-runtime');
17
16
 
18
17
  /**
@@ -71,8 +70,6 @@ const useLayoutEffectImpl = CAN_USE_DOM ? React.useLayoutEffect : React.useEffec
71
70
  class MenuOption {
72
71
  key;
73
72
  ref;
74
- icon;
75
- title;
76
73
  constructor(key) {
77
74
  this.key = key;
78
75
  this.ref = {
@@ -222,38 +219,13 @@ function useDynamicPositioning(resolution, targetElement, onReposition, onVisibi
222
219
  }, [targetElement, editor, onVisibilityChange, onReposition, resolution]);
223
220
  }
224
221
  const SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND$1 = lexical.createCommand('SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND');
225
- function MenuItem({
226
- index,
227
- isSelected,
228
- onClick,
229
- onMouseEnter,
230
- option
231
- }) {
232
- let className = 'item';
233
- if (isSelected) {
234
- className += ' selected';
235
- }
236
- return /*#__PURE__*/jsxRuntime.jsxs("li", {
237
- tabIndex: -1,
238
- className: className,
239
- ref: option.setRefElement,
240
- role: "option",
241
- "aria-selected": isSelected,
242
- id: 'typeahead-item-' + index,
243
- onMouseEnter: onMouseEnter,
244
- onClick: onClick,
245
- children: [option.icon, /*#__PURE__*/jsxRuntime.jsx("span", {
246
- className: "text",
247
- children: option.title
248
- })]
249
- }, option.key);
250
- }
251
222
  function LexicalMenu({
252
223
  close,
253
224
  editor,
254
225
  anchorElementRef,
255
226
  resolution,
256
227
  options,
228
+ menuRenderFn,
257
229
  onSelectOption,
258
230
  shouldSplitNodeWithQuery = false,
259
231
  commandPriority = lexical.COMMAND_PRIORITY_LOW,
@@ -281,25 +253,6 @@ function LexicalMenu({
281
253
  setHighlightedIndex(index);
282
254
  }
283
255
  }, [editor]);
284
- const menuRenderFn = React.useCallback(() => {
285
- return anchorElementRef.current && options.length ? /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/jsxRuntime.jsx("div", {
286
- className: "typeahead-popover mentions-menu",
287
- children: /*#__PURE__*/jsxRuntime.jsx("ul", {
288
- children: options.map((option, i) => /*#__PURE__*/jsxRuntime.jsx(MenuItem, {
289
- index: i,
290
- isSelected: selectedIndex === i,
291
- onClick: () => {
292
- setHighlightedIndex(i);
293
- selectOptionAndCleanUp(option);
294
- },
295
- onMouseEnter: () => {
296
- setHighlightedIndex(i);
297
- },
298
- option: option
299
- }, option.key))
300
- })
301
- }), anchorElementRef.current) : null;
302
- }, [anchorElementRef, options, selectedIndex, selectOptionAndCleanUp, setHighlightedIndex]);
303
256
  React.useEffect(() => {
304
257
  return () => {
305
258
  const rootElem = editor.getRootElement();
@@ -395,7 +348,13 @@ function LexicalMenu({
395
348
  return true;
396
349
  }, commandPriority));
397
350
  }, [selectOptionAndCleanUp, close, editor, options, selectedIndex, updateSelectedIndex, commandPriority]);
398
- return menuRenderFn();
351
+ const listItemProps = React.useMemo(() => ({
352
+ options,
353
+ selectOptionAndCleanUp,
354
+ selectedIndex,
355
+ setHighlightedIndex
356
+ }), [selectOptionAndCleanUp, selectedIndex, options]);
357
+ return menuRenderFn(anchorElementRef, listItemProps, resolution.match ? resolution.match.matchingString : '');
399
358
  }
400
359
  function setContainerDivAttributes(containerDiv, className) {
401
360
  if (className != null) {
@@ -605,6 +564,7 @@ function LexicalTypeaheadMenuPlugin({
605
564
  onSelectOption,
606
565
  onOpen,
607
566
  onClose,
567
+ menuRenderFn,
608
568
  triggerFn,
609
569
  anchorClassName,
610
570
  commandPriority = lexical.COMMAND_PRIORITY_LOW,
@@ -677,6 +637,7 @@ function LexicalTypeaheadMenuPlugin({
677
637
  editor: editor,
678
638
  anchorElementRef: anchorElementRef,
679
639
  options: options,
640
+ menuRenderFn: menuRenderFn,
680
641
  shouldSplitNodeWithQuery: true,
681
642
  onSelectOption: onSelectOption,
682
643
  commandPriority: commandPriority,
@@ -8,10 +8,9 @@
8
8
 
9
9
  import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
10
10
  import { createCommand, COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ESCAPE_COMMAND, KEY_TAB_COMMAND, KEY_ENTER_COMMAND, $getSelection, $isRangeSelection, $isTextNode, getDOMSelection } from 'lexical';
11
- import React, { useLayoutEffect, useEffect, useRef, useCallback, useState } from 'react';
11
+ import React, { useLayoutEffect, useEffect, useRef, useCallback, useState, useMemo } from 'react';
12
12
  import { mergeRegister } from '@lexical/utils';
13
- import ReactDOM from 'react-dom';
14
- import { jsx, jsxs } from 'react/jsx-runtime';
13
+ import { jsx } from 'react/jsx-runtime';
15
14
 
16
15
  /**
17
16
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -69,8 +68,6 @@ const useLayoutEffectImpl = CAN_USE_DOM ? useLayoutEffect : useEffect;
69
68
  class MenuOption {
70
69
  key;
71
70
  ref;
72
- icon;
73
- title;
74
71
  constructor(key) {
75
72
  this.key = key;
76
73
  this.ref = {
@@ -220,38 +217,13 @@ function useDynamicPositioning(resolution, targetElement, onReposition, onVisibi
220
217
  }, [targetElement, editor, onVisibilityChange, onReposition, resolution]);
221
218
  }
222
219
  const SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND$1 = createCommand('SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND');
223
- function MenuItem({
224
- index,
225
- isSelected,
226
- onClick,
227
- onMouseEnter,
228
- option
229
- }) {
230
- let className = 'item';
231
- if (isSelected) {
232
- className += ' selected';
233
- }
234
- return /*#__PURE__*/jsxs("li", {
235
- tabIndex: -1,
236
- className: className,
237
- ref: option.setRefElement,
238
- role: "option",
239
- "aria-selected": isSelected,
240
- id: 'typeahead-item-' + index,
241
- onMouseEnter: onMouseEnter,
242
- onClick: onClick,
243
- children: [option.icon, /*#__PURE__*/jsx("span", {
244
- className: "text",
245
- children: option.title
246
- })]
247
- }, option.key);
248
- }
249
220
  function LexicalMenu({
250
221
  close,
251
222
  editor,
252
223
  anchorElementRef,
253
224
  resolution,
254
225
  options,
226
+ menuRenderFn,
255
227
  onSelectOption,
256
228
  shouldSplitNodeWithQuery = false,
257
229
  commandPriority = COMMAND_PRIORITY_LOW,
@@ -279,25 +251,6 @@ function LexicalMenu({
279
251
  setHighlightedIndex(index);
280
252
  }
281
253
  }, [editor]);
282
- const menuRenderFn = useCallback(() => {
283
- return anchorElementRef.current && options.length ? /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/jsx("div", {
284
- className: "typeahead-popover mentions-menu",
285
- children: /*#__PURE__*/jsx("ul", {
286
- children: options.map((option, i) => /*#__PURE__*/jsx(MenuItem, {
287
- index: i,
288
- isSelected: selectedIndex === i,
289
- onClick: () => {
290
- setHighlightedIndex(i);
291
- selectOptionAndCleanUp(option);
292
- },
293
- onMouseEnter: () => {
294
- setHighlightedIndex(i);
295
- },
296
- option: option
297
- }, option.key))
298
- })
299
- }), anchorElementRef.current) : null;
300
- }, [anchorElementRef, options, selectedIndex, selectOptionAndCleanUp, setHighlightedIndex]);
301
254
  useEffect(() => {
302
255
  return () => {
303
256
  const rootElem = editor.getRootElement();
@@ -393,7 +346,13 @@ function LexicalMenu({
393
346
  return true;
394
347
  }, commandPriority));
395
348
  }, [selectOptionAndCleanUp, close, editor, options, selectedIndex, updateSelectedIndex, commandPriority]);
396
- return menuRenderFn();
349
+ const listItemProps = useMemo(() => ({
350
+ options,
351
+ selectOptionAndCleanUp,
352
+ selectedIndex,
353
+ setHighlightedIndex
354
+ }), [selectOptionAndCleanUp, selectedIndex, options]);
355
+ return menuRenderFn(anchorElementRef, listItemProps, resolution.match ? resolution.match.matchingString : '');
397
356
  }
398
357
  function setContainerDivAttributes(containerDiv, className) {
399
358
  if (className != null) {
@@ -603,6 +562,7 @@ function LexicalTypeaheadMenuPlugin({
603
562
  onSelectOption,
604
563
  onOpen,
605
564
  onClose,
565
+ menuRenderFn,
606
566
  triggerFn,
607
567
  anchorClassName,
608
568
  commandPriority = COMMAND_PRIORITY_LOW,
@@ -675,6 +635,7 @@ function LexicalTypeaheadMenuPlugin({
675
635
  editor: editor,
676
636
  anchorElementRef: anchorElementRef,
677
637
  options: options,
638
+ menuRenderFn: menuRenderFn,
678
639
  shouldSplitNodeWithQuery: true,
679
640
  onSelectOption: onSelectOption,
680
641
  commandPriority: commandPriority,
@@ -36,6 +36,17 @@ declare export var SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND: LexicalCommand<{
36
36
  option: MenuOption,
37
37
  }>;
38
38
 
39
+ export type MenuRenderFn<TOption> = (
40
+ anchorElementRef: {current: HTMLElement | null},
41
+ itemProps: {
42
+ selectedIndex: number | null,
43
+ selectOptionAndCleanUp: (option: TOption) => void,
44
+ setHighlightedIndex: (index: number) => void,
45
+ options: Array<TOption>,
46
+ },
47
+ matchingString: string,
48
+ ) => React.Portal | React.MixedElement | null;
49
+
39
50
  declare export function getScrollParent(
40
51
  element: HTMLElement,
41
52
  includeHidden: boolean,
@@ -55,6 +66,7 @@ export type TypeaheadMenuPluginProps<TOption> = {
55
66
  matchingString: string,
56
67
  ) => void,
57
68
  options: Array<TOption>,
69
+ menuRenderFn: MenuRenderFn<TOption>,
58
70
  triggerFn: TriggerFn,
59
71
  onOpen?: (resolution: MenuResolution) => void,
60
72
  onClose?: () => void,