@lexical/react 0.3.7 → 0.3.8
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/LexicalCollaborationPlugin.dev.js +1 -0
- package/LexicalCollaborationPlugin.prod.js +1 -1
- package/LexicalTableOfContents__EXPERIMENTAL.d.ts +14 -0
- package/LexicalTableOfContents__EXPERIMENTAL.dev.js +144 -0
- package/LexicalTableOfContents__EXPERIMENTAL.js +9 -0
- package/LexicalTableOfContents__EXPERIMENTAL.js.flow +17 -0
- package/LexicalTableOfContents__EXPERIMENTAL.prod.js +10 -0
- package/LexicalTypeaheadMenuPlugin.d.ts +44 -0
- package/LexicalTypeaheadMenuPlugin.dev.js +463 -0
- package/LexicalTypeaheadMenuPlugin.js +9 -0
- package/LexicalTypeaheadMenuPlugin.prod.js +19 -0
- package/package.json +19 -19
|
@@ -102,6 +102,7 @@ function useYjsCollaboration(editor, id, provider, docMap, name, color, shouldBo
|
|
|
102
102
|
provider.off('reload', onProviderDocReload);
|
|
103
103
|
awareness.off('update', onAwarenessUpdate);
|
|
104
104
|
root.getSharedType().unobserveDeep(onYjsTreeChanges);
|
|
105
|
+
docMap.delete(id);
|
|
105
106
|
removeListener();
|
|
106
107
|
};
|
|
107
108
|
}, [binding, color, connect, disconnect, docMap, editor, id, name, provider, shouldBootstrap]);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
'use strict';var e=require("@lexical/react/LexicalComposerContext"),f=require("react"),h=require("@lexical/utils"),x=require("@lexical/yjs"),F=require("lexical"),G=require("react-dom");
|
|
8
8
|
function H(b,c,a,d,l,p,q){let n=f.useRef(!1),[r,v]=f.useState(d.get(c)),g=f.useMemo(()=>x.createBinding(b,a,c,r,d),[b,a,c,d,r]),t=f.useCallback(()=>{a.connect()},[a]),u=f.useCallback(()=>{try{a.disconnect()}catch(m){}},[a]);f.useEffect(()=>{let {root:m}=g,{awareness:y}=a,z=({status:k})=>{b.dispatchCommand(x.CONNECTED_COMMAND,"connected"===k)},A=k=>{q&&k&&m.isEmpty()&&0===m._xmlText._length&&!1===n.current&&I(b);n.current=!1},B=()=>{x.syncCursorPositions(g,a)},C=(k,w)=>{w.origin!==g&&x.syncYjsChangesToLexical(g,
|
|
9
9
|
a,k)};x.initLocalState(a,l,p,document.activeElement===b.getRootElement());let D=k=>{J(b,g);v(k);d.set(c,k);n.current=!0};a.on("reload",D);a.on("status",z);a.on("sync",A);y.on("update",B);m.getSharedType().observeDeep(C);let R=b.registerUpdateListener(({prevEditorState:k,editorState:w,dirtyLeaves:O,dirtyElements:P,normalizedNodes:Q,tags:E})=>{!1===E.has("skip-collab")&&x.syncLexicalUpdateToYjs(g,a,k,w,P,O,Q,E)});t();return()=>{!1===n.current&&u();a.off("sync",A);a.off("status",z);a.off("reload",D);
|
|
10
|
-
y.off("update",B);m.getSharedType().unobserveDeep(C);R()}},[g,p,t,u,d,b,c,l,a,q]);let S=f.useMemo(()=>G.createPortal(f.createElement("div",{ref:m=>{g.cursorsContainer=m}}),document.body),[g]);f.useEffect(()=>b.registerCommand(x.TOGGLE_CONNECT_COMMAND,m=>{void 0!==t&&void 0!==u&&(m?(console.log("Collaboration connected!"),t()):(console.log("Collaboration disconnected!"),u()));return!0},F.COMMAND_PRIORITY_EDITOR),[t,u,b]);return[S,g]}
|
|
10
|
+
y.off("update",B);m.getSharedType().unobserveDeep(C);d.delete(c);R()}},[g,p,t,u,d,b,c,l,a,q]);let S=f.useMemo(()=>G.createPortal(f.createElement("div",{ref:m=>{g.cursorsContainer=m}}),document.body),[g]);f.useEffect(()=>b.registerCommand(x.TOGGLE_CONNECT_COMMAND,m=>{void 0!==t&&void 0!==u&&(m?(console.log("Collaboration connected!"),t()):(console.log("Collaboration disconnected!"),u()));return!0},F.COMMAND_PRIORITY_EDITOR),[t,u,b]);return[S,g]}
|
|
11
11
|
function K(b,c,a,d){f.useEffect(()=>h.mergeRegister(b.registerCommand(F.FOCUS_COMMAND,()=>{x.setLocalStateFocus(c,a,d,!0);return!0},F.COMMAND_PRIORITY_EDITOR),b.registerCommand(F.BLUR_COMMAND,()=>{x.setLocalStateFocus(c,a,d,!1);return!0},F.COMMAND_PRIORITY_EDITOR)),[d,b,a,c])}
|
|
12
12
|
function L(b,c){let a=f.useMemo(()=>x.createUndoManager(c,c.root.getSharedType()),[c]);f.useEffect(()=>h.mergeRegister(b.registerCommand(F.UNDO_COMMAND,()=>{a.undo();return!0},F.COMMAND_PRIORITY_EDITOR),b.registerCommand(F.REDO_COMMAND,()=>{a.redo();return!0},F.COMMAND_PRIORITY_EDITOR)));return f.useCallback(()=>{a.clear()},[a])}
|
|
13
13
|
function I(b){b.update(()=>{var c=F.$getRoot();if(null===c.getFirstChild()){let a=F.$createParagraphNode();c.append(a);c=document.activeElement;(null!==F.$getSelection()||null!==c&&c===b.getRootElement())&&a.select()}},{tag:"history-merge"})}
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
*/
|
|
8
|
+
import type { LexicalEditor, NodeKey } from 'lexical';
|
|
9
|
+
import { HeadingTagType } from '@lexical/rich-text';
|
|
10
|
+
declare type Props = {
|
|
11
|
+
children: (values: Array<[key: NodeKey, text: string, tag: HeadingTagType]>, editor: LexicalEditor) => JSX.Element;
|
|
12
|
+
};
|
|
13
|
+
export default function LexicalTableOfContentsPlugin({ children, }: Props): JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,144 @@
|
|
|
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 richText = require('@lexical/rich-text');
|
|
11
|
+
var lexical = require('lexical');
|
|
12
|
+
var react = require('react');
|
|
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
|
+
|
|
22
|
+
function $insertHeadingIntoTableOfContents(prevHeading, newHeading, currentTableOfContents) {
|
|
23
|
+
if (newHeading === null) {
|
|
24
|
+
return currentTableOfContents;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const newEntry = [newHeading.getKey(), newHeading.getTextContent(), newHeading.getTag()];
|
|
28
|
+
let newTableOfContents = [];
|
|
29
|
+
|
|
30
|
+
if (prevHeading === null) {
|
|
31
|
+
newTableOfContents = [newEntry, ...currentTableOfContents];
|
|
32
|
+
} else {
|
|
33
|
+
for (let i = 0; i < currentTableOfContents.length; i++) {
|
|
34
|
+
const key = currentTableOfContents[i][0];
|
|
35
|
+
newTableOfContents.push(currentTableOfContents[i]);
|
|
36
|
+
|
|
37
|
+
if (key === prevHeading.getKey() && key !== newHeading.getKey()) {
|
|
38
|
+
newTableOfContents.push(newEntry);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return newTableOfContents;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function $deleteHeadingFromTableOfContents(key, currentTableOfContents) {
|
|
47
|
+
const newTableOfContents = [];
|
|
48
|
+
|
|
49
|
+
for (const heading of currentTableOfContents) {
|
|
50
|
+
if (heading[0] !== key) {
|
|
51
|
+
newTableOfContents.push(heading);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return newTableOfContents;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function $updateHeadingInTableOfContents(heading, currentTableOfContents) {
|
|
59
|
+
const newTextContent = heading.getTextContent();
|
|
60
|
+
const newTableOfContents = [];
|
|
61
|
+
|
|
62
|
+
for (const oldHeading of currentTableOfContents) {
|
|
63
|
+
if (oldHeading[0] === heading.getKey()) {
|
|
64
|
+
newTableOfContents.push([heading.getKey(), newTextContent, heading.getTag()]);
|
|
65
|
+
} else {
|
|
66
|
+
newTableOfContents.push(oldHeading);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return newTableOfContents;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function LexicalTableOfContentsPlugin({
|
|
74
|
+
children
|
|
75
|
+
}) {
|
|
76
|
+
const [tableOfContents, setTableOfContents] = react.useState([]);
|
|
77
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
78
|
+
react.useEffect(() => {
|
|
79
|
+
// Set table of contents initial state
|
|
80
|
+
let currentTableOfContents = [];
|
|
81
|
+
editor.getEditorState().read(() => {
|
|
82
|
+
const root = lexical.$getRoot();
|
|
83
|
+
const rootChildren = root.getChildren();
|
|
84
|
+
|
|
85
|
+
for (const child of rootChildren) {
|
|
86
|
+
if (richText.$isHeadingNode(child)) {
|
|
87
|
+
currentTableOfContents.push([child.getKey(), child.getTextContent(), child.getTag()]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setTableOfContents(currentTableOfContents);
|
|
92
|
+
}); // Listen to updates to heading mutations and update state
|
|
93
|
+
|
|
94
|
+
const removeHeaderMutationListener = editor.registerMutationListener(richText.HeadingNode, mutatedNodes => {
|
|
95
|
+
editor.getEditorState().read(() => {
|
|
96
|
+
for (const [nodeKey, mutation] of mutatedNodes) {
|
|
97
|
+
if (mutation === 'created') {
|
|
98
|
+
const newHeading = lexical.$getNodeByKey(nodeKey);
|
|
99
|
+
|
|
100
|
+
if (newHeading !== null) {
|
|
101
|
+
let prevHeading = newHeading.getPreviousSibling();
|
|
102
|
+
|
|
103
|
+
while (prevHeading && !richText.$isHeadingNode(prevHeading)) {
|
|
104
|
+
prevHeading = prevHeading.getPreviousSibling();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
currentTableOfContents = $insertHeadingIntoTableOfContents(prevHeading, newHeading, currentTableOfContents);
|
|
108
|
+
setTableOfContents(currentTableOfContents);
|
|
109
|
+
}
|
|
110
|
+
} else if (mutation === 'destroyed') {
|
|
111
|
+
currentTableOfContents = $deleteHeadingFromTableOfContents(nodeKey, currentTableOfContents);
|
|
112
|
+
setTableOfContents(currentTableOfContents);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}); // Listen to text node mutation updates
|
|
117
|
+
|
|
118
|
+
const removeTextNodeMutationListener = editor.registerMutationListener(lexical.TextNode, mutatedNodes => {
|
|
119
|
+
editor.getEditorState().read(() => {
|
|
120
|
+
for (const [nodeKey, mutation] of mutatedNodes) {
|
|
121
|
+
if (mutation === 'updated') {
|
|
122
|
+
const currNode = lexical.$getNodeByKey(nodeKey);
|
|
123
|
+
|
|
124
|
+
if (currNode !== null) {
|
|
125
|
+
const parentNode = currNode.getParentOrThrow();
|
|
126
|
+
|
|
127
|
+
if (richText.$isHeadingNode(parentNode)) {
|
|
128
|
+
currentTableOfContents = $updateHeadingInTableOfContents(parentNode, currentTableOfContents);
|
|
129
|
+
setTableOfContents(currentTableOfContents);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
return () => {
|
|
137
|
+
removeHeaderMutationListener();
|
|
138
|
+
removeTextNodeMutationListener();
|
|
139
|
+
};
|
|
140
|
+
}, [editor]);
|
|
141
|
+
return children(tableOfContents, editor);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = LexicalTableOfContentsPlugin;
|
|
@@ -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 LexicalTableOfContents__EXPERIMENTAL = process.env.NODE_ENV === 'development' ? require('./LexicalTableOfContents__EXPERIMENTAL.dev.js') : require('./LexicalTableOfContents__EXPERIMENTAL.prod.js')
|
|
9
|
+
module.exports = LexicalTableOfContents__EXPERIMENTAL;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @flow strict
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {HeadingTagType} from '@lexical/rich-text';
|
|
11
|
+
import type {NodeKey} from 'lexical';
|
|
12
|
+
|
|
13
|
+
declare export function LexicalTableOfContentsPlugin(
|
|
14
|
+
children: (
|
|
15
|
+
tableOfContents: Array<[NodeKey, string, HeadingTagType]>,
|
|
16
|
+
) => React$Node,
|
|
17
|
+
): React$Node;
|
|
@@ -0,0 +1,10 @@
|
|
|
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"),l=require("@lexical/rich-text"),r=require("lexical"),u=require("react");
|
|
8
|
+
module.exports=function({children:v}){let [w,m]=u.useState([]),[f]=k.useLexicalComposerContext();u.useEffect(()=>{let b=[];f.getEditorState().read(()=>{let h=r.$getRoot().getChildren();for(let a of h)l.$isHeadingNode(a)&&b.push([a.getKey(),a.getTextContent(),a.getTag()]);m(b)});let x=f.registerMutationListener(l.HeadingNode,h=>{f.getEditorState().read(()=>{for(const [n,p]of h)if("created"===p){var a=r.$getNodeByKey(n);if(null!==a){for(var c=a.getPreviousSibling();c&&!l.$isHeadingNode(c);)c=c.getPreviousSibling();
|
|
9
|
+
a:{var d=b;if(null===a){b=d;break a}let e=[a.getKey(),a.getTextContent(),a.getTag()],g=[];if(null===c)g=[e,...d];else for(let q=0;q<d.length;q++){let t=d[q][0];g.push(d[q]);t===c.getKey()&&t!==a.getKey()&&g.push(e)}b=g}m(b)}}else if("destroyed"===p){c=n;a=b;d=[];for(let e of a)e[0]!==c&&d.push(e);b=d;m(b)}})}),y=f.registerMutationListener(r.TextNode,h=>{f.getEditorState().read(()=>{for(const [d,n]of h)if("updated"===n){var a=r.$getNodeByKey(d);if(null!==a&&(a=a.getParentOrThrow(),l.$isHeadingNode(a))){var c=
|
|
10
|
+
b;let p=a.getTextContent(),e=[];for(let g of c)g[0]===a.getKey()?e.push([a.getKey(),p,a.getTag()]):e.push(g);b=e;m(b)}}})});return()=>{x();y()}},[f]);return v(w,f)}
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
*/
|
|
8
|
+
import { TextNode } from 'lexical';
|
|
9
|
+
import { MutableRefObject, ReactPortal } from 'react';
|
|
10
|
+
export declare type QueryMatch = {
|
|
11
|
+
leadOffset: number;
|
|
12
|
+
matchingString: string;
|
|
13
|
+
replaceableString: string;
|
|
14
|
+
};
|
|
15
|
+
export declare type Resolution = {
|
|
16
|
+
match: QueryMatch;
|
|
17
|
+
range: Range;
|
|
18
|
+
};
|
|
19
|
+
export declare const PUNCTUATION = "\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%'\"~=<>_:;";
|
|
20
|
+
export declare class TypeaheadOption {
|
|
21
|
+
key: string;
|
|
22
|
+
ref?: MutableRefObject<HTMLElement | null>;
|
|
23
|
+
constructor(key: string);
|
|
24
|
+
setRefElement(element: HTMLElement | null): void;
|
|
25
|
+
}
|
|
26
|
+
declare type MenuRenderFn<TOption extends TypeaheadOption> = (anchorElement: HTMLElement | null, itemProps: {
|
|
27
|
+
selectedIndex: number | null;
|
|
28
|
+
selectOptionAndCleanUp: (option: TOption) => void;
|
|
29
|
+
setHighlightedIndex: (index: number) => void;
|
|
30
|
+
}, matchingString: string) => ReactPortal | JSX.Element | null;
|
|
31
|
+
export declare function useBasicTypeaheadTriggerMatch(trigger: string, { minLength, maxLength }: {
|
|
32
|
+
minLength?: number;
|
|
33
|
+
maxLength?: number;
|
|
34
|
+
}): TriggerFn;
|
|
35
|
+
declare type TypeaheadMenuPluginArgs<TOption extends TypeaheadOption> = {
|
|
36
|
+
onQueryChange: (matchingString: string | null) => void;
|
|
37
|
+
onSelectOption: (option: TOption, textNodeContainingQuery: TextNode | null, closeMenu: () => void, matchingString: string) => void;
|
|
38
|
+
options: Array<TOption>;
|
|
39
|
+
menuRenderFn: MenuRenderFn<TOption>;
|
|
40
|
+
triggerFn: TriggerFn;
|
|
41
|
+
};
|
|
42
|
+
declare type TriggerFn = (text: string) => QueryMatch | null;
|
|
43
|
+
export declare function LexicalTypeaheadMenuPlugin<TOption extends TypeaheadOption>({ options, onQueryChange, onSelectOption, menuRenderFn, triggerFn, }: TypeaheadMenuPluginArgs<TOption>): JSX.Element | null;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,463 @@
|
|
|
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 utils = require('@lexical/utils');
|
|
11
|
+
var lexical = require('lexical');
|
|
12
|
+
var React = require('react');
|
|
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
|
+
const PUNCTUATION = '\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%\'"~=<>_:;';
|
|
41
|
+
class TypeaheadOption {
|
|
42
|
+
constructor(key) {
|
|
43
|
+
this.key = key;
|
|
44
|
+
this.ref = {
|
|
45
|
+
current: null
|
|
46
|
+
};
|
|
47
|
+
this.setRefElement = this.setRefElement.bind(this);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setRefElement(element) {
|
|
51
|
+
this.ref = {
|
|
52
|
+
current: element
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const scrollIntoViewIfNeeded = target => {
|
|
59
|
+
const container = document.getElementById('typeahead-menu');
|
|
60
|
+
|
|
61
|
+
if (container) {
|
|
62
|
+
const containerRect = container.getBoundingClientRect();
|
|
63
|
+
const targetRect = target.getBoundingClientRect();
|
|
64
|
+
|
|
65
|
+
if (targetRect.bottom > containerRect.bottom) {
|
|
66
|
+
target.scrollIntoView(false);
|
|
67
|
+
} else if (targetRect.top < containerRect.top) {
|
|
68
|
+
target.scrollIntoView();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
function getTextUpToAnchor(selection) {
|
|
74
|
+
const anchor = selection.anchor;
|
|
75
|
+
|
|
76
|
+
if (anchor.type !== 'text') {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const anchorNode = anchor.getNode();
|
|
81
|
+
|
|
82
|
+
if (!anchorNode.isSimpleText()) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const anchorOffset = anchor.offset;
|
|
87
|
+
return anchorNode.getTextContent().slice(0, anchorOffset);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function tryToPositionRange(leadOffset, range) {
|
|
91
|
+
const domSelection = window.getSelection();
|
|
92
|
+
|
|
93
|
+
if (domSelection === null || !domSelection.isCollapsed) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const anchorNode = domSelection.anchorNode;
|
|
98
|
+
const startOffset = leadOffset;
|
|
99
|
+
const endOffset = domSelection.anchorOffset;
|
|
100
|
+
|
|
101
|
+
if (anchorNode == null || endOffset == null) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
range.setStart(anchorNode, startOffset);
|
|
107
|
+
range.setEnd(anchorNode, endOffset);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function getQueryTextForSearch(editor) {
|
|
116
|
+
let text = null;
|
|
117
|
+
editor.getEditorState().read(() => {
|
|
118
|
+
const selection = lexical.$getSelection();
|
|
119
|
+
|
|
120
|
+
if (!lexical.$isRangeSelection(selection)) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
text = getTextUpToAnchor(selection);
|
|
125
|
+
});
|
|
126
|
+
return text;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Walk backwards along user input and forward through entity title to try
|
|
130
|
+
* and replace more of the user's text with entity.
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
function getFullMatchOffset(documentText, entryText, offset) {
|
|
135
|
+
let triggerOffset = offset;
|
|
136
|
+
|
|
137
|
+
for (let i = triggerOffset; i <= entryText.length; i++) {
|
|
138
|
+
if (documentText.substr(-i) === entryText.substr(0, i)) {
|
|
139
|
+
triggerOffset = i;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return triggerOffset;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Split Lexica TextNode and return a new TextNode only containing matched text.
|
|
147
|
+
* Common use cases include: removing the node, replacing with a new node.
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
function splitNodeContainingQuery(editor, match) {
|
|
152
|
+
const selection = lexical.$getSelection();
|
|
153
|
+
|
|
154
|
+
if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed()) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const anchor = selection.anchor;
|
|
159
|
+
|
|
160
|
+
if (anchor.type !== 'text') {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const anchorNode = anchor.getNode();
|
|
165
|
+
|
|
166
|
+
if (!anchorNode.isSimpleText()) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const selectionOffset = anchor.offset;
|
|
171
|
+
const textContent = anchorNode.getTextContent().slice(0, selectionOffset);
|
|
172
|
+
const characterOffset = match.replaceableString.length;
|
|
173
|
+
const queryOffset = getFullMatchOffset(textContent, match.matchingString, characterOffset);
|
|
174
|
+
const startOffset = selectionOffset - queryOffset;
|
|
175
|
+
|
|
176
|
+
if (startOffset < 0) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let newNode;
|
|
181
|
+
|
|
182
|
+
if (startOffset === 0) {
|
|
183
|
+
[newNode] = anchorNode.splitText(selectionOffset);
|
|
184
|
+
} else {
|
|
185
|
+
[, newNode] = anchorNode.splitText(startOffset, selectionOffset);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return newNode;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function isSelectionOnEntityBoundary(editor, offset) {
|
|
192
|
+
if (offset !== 0) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return editor.getEditorState().read(() => {
|
|
197
|
+
const selection = lexical.$getSelection();
|
|
198
|
+
|
|
199
|
+
if (lexical.$isRangeSelection(selection)) {
|
|
200
|
+
const anchor = selection.anchor;
|
|
201
|
+
const anchorNode = anchor.getNode();
|
|
202
|
+
const prevSibling = anchorNode.getPreviousSibling();
|
|
203
|
+
return lexical.$isTextNode(prevSibling) && prevSibling.isTextEntity();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return false;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function ShortcutTypeahead({
|
|
211
|
+
close,
|
|
212
|
+
editor,
|
|
213
|
+
resolution,
|
|
214
|
+
options,
|
|
215
|
+
menuRenderFn,
|
|
216
|
+
onSelectOption
|
|
217
|
+
}) {
|
|
218
|
+
const [selectedIndex, setHighlightedIndex] = React.useState(null);
|
|
219
|
+
const anchorElementRef = React.useRef(document.createElement('div'));
|
|
220
|
+
React.useEffect(() => {
|
|
221
|
+
setHighlightedIndex(0);
|
|
222
|
+
}, [resolution.match.matchingString]);
|
|
223
|
+
React.useEffect(() => {
|
|
224
|
+
const rootElement = editor.getRootElement();
|
|
225
|
+
|
|
226
|
+
function positionMenu() {
|
|
227
|
+
const containerDiv = anchorElementRef.current;
|
|
228
|
+
containerDiv.setAttribute('aria-label', 'Typeahead menu');
|
|
229
|
+
containerDiv.setAttribute('id', 'typeahead-menu');
|
|
230
|
+
containerDiv.setAttribute('role', 'listbox');
|
|
231
|
+
|
|
232
|
+
if (rootElement !== null) {
|
|
233
|
+
const range = resolution.range;
|
|
234
|
+
const {
|
|
235
|
+
left,
|
|
236
|
+
top,
|
|
237
|
+
height
|
|
238
|
+
} = range.getBoundingClientRect();
|
|
239
|
+
containerDiv.style.top = `${top + height + window.pageYOffset}px`;
|
|
240
|
+
containerDiv.style.left = `${left + window.pageXOffset}px`;
|
|
241
|
+
containerDiv.style.display = 'block';
|
|
242
|
+
containerDiv.style.position = 'absolute';
|
|
243
|
+
|
|
244
|
+
if (!containerDiv.isConnected) {
|
|
245
|
+
document.body.append(containerDiv);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
anchorElementRef.current = containerDiv;
|
|
249
|
+
rootElement.setAttribute('aria-controls', 'typeahead-menu');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
positionMenu();
|
|
254
|
+
window.addEventListener('resize', positionMenu);
|
|
255
|
+
return () => {
|
|
256
|
+
window.removeEventListener('resize', positionMenu);
|
|
257
|
+
|
|
258
|
+
if (rootElement !== null) {
|
|
259
|
+
rootElement.removeAttribute('aria-controls');
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}, [editor, resolution, options]);
|
|
263
|
+
const selectOptionAndCleanUp = React.useCallback(async selectedEntry => {
|
|
264
|
+
editor.update(() => {
|
|
265
|
+
const textNodeContainingQuery = splitNodeContainingQuery(editor, resolution.match);
|
|
266
|
+
onSelectOption(selectedEntry, textNodeContainingQuery, close, resolution.match.matchingString);
|
|
267
|
+
});
|
|
268
|
+
}, [close, editor, resolution.match, onSelectOption]);
|
|
269
|
+
const updateSelectedIndex = React.useCallback(index => {
|
|
270
|
+
const rootElem = editor.getRootElement();
|
|
271
|
+
|
|
272
|
+
if (rootElem !== null) {
|
|
273
|
+
rootElem.setAttribute('aria-activedescendant', 'typeahead-item-' + index);
|
|
274
|
+
setHighlightedIndex(index);
|
|
275
|
+
}
|
|
276
|
+
}, [editor]);
|
|
277
|
+
React.useEffect(() => {
|
|
278
|
+
return () => {
|
|
279
|
+
const rootElem = editor.getRootElement();
|
|
280
|
+
|
|
281
|
+
if (rootElem !== null) {
|
|
282
|
+
rootElem.removeAttribute('aria-activedescendant');
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
}, [editor]);
|
|
286
|
+
useLayoutEffect(() => {
|
|
287
|
+
if (options === null) {
|
|
288
|
+
setHighlightedIndex(null);
|
|
289
|
+
} else if (selectedIndex === null) {
|
|
290
|
+
updateSelectedIndex(0);
|
|
291
|
+
}
|
|
292
|
+
}, [options, selectedIndex, updateSelectedIndex]);
|
|
293
|
+
React.useEffect(() => {
|
|
294
|
+
return utils.mergeRegister(editor.registerCommand(lexical.KEY_ARROW_DOWN_COMMAND, payload => {
|
|
295
|
+
const event = payload;
|
|
296
|
+
|
|
297
|
+
if (options !== null && selectedIndex !== null) {
|
|
298
|
+
const newSelectedIndex = selectedIndex !== options.length - 1 ? selectedIndex + 1 : 0;
|
|
299
|
+
updateSelectedIndex(newSelectedIndex);
|
|
300
|
+
const option = options[newSelectedIndex];
|
|
301
|
+
|
|
302
|
+
if (option.ref != null && option.ref.current) {
|
|
303
|
+
scrollIntoViewIfNeeded(option.ref.current);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
event.preventDefault();
|
|
307
|
+
event.stopImmediatePropagation();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return true;
|
|
311
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ARROW_UP_COMMAND, payload => {
|
|
312
|
+
const event = payload;
|
|
313
|
+
|
|
314
|
+
if (options !== null && selectedIndex !== null) {
|
|
315
|
+
const newSelectedIndex = selectedIndex !== 0 ? selectedIndex - 1 : options.length - 1;
|
|
316
|
+
updateSelectedIndex(newSelectedIndex);
|
|
317
|
+
const option = options[newSelectedIndex];
|
|
318
|
+
|
|
319
|
+
if (option.ref != null && option.ref.current) {
|
|
320
|
+
scrollIntoViewIfNeeded(option.ref.current);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
event.preventDefault();
|
|
324
|
+
event.stopImmediatePropagation();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return true;
|
|
328
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ESCAPE_COMMAND, payload => {
|
|
329
|
+
const event = payload;
|
|
330
|
+
|
|
331
|
+
if (options === null || selectedIndex === null) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
event.preventDefault();
|
|
336
|
+
event.stopImmediatePropagation();
|
|
337
|
+
close();
|
|
338
|
+
return true;
|
|
339
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_TAB_COMMAND, payload => {
|
|
340
|
+
const event = payload;
|
|
341
|
+
|
|
342
|
+
if (options === null || selectedIndex === null || options[selectedIndex] == null) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
event.preventDefault();
|
|
347
|
+
event.stopImmediatePropagation();
|
|
348
|
+
selectOptionAndCleanUp(options[selectedIndex]);
|
|
349
|
+
return true;
|
|
350
|
+
}, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.KEY_ENTER_COMMAND, event => {
|
|
351
|
+
if (options === null || selectedIndex === null || options[selectedIndex] == null) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (event !== null) {
|
|
356
|
+
event.preventDefault();
|
|
357
|
+
event.stopImmediatePropagation();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
selectOptionAndCleanUp(options[selectedIndex]);
|
|
361
|
+
return true;
|
|
362
|
+
}, lexical.COMMAND_PRIORITY_LOW));
|
|
363
|
+
}, [selectOptionAndCleanUp, close, editor, options, selectedIndex, updateSelectedIndex]);
|
|
364
|
+
const listItemProps = React.useMemo(() => ({
|
|
365
|
+
selectOptionAndCleanUp,
|
|
366
|
+
selectedIndex,
|
|
367
|
+
setHighlightedIndex
|
|
368
|
+
}), [selectOptionAndCleanUp, selectedIndex]);
|
|
369
|
+
return menuRenderFn(anchorElementRef.current, listItemProps, resolution.match.matchingString);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function useBasicTypeaheadTriggerMatch(trigger, {
|
|
373
|
+
minLength = 1,
|
|
374
|
+
maxLength = 75
|
|
375
|
+
}) {
|
|
376
|
+
return React.useCallback(text => {
|
|
377
|
+
const validChars = '[^' + trigger + PUNCTUATION + '\\s]';
|
|
378
|
+
const TypeaheadTriggerRegex = new RegExp('(^|\\s|\\()(' + '[' + trigger + ']' + '((?:' + validChars + '){0,' + maxLength + '})' + ')$');
|
|
379
|
+
const match = TypeaheadTriggerRegex.exec(text);
|
|
380
|
+
|
|
381
|
+
if (match !== null) {
|
|
382
|
+
const maybeLeadingWhitespace = match[1];
|
|
383
|
+
const matchingString = match[3];
|
|
384
|
+
|
|
385
|
+
if (matchingString.length >= minLength) {
|
|
386
|
+
return {
|
|
387
|
+
leadOffset: match.index + maybeLeadingWhitespace.length,
|
|
388
|
+
matchingString,
|
|
389
|
+
replaceableString: match[2]
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return null;
|
|
395
|
+
}, [maxLength, minLength, trigger]);
|
|
396
|
+
}
|
|
397
|
+
function LexicalTypeaheadMenuPlugin({
|
|
398
|
+
options,
|
|
399
|
+
onQueryChange,
|
|
400
|
+
onSelectOption,
|
|
401
|
+
menuRenderFn,
|
|
402
|
+
triggerFn
|
|
403
|
+
}) {
|
|
404
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
405
|
+
const [resolution, setResolution] = React.useState(null);
|
|
406
|
+
React.useEffect(() => {
|
|
407
|
+
let activeRange = document.createRange();
|
|
408
|
+
let previousText = null;
|
|
409
|
+
|
|
410
|
+
const updateListener = () => {
|
|
411
|
+
editor.getEditorState().read(() => {
|
|
412
|
+
const range = activeRange;
|
|
413
|
+
const selection = lexical.$getSelection();
|
|
414
|
+
const text = getQueryTextForSearch(editor);
|
|
415
|
+
|
|
416
|
+
if (!lexical.$isRangeSelection(selection) || !selection.isCollapsed() || text === previousText || text === null || range === null) {
|
|
417
|
+
React.startTransition(() => setResolution(null));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
previousText = text;
|
|
422
|
+
const match = triggerFn(text);
|
|
423
|
+
onQueryChange(match ? match.matchingString : null);
|
|
424
|
+
|
|
425
|
+
if (match !== null && !isSelectionOnEntityBoundary(editor, match.leadOffset)) {
|
|
426
|
+
const isRangePositioned = tryToPositionRange(match.leadOffset, range);
|
|
427
|
+
|
|
428
|
+
if (isRangePositioned !== null) {
|
|
429
|
+
React.startTransition(() => setResolution({
|
|
430
|
+
match,
|
|
431
|
+
range
|
|
432
|
+
}));
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
React.startTransition(() => setResolution(null));
|
|
438
|
+
});
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const removeUpdateListener = editor.registerUpdateListener(updateListener);
|
|
442
|
+
return () => {
|
|
443
|
+
activeRange = null;
|
|
444
|
+
removeUpdateListener();
|
|
445
|
+
};
|
|
446
|
+
}, [editor, triggerFn, onQueryChange, resolution]);
|
|
447
|
+
const closeTypeahead = React.useCallback(() => {
|
|
448
|
+
setResolution(null);
|
|
449
|
+
}, []);
|
|
450
|
+
return resolution === null || editor === null ? null : /*#__PURE__*/React.createElement(ShortcutTypeahead, {
|
|
451
|
+
close: closeTypeahead,
|
|
452
|
+
resolution: resolution,
|
|
453
|
+
editor: editor,
|
|
454
|
+
options: options,
|
|
455
|
+
menuRenderFn: menuRenderFn,
|
|
456
|
+
onSelectOption: onSelectOption
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
exports.LexicalTypeaheadMenuPlugin = LexicalTypeaheadMenuPlugin;
|
|
461
|
+
exports.PUNCTUATION = PUNCTUATION;
|
|
462
|
+
exports.TypeaheadOption = TypeaheadOption;
|
|
463
|
+
exports.useBasicTypeaheadTriggerMatch = useBasicTypeaheadTriggerMatch;
|
|
@@ -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 LexicalTypeaheadMenuPlugin = process.env.NODE_ENV === 'development' ? require('./LexicalTypeaheadMenuPlugin.dev.js') : require('./LexicalTypeaheadMenuPlugin.prod.js')
|
|
9
|
+
module.exports = LexicalTypeaheadMenuPlugin;
|
|
@@ -0,0 +1,19 @@
|
|
|
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 h=require("@lexical/react/LexicalComposerContext"),k=require("@lexical/utils"),w=require("lexical"),x=require("react"),y="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement?x.useLayoutEffect:x.useEffect;class z{constructor(d){this.key=d;this.ref={current:null};this.setRefElement=this.setRefElement.bind(this)}setRefElement(d){this.ref={current:d}}}
|
|
8
|
+
let A=d=>{var a=document.getElementById("typeahead-menu");if(a){a=a.getBoundingClientRect();const b=d.getBoundingClientRect();b.bottom>a.bottom?d.scrollIntoView(!1):b.top<a.top&&d.scrollIntoView()}};function B(d,a){var b=window.getSelection();if(null===b||!b.isCollapsed)return!1;let c=b.anchorNode;b=b.anchorOffset;if(null==c||null==b)return!1;try{a.setStart(c,d),a.setEnd(c,b)}catch(n){return!1}return!0}
|
|
9
|
+
function C(d){let a=null;d.getEditorState().read(()=>{var b=w.$getSelection();if(w.$isRangeSelection(b)){var c=b.anchor;"text"!==c.type?a=null:(b=c.getNode(),b.isSimpleText()?(c=c.offset,a=b.getTextContent().slice(0,c)):a=null)}});return a}
|
|
10
|
+
function D(d,a){d=w.$getSelection();if(!w.$isRangeSelection(d)||!d.isCollapsed())return null;var b=d.anchor;if("text"!==b.type)return null;d=b.getNode();if(!d.isSimpleText())return null;b=b.offset;let c=d.getTextContent().slice(0,b);var n=a.matchingString;a=a.replaceableString.length;for(let f=a;f<=n.length;f++)c.substr(-f)===n.substr(0,f)&&(a=f);a=b-a;if(0>a)return null;let l;0===a?[l]=d.splitText(b):[,l]=d.splitText(a,b);return l}
|
|
11
|
+
function E(d,a){return 0!==a?!1:d.getEditorState().read(()=>{var b=w.$getSelection();return w.$isRangeSelection(b)?(b=b.anchor.getNode().getPreviousSibling(),w.$isTextNode(b)&&b.isTextEntity()):!1})}
|
|
12
|
+
function F({close:d,editor:a,resolution:b,options:c,menuRenderFn:n,onSelectOption:l}){let [f,p]=x.useState(null),u=x.useRef(document.createElement("div"));x.useEffect(()=>{p(0)},[b.match.matchingString]);x.useEffect(()=>{function e(){let m=u.current;m.setAttribute("aria-label","Typeahead menu");m.setAttribute("id","typeahead-menu");m.setAttribute("role","listbox");if(null!==g){let {left:q,top:G,height:H}=b.range.getBoundingClientRect();m.style.top=`${G+H+window.pageYOffset}px`;m.style.left=`${q+window.pageXOffset}px`;
|
|
13
|
+
m.style.display="block";m.style.position="absolute";m.isConnected||document.body.append(m);u.current=m;g.setAttribute("aria-controls","typeahead-menu")}}let g=a.getRootElement();e();window.addEventListener("resize",e);return()=>{window.removeEventListener("resize",e);null!==g&&g.removeAttribute("aria-controls")}},[a,b,c]);let r=x.useCallback(async e=>{a.update(()=>{const g=D(a,b.match);l(e,g,d,b.match.matchingString)})},[d,a,b.match,l]),t=x.useCallback(e=>{const g=a.getRootElement();null!==g&&(g.setAttribute("aria-activedescendant",
|
|
14
|
+
"typeahead-item-"+e),p(e))},[a]);x.useEffect(()=>()=>{let e=a.getRootElement();null!==e&&e.removeAttribute("aria-activedescendant")},[a]);y(()=>{null===c?p(null):null===f&&t(0)},[c,f,t]);x.useEffect(()=>k.mergeRegister(a.registerCommand(w.KEY_ARROW_DOWN_COMMAND,e=>{if(null!==c&&null!==f){var g=f!==c.length-1?f+1:0;t(g);g=c[g];null!=g.ref&&g.ref.current&&A(g.ref.current);e.preventDefault();e.stopImmediatePropagation()}return!0},w.COMMAND_PRIORITY_LOW),a.registerCommand(w.KEY_ARROW_UP_COMMAND,e=>{if(null!==
|
|
15
|
+
c&&null!==f){var g=0!==f?f-1:c.length-1;t(g);g=c[g];null!=g.ref&&g.ref.current&&A(g.ref.current);e.preventDefault();e.stopImmediatePropagation()}return!0},w.COMMAND_PRIORITY_LOW),a.registerCommand(w.KEY_ESCAPE_COMMAND,e=>{if(null===c||null===f)return!1;e.preventDefault();e.stopImmediatePropagation();d();return!0},w.COMMAND_PRIORITY_LOW),a.registerCommand(w.KEY_TAB_COMMAND,e=>{if(null===c||null===f||null==c[f])return!1;e.preventDefault();e.stopImmediatePropagation();r(c[f]);return!0},w.COMMAND_PRIORITY_LOW),
|
|
16
|
+
a.registerCommand(w.KEY_ENTER_COMMAND,e=>{if(null===c||null===f||null==c[f])return!1;null!==e&&(e.preventDefault(),e.stopImmediatePropagation());r(c[f]);return!0},w.COMMAND_PRIORITY_LOW)),[r,d,a,c,f,t]);let v=x.useMemo(()=>({selectOptionAndCleanUp:r,selectedIndex:f,setHighlightedIndex:p}),[r,f]);return n(u.current,v,b.match.matchingString)}
|
|
17
|
+
exports.LexicalTypeaheadMenuPlugin=function({options:d,onQueryChange:a,onSelectOption:b,menuRenderFn:c,triggerFn:n}){let [l]=h.useLexicalComposerContext(),[f,p]=x.useState(null);x.useEffect(()=>{let r=document.createRange(),t=null,v=l.registerUpdateListener(()=>{l.getEditorState().read(()=>{const e=r,g=w.$getSelection(),m=C(l);if(w.$isRangeSelection(g)&&g.isCollapsed()&&m!==t&&null!==m&&null!==e){t=m;var q=n(m);a(q?q.matchingString:null);null===q||E(l,q.leadOffset)||null===B(q.leadOffset,e)?x.startTransition(()=>
|
|
18
|
+
p(null)):x.startTransition(()=>p({match:q,range:e}))}else x.startTransition(()=>p(null))})});return()=>{r=null;v()}},[l,n,a,f]);let u=x.useCallback(()=>{p(null)},[]);return null===f||null===l?null:x.createElement(F,{close:u,resolution:f,editor:l,options:d,menuRenderFn:c,onSelectOption:b})};exports.PUNCTUATION="\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%'\"~=<>_:;";exports.TypeaheadOption=z;
|
|
19
|
+
exports.useBasicTypeaheadTriggerMatch=function(d,{minLength:a=1,maxLength:b=75}){return x.useCallback(c=>{c=(new RegExp("(^|\\s|\\()(["+d+"]((?:[^"+(d+"\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%'\"~=<>_:;\\s]){0,")+b+"}))$")).exec(c);if(null!==c){let n=c[1],l=c[3];if(l.length>=a)return{leadOffset:c.index+n.length,matchingString:l,replaceableString:c[2]}}return null},[b,a,d])}
|
package/package.json
CHANGED
|
@@ -8,28 +8,28 @@
|
|
|
8
8
|
"rich-text"
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"version": "0.3.
|
|
11
|
+
"version": "0.3.8",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@lexical/clipboard": "0.3.
|
|
14
|
-
"@lexical/code": "0.3.
|
|
15
|
-
"@lexical/dragon": "0.3.
|
|
16
|
-
"@lexical/hashtag": "0.3.
|
|
17
|
-
"@lexical/history": "0.3.
|
|
18
|
-
"@lexical/link": "0.3.
|
|
19
|
-
"@lexical/list": "0.3.
|
|
20
|
-
"@lexical/mark": "0.3.
|
|
21
|
-
"@lexical/markdown": "0.3.
|
|
22
|
-
"@lexical/overflow": "0.3.
|
|
23
|
-
"@lexical/plain-text": "0.3.
|
|
24
|
-
"@lexical/rich-text": "0.3.
|
|
25
|
-
"@lexical/selection": "0.3.
|
|
26
|
-
"@lexical/table": "0.3.
|
|
27
|
-
"@lexical/text": "0.3.
|
|
28
|
-
"@lexical/utils": "0.3.
|
|
29
|
-
"@lexical/yjs": "0.3.
|
|
13
|
+
"@lexical/clipboard": "0.3.8",
|
|
14
|
+
"@lexical/code": "0.3.8",
|
|
15
|
+
"@lexical/dragon": "0.3.8",
|
|
16
|
+
"@lexical/hashtag": "0.3.8",
|
|
17
|
+
"@lexical/history": "0.3.8",
|
|
18
|
+
"@lexical/link": "0.3.8",
|
|
19
|
+
"@lexical/list": "0.3.8",
|
|
20
|
+
"@lexical/mark": "0.3.8",
|
|
21
|
+
"@lexical/markdown": "0.3.8",
|
|
22
|
+
"@lexical/overflow": "0.3.8",
|
|
23
|
+
"@lexical/plain-text": "0.3.8",
|
|
24
|
+
"@lexical/rich-text": "0.3.8",
|
|
25
|
+
"@lexical/selection": "0.3.8",
|
|
26
|
+
"@lexical/table": "0.3.8",
|
|
27
|
+
"@lexical/text": "0.3.8",
|
|
28
|
+
"@lexical/utils": "0.3.8",
|
|
29
|
+
"@lexical/yjs": "0.3.8"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"lexical": "0.3.
|
|
32
|
+
"lexical": "0.3.8",
|
|
33
33
|
"react": ">=17.x",
|
|
34
34
|
"react-dom": ">=17.x"
|
|
35
35
|
},
|