@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.
- package/LexicalAutoEmbedPlugin.d.ts +3 -2
- package/LexicalAutoEmbedPlugin.dev.js +2 -0
- package/LexicalAutoEmbedPlugin.dev.mjs +2 -0
- package/LexicalAutoEmbedPlugin.js.flow +2 -0
- package/LexicalAutoEmbedPlugin.prod.js +1 -1
- package/LexicalAutoEmbedPlugin.prod.mjs +1 -1
- package/LexicalContextMenuPlugin.d.ts +36 -0
- package/LexicalContextMenuPlugin.dev.js +524 -0
- package/LexicalContextMenuPlugin.dev.mjs +509 -0
- package/LexicalContextMenuPlugin.js +11 -0
- package/LexicalContextMenuPlugin.mjs +13 -0
- package/LexicalContextMenuPlugin.node.mjs +11 -0
- package/LexicalContextMenuPlugin.prod.js +9 -0
- package/LexicalContextMenuPlugin.prod.mjs +9 -0
- package/LexicalNodeMenuPlugin.d.ts +4 -3
- package/LexicalNodeMenuPlugin.dev.js +10 -49
- package/LexicalNodeMenuPlugin.dev.mjs +12 -51
- package/LexicalNodeMenuPlugin.js.flow +12 -0
- package/LexicalNodeMenuPlugin.prod.js +1 -1
- package/LexicalNodeMenuPlugin.prod.mjs +1 -1
- package/LexicalTypeaheadMenuPlugin.d.ts +4 -3
- package/LexicalTypeaheadMenuPlugin.dev.js +10 -49
- package/LexicalTypeaheadMenuPlugin.dev.mjs +12 -51
- package/LexicalTypeaheadMenuPlugin.js.flow +12 -0
- package/LexicalTypeaheadMenuPlugin.prod.js +1 -1
- package/LexicalTypeaheadMenuPlugin.prod.mjs +1 -1
- package/package.json +48 -18
- package/shared/LexicalMenu.d.ts +10 -4
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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"),
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,
|