5htp-core 0.4.8-3 → 0.4.9-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -1
- package/src/client/components/Form.ts +2 -6
- package/src/client/components/button.tsx +2 -1
- package/src/client/components/containers/Popover/index.tsx +2 -2
- package/src/client/components/dropdown/index.tsx +16 -6
- package/src/client/components/input/Slider/index.tsx +0 -2
- package/src/client/components/inputv3/Rte/Editor.tsx +271 -0
- package/src/client/components/inputv3/Rte/ToolbarPlugin/BlockFormat.tsx +220 -0
- package/src/client/components/inputv3/Rte/ToolbarPlugin/ElementFormat.tsx +107 -0
- package/src/client/components/inputv3/Rte/ToolbarPlugin/index.tsx +768 -0
- package/src/client/components/inputv3/Rte/appSettings.ts +36 -0
- package/src/client/components/inputv3/Rte/context/FlashMessageContext.tsx +68 -0
- package/src/client/components/inputv3/Rte/context/SettingsContext.tsx +71 -0
- package/src/client/components/inputv3/Rte/context/SharedAutocompleteContext.tsx +71 -0
- package/src/client/components/inputv3/Rte/context/SharedHistoryContext.tsx +35 -0
- package/src/client/components/inputv3/Rte/currentEditor.ts +3 -1
- package/src/client/components/inputv3/Rte/hooks/useFlashMessage.tsx +16 -0
- package/src/client/components/inputv3/Rte/hooks/useReport.ts +67 -0
- package/src/client/components/inputv3/Rte/images/emoji/1F600.png +0 -0
- package/src/client/components/inputv3/Rte/images/emoji/1F641.png +0 -0
- package/src/client/components/inputv3/Rte/images/emoji/1F642.png +0 -0
- package/src/client/components/inputv3/Rte/images/emoji/2764.png +0 -0
- package/src/client/components/inputv3/Rte/images/emoji/LICENSE.md +5 -0
- package/src/client/components/inputv3/Rte/images/icons/draggable-block-menu.svg +1 -0
- package/src/client/components/inputv3/Rte/images/icons/prettier-error.svg +1 -0
- package/src/client/components/inputv3/Rte/images/icons/prettier.svg +1 -0
- package/src/client/components/inputv3/Rte/images/image/LICENSE.md +5 -0
- package/src/client/components/inputv3/Rte/images/image-broken.svg +4 -0
- package/src/client/components/inputv3/Rte/images/logo.svg +1 -0
- package/src/client/components/inputv3/Rte/index.tsx +25 -94
- package/src/client/components/inputv3/Rte/nodes/AutocompleteNode.tsx +119 -0
- package/src/client/components/inputv3/Rte/nodes/EmojiNode.tsx +102 -0
- package/src/client/components/inputv3/Rte/nodes/EquationComponent.tsx +141 -0
- package/src/client/components/inputv3/Rte/nodes/EquationNode.tsx +174 -0
- package/src/client/components/inputv3/Rte/nodes/FigmaNode.tsx +135 -0
- package/src/client/components/inputv3/Rte/nodes/ImageComponent.tsx +468 -0
- package/src/client/components/inputv3/Rte/nodes/ImageNode.css +43 -0
- package/src/client/components/inputv3/Rte/nodes/ImageNode.tsx +266 -0
- package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageComponent.tsx +402 -0
- package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageNode.css +94 -0
- package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageNode.tsx +294 -0
- package/src/client/components/inputv3/Rte/nodes/KeywordNode.ts +67 -0
- package/src/client/components/inputv3/Rte/nodes/LayoutContainerNode.ts +137 -0
- package/src/client/components/inputv3/Rte/nodes/LayoutItemNode.ts +71 -0
- package/src/client/components/inputv3/Rte/nodes/MentionNode.ts +130 -0
- package/src/client/components/inputv3/Rte/nodes/PageBreakNode/index.css +62 -0
- package/src/client/components/inputv3/Rte/nodes/PageBreakNode/index.tsx +170 -0
- package/src/client/components/inputv3/Rte/nodes/PlaygroundNodes.ts +76 -0
- package/src/client/components/inputv3/Rte/nodes/PollComponent.tsx +249 -0
- package/src/client/components/inputv3/Rte/nodes/PollNode.css +187 -0
- package/src/client/components/inputv3/Rte/nodes/PollNode.tsx +209 -0
- package/src/client/components/inputv3/Rte/nodes/StickyComponent.tsx +261 -0
- package/src/client/components/inputv3/Rte/nodes/StickyNode.css +37 -0
- package/src/client/components/inputv3/Rte/nodes/StickyNode.tsx +150 -0
- package/src/client/components/inputv3/Rte/nodes/TweetNode.tsx +223 -0
- package/src/client/components/inputv3/Rte/nodes/YouTubeNode.tsx +184 -0
- package/src/client/components/inputv3/Rte/plugins/ActionsPlugin/index.tsx +334 -0
- package/src/client/components/inputv3/Rte/plugins/AutoEmbedPlugin/index.tsx +352 -0
- package/src/client/components/inputv3/Rte/plugins/AutoLinkPlugin/index.tsx +32 -0
- package/src/client/components/inputv3/Rte/plugins/AutocompletePlugin/index.tsx +2529 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/CopyButton/index.tsx +70 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/PrettierButton/index.css +14 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/PrettierButton/index.tsx +156 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/index.css +54 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/index.tsx +190 -0
- package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/utils.ts +33 -0
- package/src/client/components/inputv3/Rte/plugins/CodeHighlightPlugin/index.ts +21 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/Collapsible.css +57 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleContainerNode.ts +168 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleContentNode.ts +127 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleTitleNode.ts +152 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleUtils.ts +17 -0
- package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/index.ts +284 -0
- package/src/client/components/inputv3/Rte/plugins/ComponentPickerPlugin/index.tsx +370 -0
- package/src/client/components/inputv3/Rte/plugins/ContextMenuPlugin/index.tsx +270 -0
- package/src/client/components/inputv3/Rte/plugins/DocsPlugin/index.tsx +20 -0
- package/src/client/components/inputv3/Rte/plugins/DragDropPastePlugin/index.ts +51 -0
- package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.css +36 -0
- package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.tsx +43 -0
- package/src/client/components/inputv3/Rte/plugins/EmojiPickerPlugin/index.tsx +198 -0
- package/src/client/components/inputv3/Rte/plugins/EmojisPlugin/index.ts +75 -0
- package/src/client/components/inputv3/Rte/plugins/EquationsPlugin/index.tsx +82 -0
- package/src/client/components/inputv3/Rte/plugins/FigmaPlugin/index.tsx +40 -0
- package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.css +41 -0
- package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.tsx +393 -0
- package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.css +141 -0
- package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.tsx +388 -0
- package/src/client/components/inputv3/Rte/plugins/ImagesPlugin/index.tsx +350 -0
- package/src/client/components/inputv3/Rte/plugins/InlineImagePlugin/index.tsx +336 -0
- package/src/client/components/inputv3/Rte/plugins/KeywordsPlugin/index.ts +56 -0
- package/src/client/components/inputv3/Rte/plugins/LayoutPlugin/InsertLayoutDialog.tsx +58 -0
- package/src/client/components/inputv3/Rte/plugins/LayoutPlugin/LayoutPlugin.tsx +219 -0
- package/src/client/components/inputv3/Rte/plugins/LinkPlugin/index.tsx +34 -0
- package/src/client/components/inputv3/Rte/plugins/ListMaxIndentLevelPlugin/index.ts +85 -0
- package/src/client/components/inputv3/Rte/plugins/MarkdownShortcutPlugin/index.tsx +16 -0
- package/src/client/components/inputv3/Rte/plugins/MarkdownTransformers/index.ts +324 -0
- package/src/client/components/inputv3/Rte/plugins/MaxLengthPlugin/index.tsx +53 -0
- package/src/client/components/inputv3/Rte/plugins/MentionsPlugin/index.tsx +696 -0
- package/src/client/components/inputv3/Rte/plugins/PageBreakPlugin/index.tsx +57 -0
- package/src/client/components/inputv3/Rte/plugins/PasteLogPlugin/index.tsx +54 -0
- package/src/client/components/inputv3/Rte/plugins/PollPlugin/index.tsx +86 -0
- package/src/client/components/inputv3/Rte/plugins/SpeechToTextPlugin/index.ts +125 -0
- package/src/client/components/inputv3/Rte/plugins/StickyPlugin/index.ts +22 -0
- package/src/client/components/inputv3/Rte/plugins/TabFocusPlugin/index.tsx +65 -0
- package/src/client/components/inputv3/Rte/plugins/TableActionMenuPlugin/index.tsx +773 -0
- package/src/client/components/inputv3/Rte/plugins/TableCellResizer/index.css +12 -0
- package/src/client/components/inputv3/Rte/plugins/TableCellResizer/index.tsx +436 -0
- package/src/client/components/inputv3/Rte/plugins/TableHoverActionsPlugin/index.tsx +287 -0
- package/src/client/components/inputv3/Rte/plugins/TableOfContentsPlugin/index.css +95 -0
- package/src/client/components/inputv3/Rte/plugins/TableOfContentsPlugin/index.tsx +197 -0
- package/src/client/components/inputv3/Rte/plugins/TablePlugin.tsx +178 -0
- package/src/client/components/inputv3/Rte/plugins/TestRecorderPlugin/index.tsx +468 -0
- package/src/client/components/inputv3/Rte/plugins/TreeViewPlugin/index.tsx +26 -0
- package/src/client/components/inputv3/Rte/plugins/TwitterPlugin/index.ts +41 -0
- package/src/client/components/inputv3/Rte/plugins/TypingPerfPlugin/index.ts +117 -0
- package/src/client/components/inputv3/Rte/plugins/YouTubePlugin/index.ts +41 -0
- package/src/client/components/inputv3/Rte/shared/canUseDOM.ts +4 -0
- package/src/client/components/inputv3/Rte/shared/caretFromPoint.ts +40 -0
- package/src/client/components/inputv3/Rte/shared/environment.ts +56 -0
- package/src/client/components/inputv3/Rte/shared/invariant.ts +26 -0
- package/src/client/components/inputv3/Rte/shared/normalizeClassNames.ts +21 -0
- package/src/client/components/inputv3/Rte/shared/react-test-utils.ts +18 -0
- package/src/client/components/inputv3/Rte/shared/reactPatches.ts +22 -0
- package/src/client/components/inputv3/Rte/shared/simpleDiffWithCursor.ts +49 -0
- package/src/client/components/inputv3/Rte/shared/useLayoutEffect.ts +19 -0
- package/src/client/components/inputv3/Rte/shared/warnOnlyOnce.ts +20 -0
- package/src/client/components/inputv3/Rte/style.less +30 -60
- package/src/client/components/inputv3/Rte/themes/CommentEditorTheme.css +13 -0
- package/src/client/components/inputv3/Rte/themes/CommentEditorTheme.ts +20 -0
- package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.css +447 -0
- package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.ts +120 -0
- package/src/client/components/inputv3/Rte/themes/StickyEditorTheme.css +13 -0
- package/src/client/components/inputv3/Rte/themes/StickyEditorTheme.ts +20 -0
- package/src/client/components/inputv3/Rte/ui/ColorPicker.css +88 -0
- package/src/client/components/inputv3/Rte/ui/ColorPicker.tsx +365 -0
- package/src/client/components/inputv3/Rte/ui/ContentEditable.css +44 -0
- package/src/client/components/inputv3/Rte/ui/ContentEditable.tsx +36 -0
- package/src/client/components/inputv3/Rte/ui/DropDown.tsx +259 -0
- package/src/client/components/inputv3/Rte/ui/DropdownColorPicker.tsx +41 -0
- package/src/client/components/inputv3/Rte/ui/EquationEditor.css +38 -0
- package/src/client/components/inputv3/Rte/ui/EquationEditor.tsx +56 -0
- package/src/client/components/inputv3/Rte/ui/FileInput.tsx +38 -0
- package/src/client/components/inputv3/Rte/ui/FlashMessage.css +28 -0
- package/src/client/components/inputv3/Rte/ui/FlashMessage.tsx +29 -0
- package/src/client/components/inputv3/Rte/ui/ImageResizer.tsx +316 -0
- package/src/client/components/inputv3/Rte/ui/Input.css +32 -0
- package/src/client/components/inputv3/Rte/ui/KatexRenderer.tsx +54 -0
- package/src/client/components/inputv3/Rte/ui/Switch.tsx +36 -0
- package/src/client/components/inputv3/Rte/utils/docSerialization.ts +77 -0
- package/src/client/components/inputv3/Rte/utils/emoji-list.ts +16615 -0
- package/src/client/components/inputv3/Rte/utils/getDOMRangeRect.ts +27 -0
- package/src/client/components/inputv3/Rte/utils/getSelectedNode.ts +27 -0
- package/src/client/components/inputv3/Rte/utils/guard.ts +10 -0
- package/src/client/components/inputv3/Rte/utils/isMobileWidth.ts +7 -0
- package/src/client/components/inputv3/Rte/utils/joinClasses.ts +13 -0
- package/src/client/components/inputv3/Rte/utils/setFloatingElemPosition.ts +51 -0
- package/src/client/components/inputv3/Rte/utils/setFloatingElemPositionForLinkEditor.ts +46 -0
- package/src/client/components/inputv3/Rte/utils/swipe.ts +127 -0
- package/src/client/components/inputv3/Rte/utils/url.ts +38 -0
- package/src/client/components/inputv3/base.tsx +8 -5
- package/src/client/components/inputv3/index.tsx +1 -1
- package/src/common/data/rte/nodes.ts +60 -9
- package/src/common/validation/index.ts +21 -2
- package/src/common/validation/schema.ts +27 -10
- package/src/common/validation/validator.ts +12 -4
- package/src/common/validation/validators.ts +108 -66
- package/src/server/services/router/http/multipart.ts +0 -1
- package/src/server/services/schema/index.ts +26 -4
- package/src/server/services/schema/request.ts +3 -2
- package/src/server/services/schema/rte.ts +110 -0
- package/src/server/utils/rte.ts +7 -4
- package/src/client/components/inputv3/Rte/ExampleTheme.tsx +0 -42
- package/src/client/components/inputv3/Rte/ToolbarPlugin.tsx +0 -167
- package/src/client/components/inputv3/Rte/icons/LICENSE.md +0 -5
- package/src/client/components/inputv3/Rte/icons/arrow-clockwise.svg +0 -4
- package/src/client/components/inputv3/Rte/icons/arrow-counterclockwise.svg +0 -4
- package/src/client/components/inputv3/Rte/icons/journal-text.svg +0 -5
- package/src/client/components/inputv3/Rte/icons/justify.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/text-center.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/text-left.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/text-paragraph.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/text-right.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/type-bold.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/type-italic.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/type-strikethrough.svg +0 -3
- package/src/client/components/inputv3/Rte/icons/type-underline.svg +0 -3
|
@@ -0,0 +1,284 @@
|
|
|
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
|
+
|
|
9
|
+
import './Collapsible.css';
|
|
10
|
+
|
|
11
|
+
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
|
|
12
|
+
import {
|
|
13
|
+
$findMatchingParent,
|
|
14
|
+
$insertNodeToNearestRoot,
|
|
15
|
+
mergeRegister,
|
|
16
|
+
} from '@lexical/utils';
|
|
17
|
+
import {
|
|
18
|
+
$createParagraphNode,
|
|
19
|
+
$getSelection,
|
|
20
|
+
$isRangeSelection,
|
|
21
|
+
COMMAND_PRIORITY_LOW,
|
|
22
|
+
createCommand,
|
|
23
|
+
DELETE_CHARACTER_COMMAND,
|
|
24
|
+
ElementNode,
|
|
25
|
+
INSERT_PARAGRAPH_COMMAND,
|
|
26
|
+
KEY_ARROW_DOWN_COMMAND,
|
|
27
|
+
KEY_ARROW_LEFT_COMMAND,
|
|
28
|
+
KEY_ARROW_RIGHT_COMMAND,
|
|
29
|
+
KEY_ARROW_UP_COMMAND,
|
|
30
|
+
LexicalNode,
|
|
31
|
+
} from 'lexical';
|
|
32
|
+
import {useEffect} from 'react';
|
|
33
|
+
|
|
34
|
+
import {
|
|
35
|
+
$createCollapsibleContainerNode,
|
|
36
|
+
$isCollapsibleContainerNode,
|
|
37
|
+
CollapsibleContainerNode,
|
|
38
|
+
} from './CollapsibleContainerNode';
|
|
39
|
+
import {
|
|
40
|
+
$createCollapsibleContentNode,
|
|
41
|
+
$isCollapsibleContentNode,
|
|
42
|
+
CollapsibleContentNode,
|
|
43
|
+
} from './CollapsibleContentNode';
|
|
44
|
+
import {
|
|
45
|
+
$createCollapsibleTitleNode,
|
|
46
|
+
$isCollapsibleTitleNode,
|
|
47
|
+
CollapsibleTitleNode,
|
|
48
|
+
} from './CollapsibleTitleNode';
|
|
49
|
+
|
|
50
|
+
export const INSERT_COLLAPSIBLE_COMMAND = createCommand<void>();
|
|
51
|
+
|
|
52
|
+
export default function CollapsiblePlugin(): null {
|
|
53
|
+
const [editor] = useLexicalComposerContext();
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (
|
|
57
|
+
!editor.hasNodes([
|
|
58
|
+
CollapsibleContainerNode,
|
|
59
|
+
CollapsibleTitleNode,
|
|
60
|
+
CollapsibleContentNode,
|
|
61
|
+
])
|
|
62
|
+
) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
'CollapsiblePlugin: CollapsibleContainerNode, CollapsibleTitleNode, or CollapsibleContentNode not registered on editor',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const $onEscapeUp = () => {
|
|
69
|
+
const selection = $getSelection();
|
|
70
|
+
if (
|
|
71
|
+
$isRangeSelection(selection) &&
|
|
72
|
+
selection.isCollapsed() &&
|
|
73
|
+
selection.anchor.offset === 0
|
|
74
|
+
) {
|
|
75
|
+
const container = $findMatchingParent(
|
|
76
|
+
selection.anchor.getNode(),
|
|
77
|
+
$isCollapsibleContainerNode,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if ($isCollapsibleContainerNode(container)) {
|
|
81
|
+
const parent = container.getParent<ElementNode>();
|
|
82
|
+
if (
|
|
83
|
+
parent !== null &&
|
|
84
|
+
parent.getFirstChild<LexicalNode>() === container &&
|
|
85
|
+
selection.anchor.key ===
|
|
86
|
+
container.getFirstDescendant<LexicalNode>()?.getKey()
|
|
87
|
+
) {
|
|
88
|
+
container.insertBefore($createParagraphNode());
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return false;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const $onEscapeDown = () => {
|
|
97
|
+
const selection = $getSelection();
|
|
98
|
+
if ($isRangeSelection(selection) && selection.isCollapsed()) {
|
|
99
|
+
const container = $findMatchingParent(
|
|
100
|
+
selection.anchor.getNode(),
|
|
101
|
+
$isCollapsibleContainerNode,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if ($isCollapsibleContainerNode(container)) {
|
|
105
|
+
const parent = container.getParent<ElementNode>();
|
|
106
|
+
if (
|
|
107
|
+
parent !== null &&
|
|
108
|
+
parent.getLastChild<LexicalNode>() === container
|
|
109
|
+
) {
|
|
110
|
+
const titleParagraph = container.getFirstDescendant<LexicalNode>();
|
|
111
|
+
const contentParagraph = container.getLastDescendant<LexicalNode>();
|
|
112
|
+
|
|
113
|
+
if (
|
|
114
|
+
(contentParagraph !== null &&
|
|
115
|
+
selection.anchor.key === contentParagraph.getKey() &&
|
|
116
|
+
selection.anchor.offset ===
|
|
117
|
+
contentParagraph.getTextContentSize()) ||
|
|
118
|
+
(titleParagraph !== null &&
|
|
119
|
+
selection.anchor.key === titleParagraph.getKey() &&
|
|
120
|
+
selection.anchor.offset === titleParagraph.getTextContentSize())
|
|
121
|
+
) {
|
|
122
|
+
container.insertAfter($createParagraphNode());
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return false;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return mergeRegister(
|
|
132
|
+
// Structure enforcing transformers for each node type. In case nesting structure is not
|
|
133
|
+
// "Container > Title + Content" it'll unwrap nodes and convert it back
|
|
134
|
+
// to regular content.
|
|
135
|
+
editor.registerNodeTransform(CollapsibleContentNode, (node) => {
|
|
136
|
+
const parent = node.getParent<ElementNode>();
|
|
137
|
+
if (!$isCollapsibleContainerNode(parent)) {
|
|
138
|
+
const children = node.getChildren<LexicalNode>();
|
|
139
|
+
for (const child of children) {
|
|
140
|
+
node.insertBefore(child);
|
|
141
|
+
}
|
|
142
|
+
node.remove();
|
|
143
|
+
}
|
|
144
|
+
}),
|
|
145
|
+
|
|
146
|
+
editor.registerNodeTransform(CollapsibleTitleNode, (node) => {
|
|
147
|
+
const parent = node.getParent<ElementNode>();
|
|
148
|
+
if (!$isCollapsibleContainerNode(parent)) {
|
|
149
|
+
node.replace(
|
|
150
|
+
$createParagraphNode().append(...node.getChildren<LexicalNode>()),
|
|
151
|
+
);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
}),
|
|
155
|
+
|
|
156
|
+
editor.registerNodeTransform(CollapsibleContainerNode, (node) => {
|
|
157
|
+
const children = node.getChildren<LexicalNode>();
|
|
158
|
+
if (
|
|
159
|
+
children.length !== 2 ||
|
|
160
|
+
!$isCollapsibleTitleNode(children[0]) ||
|
|
161
|
+
!$isCollapsibleContentNode(children[1])
|
|
162
|
+
) {
|
|
163
|
+
for (const child of children) {
|
|
164
|
+
node.insertBefore(child);
|
|
165
|
+
}
|
|
166
|
+
node.remove();
|
|
167
|
+
}
|
|
168
|
+
}),
|
|
169
|
+
|
|
170
|
+
// This handles the case when container is collapsed and we delete its previous sibling
|
|
171
|
+
// into it, it would cause collapsed content deleted (since it's display: none, and selection
|
|
172
|
+
// swallows it when deletes single char). Instead we expand container, which is although
|
|
173
|
+
// not perfect, but avoids bigger problem
|
|
174
|
+
editor.registerCommand(
|
|
175
|
+
DELETE_CHARACTER_COMMAND,
|
|
176
|
+
() => {
|
|
177
|
+
const selection = $getSelection();
|
|
178
|
+
if (
|
|
179
|
+
!$isRangeSelection(selection) ||
|
|
180
|
+
!selection.isCollapsed() ||
|
|
181
|
+
selection.anchor.offset !== 0
|
|
182
|
+
) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const anchorNode = selection.anchor.getNode();
|
|
187
|
+
const topLevelElement = anchorNode.getTopLevelElement();
|
|
188
|
+
if (topLevelElement === null) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const container = topLevelElement.getPreviousSibling<LexicalNode>();
|
|
193
|
+
if (!$isCollapsibleContainerNode(container) || container.getOpen()) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
container.setOpen(true);
|
|
198
|
+
return true;
|
|
199
|
+
},
|
|
200
|
+
COMMAND_PRIORITY_LOW,
|
|
201
|
+
),
|
|
202
|
+
|
|
203
|
+
// When collapsible is the last child pressing down/right arrow will insert paragraph
|
|
204
|
+
// below it to allow adding more content. It's similar what $insertBlockNode
|
|
205
|
+
// (mainly for decorators), except it'll always be possible to continue adding
|
|
206
|
+
// new content even if trailing paragraph is accidentally deleted
|
|
207
|
+
editor.registerCommand(
|
|
208
|
+
KEY_ARROW_DOWN_COMMAND,
|
|
209
|
+
$onEscapeDown,
|
|
210
|
+
COMMAND_PRIORITY_LOW,
|
|
211
|
+
),
|
|
212
|
+
|
|
213
|
+
editor.registerCommand(
|
|
214
|
+
KEY_ARROW_RIGHT_COMMAND,
|
|
215
|
+
$onEscapeDown,
|
|
216
|
+
COMMAND_PRIORITY_LOW,
|
|
217
|
+
),
|
|
218
|
+
|
|
219
|
+
// When collapsible is the first child pressing up/left arrow will insert paragraph
|
|
220
|
+
// above it to allow adding more content. It's similar what $insertBlockNode
|
|
221
|
+
// (mainly for decorators), except it'll always be possible to continue adding
|
|
222
|
+
// new content even if leading paragraph is accidentally deleted
|
|
223
|
+
editor.registerCommand(
|
|
224
|
+
KEY_ARROW_UP_COMMAND,
|
|
225
|
+
$onEscapeUp,
|
|
226
|
+
COMMAND_PRIORITY_LOW,
|
|
227
|
+
),
|
|
228
|
+
|
|
229
|
+
editor.registerCommand(
|
|
230
|
+
KEY_ARROW_LEFT_COMMAND,
|
|
231
|
+
$onEscapeUp,
|
|
232
|
+
COMMAND_PRIORITY_LOW,
|
|
233
|
+
),
|
|
234
|
+
|
|
235
|
+
// Enter goes from Title to Content rather than a new line inside Title
|
|
236
|
+
editor.registerCommand(
|
|
237
|
+
INSERT_PARAGRAPH_COMMAND,
|
|
238
|
+
() => {
|
|
239
|
+
const selection = $getSelection();
|
|
240
|
+
if ($isRangeSelection(selection)) {
|
|
241
|
+
const titleNode = $findMatchingParent(
|
|
242
|
+
selection.anchor.getNode(),
|
|
243
|
+
(node) => $isCollapsibleTitleNode(node),
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
if ($isCollapsibleTitleNode(titleNode)) {
|
|
247
|
+
const container = titleNode.getParent<ElementNode>();
|
|
248
|
+
if (container && $isCollapsibleContainerNode(container)) {
|
|
249
|
+
if (!container.getOpen()) {
|
|
250
|
+
container.toggleOpen();
|
|
251
|
+
}
|
|
252
|
+
titleNode.getNextSibling()?.selectEnd();
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return false;
|
|
259
|
+
},
|
|
260
|
+
COMMAND_PRIORITY_LOW,
|
|
261
|
+
),
|
|
262
|
+
editor.registerCommand(
|
|
263
|
+
INSERT_COLLAPSIBLE_COMMAND,
|
|
264
|
+
() => {
|
|
265
|
+
editor.update(() => {
|
|
266
|
+
const title = $createCollapsibleTitleNode();
|
|
267
|
+
const paragraph = $createParagraphNode();
|
|
268
|
+
$insertNodeToNearestRoot(
|
|
269
|
+
$createCollapsibleContainerNode(true).append(
|
|
270
|
+
title.append(paragraph),
|
|
271
|
+
$createCollapsibleContentNode().append($createParagraphNode()),
|
|
272
|
+
),
|
|
273
|
+
);
|
|
274
|
+
paragraph.select();
|
|
275
|
+
});
|
|
276
|
+
return true;
|
|
277
|
+
},
|
|
278
|
+
COMMAND_PRIORITY_LOW,
|
|
279
|
+
),
|
|
280
|
+
);
|
|
281
|
+
}, [editor]);
|
|
282
|
+
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
@@ -0,0 +1,370 @@
|
|
|
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
|
+
|
|
9
|
+
import { $createCodeNode } from '@lexical/code';
|
|
10
|
+
import {
|
|
11
|
+
INSERT_CHECK_LIST_COMMAND,
|
|
12
|
+
INSERT_ORDERED_LIST_COMMAND,
|
|
13
|
+
INSERT_UNORDERED_LIST_COMMAND,
|
|
14
|
+
} from '@lexical/list';
|
|
15
|
+
import { INSERT_EMBED_COMMAND } from '@lexical/react/LexicalAutoEmbedPlugin';
|
|
16
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
17
|
+
import { INSERT_HORIZONTAL_RULE_COMMAND } from '@lexical/react/LexicalHorizontalRuleNode';
|
|
18
|
+
import {
|
|
19
|
+
LexicalTypeaheadMenuPlugin,
|
|
20
|
+
MenuOption,
|
|
21
|
+
useBasicTypeaheadTriggerMatch,
|
|
22
|
+
} from '@lexical/react/LexicalTypeaheadMenuPlugin';
|
|
23
|
+
import { $createHeadingNode, $createQuoteNode } from '@lexical/rich-text';
|
|
24
|
+
import { $setBlocksType } from '@lexical/selection';
|
|
25
|
+
import { INSERT_TABLE_COMMAND } from '@lexical/table';
|
|
26
|
+
import {
|
|
27
|
+
$createParagraphNode,
|
|
28
|
+
$getSelection,
|
|
29
|
+
$isRangeSelection,
|
|
30
|
+
FORMAT_ELEMENT_COMMAND,
|
|
31
|
+
LexicalEditor,
|
|
32
|
+
TextNode,
|
|
33
|
+
} from 'lexical';
|
|
34
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
35
|
+
import * as React from 'react';
|
|
36
|
+
import * as ReactDOM from 'react-dom';
|
|
37
|
+
|
|
38
|
+
// Core
|
|
39
|
+
import useContext from '@/client/context';
|
|
40
|
+
|
|
41
|
+
import { EmbedConfigs } from '../AutoEmbedPlugin';
|
|
42
|
+
import { INSERT_COLLAPSIBLE_COMMAND } from '../CollapsiblePlugin';
|
|
43
|
+
import { INSERT_IMAGE_COMMAND, InsertImageDialog } from '../ImagesPlugin';
|
|
44
|
+
import InsertLayoutDialog from '../LayoutPlugin/InsertLayoutDialog';
|
|
45
|
+
import { INSERT_PAGE_BREAK } from '../PageBreakPlugin';
|
|
46
|
+
import { InsertPollDialog } from '../PollPlugin';
|
|
47
|
+
import { InsertTableDialog } from '../TablePlugin';
|
|
48
|
+
|
|
49
|
+
class ComponentPickerOption extends MenuOption {
|
|
50
|
+
// What shows up in the editor
|
|
51
|
+
title: string;
|
|
52
|
+
// Icon for display
|
|
53
|
+
icon?: JSX.Element;
|
|
54
|
+
// For extra searching.
|
|
55
|
+
keywords: Array<string>;
|
|
56
|
+
// TBD
|
|
57
|
+
keyboardShortcut?: string;
|
|
58
|
+
// What happens when you select this option?
|
|
59
|
+
onSelect: (queryString: string) => void;
|
|
60
|
+
|
|
61
|
+
constructor(
|
|
62
|
+
title: string,
|
|
63
|
+
options: {
|
|
64
|
+
icon?: JSX.Element;
|
|
65
|
+
keywords?: Array<string>;
|
|
66
|
+
keyboardShortcut?: string;
|
|
67
|
+
onSelect: (queryString: string) => void;
|
|
68
|
+
},
|
|
69
|
+
) {
|
|
70
|
+
super(title);
|
|
71
|
+
this.title = title;
|
|
72
|
+
this.keywords = options.keywords || [];
|
|
73
|
+
this.icon = options.icon;
|
|
74
|
+
this.keyboardShortcut = options.keyboardShortcut;
|
|
75
|
+
this.onSelect = options.onSelect.bind(this);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function ComponentPickerMenuItem({
|
|
80
|
+
index,
|
|
81
|
+
isSelected,
|
|
82
|
+
onClick,
|
|
83
|
+
onMouseEnter,
|
|
84
|
+
option,
|
|
85
|
+
}: {
|
|
86
|
+
index: number;
|
|
87
|
+
isSelected: boolean;
|
|
88
|
+
onClick: () => void;
|
|
89
|
+
onMouseEnter: () => void;
|
|
90
|
+
option: ComponentPickerOption;
|
|
91
|
+
}) {
|
|
92
|
+
let className = 'item';
|
|
93
|
+
if (isSelected) {
|
|
94
|
+
className += ' selected';
|
|
95
|
+
}
|
|
96
|
+
return (
|
|
97
|
+
<li
|
|
98
|
+
key={option.key}
|
|
99
|
+
tabIndex={-1}
|
|
100
|
+
className={className}
|
|
101
|
+
ref={option.setRefElement}
|
|
102
|
+
role="option"
|
|
103
|
+
aria-selected={isSelected}
|
|
104
|
+
id={'typeahead-item-' + index}
|
|
105
|
+
onMouseEnter={onMouseEnter}
|
|
106
|
+
onClick={onClick}>
|
|
107
|
+
{option.icon}
|
|
108
|
+
<span className="text">{option.title}</span>
|
|
109
|
+
</li>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getDynamicOptions(editor: LexicalEditor, queryString: string) {
|
|
114
|
+
const options: Array<ComponentPickerOption> = [];
|
|
115
|
+
|
|
116
|
+
if (queryString == null) {
|
|
117
|
+
return options;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const tableMatch = queryString.match(/^([1-9]\d?)(?:x([1-9]\d?)?)?$/);
|
|
121
|
+
|
|
122
|
+
if (tableMatch !== null) {
|
|
123
|
+
const rows = tableMatch[1];
|
|
124
|
+
const colOptions = tableMatch[2]
|
|
125
|
+
? [tableMatch[2]]
|
|
126
|
+
: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(String);
|
|
127
|
+
|
|
128
|
+
options.push(
|
|
129
|
+
...colOptions.map(
|
|
130
|
+
(columns) =>
|
|
131
|
+
new ComponentPickerOption(`${rows}x${columns} Table`, {
|
|
132
|
+
icon: <i className="icon table" />,
|
|
133
|
+
keywords: ['table'],
|
|
134
|
+
onSelect: () =>
|
|
135
|
+
editor.dispatchCommand(INSERT_TABLE_COMMAND, { columns, rows }),
|
|
136
|
+
}),
|
|
137
|
+
),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return options;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function getBaseOptions(editor: LexicalEditor, { modal }: TContext) {
|
|
145
|
+
return [
|
|
146
|
+
new ComponentPickerOption('Paragraph', {
|
|
147
|
+
icon: <i className="icon paragraph" />,
|
|
148
|
+
keywords: ['normal', 'paragraph', 'p', 'text'],
|
|
149
|
+
onSelect: () =>
|
|
150
|
+
editor.update(() => {
|
|
151
|
+
const selection = $getSelection();
|
|
152
|
+
if ($isRangeSelection(selection)) {
|
|
153
|
+
$setBlocksType(selection, () => $createParagraphNode());
|
|
154
|
+
}
|
|
155
|
+
}),
|
|
156
|
+
}),
|
|
157
|
+
...([1, 2, 3] as const).map(
|
|
158
|
+
(n) =>
|
|
159
|
+
new ComponentPickerOption(`Heading ${n}`, {
|
|
160
|
+
icon: <i className={`icon h${n}`} />,
|
|
161
|
+
keywords: ['heading', 'header', `h${n}`],
|
|
162
|
+
onSelect: () =>
|
|
163
|
+
editor.update(() => {
|
|
164
|
+
const selection = $getSelection();
|
|
165
|
+
if ($isRangeSelection(selection)) {
|
|
166
|
+
$setBlocksType(selection, () => $createHeadingNode(`h${n}`));
|
|
167
|
+
}
|
|
168
|
+
}),
|
|
169
|
+
}),
|
|
170
|
+
),
|
|
171
|
+
new ComponentPickerOption('Table', {
|
|
172
|
+
icon: <i className="icon table" />,
|
|
173
|
+
keywords: ['table', 'grid', 'spreadsheet', 'rows', 'columns'],
|
|
174
|
+
onSelect: () => modal.show('Insert Table', InsertTableDialog, { editor }),
|
|
175
|
+
}),
|
|
176
|
+
new ComponentPickerOption('Numbered List', {
|
|
177
|
+
icon: <i className="icon number" />,
|
|
178
|
+
keywords: ['numbered list', 'ordered list', 'ol'],
|
|
179
|
+
onSelect: () =>
|
|
180
|
+
editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined),
|
|
181
|
+
}),
|
|
182
|
+
new ComponentPickerOption('Bulleted List', {
|
|
183
|
+
icon: <i className="icon bullet" />,
|
|
184
|
+
keywords: ['bulleted list', 'unordered list', 'ul'],
|
|
185
|
+
onSelect: () =>
|
|
186
|
+
editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined),
|
|
187
|
+
}),
|
|
188
|
+
new ComponentPickerOption('Check List', {
|
|
189
|
+
icon: <i className="icon check" />,
|
|
190
|
+
keywords: ['check list', 'todo list'],
|
|
191
|
+
onSelect: () =>
|
|
192
|
+
editor.dispatchCommand(INSERT_CHECK_LIST_COMMAND, undefined),
|
|
193
|
+
}),
|
|
194
|
+
new ComponentPickerOption('Quote', {
|
|
195
|
+
icon: <i className="icon quote" />,
|
|
196
|
+
keywords: ['block quote'],
|
|
197
|
+
onSelect: () =>
|
|
198
|
+
editor.update(() => {
|
|
199
|
+
const selection = $getSelection();
|
|
200
|
+
if ($isRangeSelection(selection)) {
|
|
201
|
+
$setBlocksType(selection, () => $createQuoteNode());
|
|
202
|
+
}
|
|
203
|
+
}),
|
|
204
|
+
}),
|
|
205
|
+
new ComponentPickerOption('Code', {
|
|
206
|
+
icon: <i className="icon code" />,
|
|
207
|
+
keywords: ['javascript', 'python', 'js', 'codeblock'],
|
|
208
|
+
onSelect: () =>
|
|
209
|
+
editor.update(() => {
|
|
210
|
+
const selection = $getSelection();
|
|
211
|
+
|
|
212
|
+
if ($isRangeSelection(selection)) {
|
|
213
|
+
if (selection.isCollapsed()) {
|
|
214
|
+
$setBlocksType(selection, () => $createCodeNode());
|
|
215
|
+
} else {
|
|
216
|
+
// Will this ever happen?
|
|
217
|
+
const textContent = selection.getTextContent();
|
|
218
|
+
const codeNode = $createCodeNode();
|
|
219
|
+
selection.insertNodes([codeNode]);
|
|
220
|
+
selection.insertRawText(textContent);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}),
|
|
224
|
+
}),
|
|
225
|
+
new ComponentPickerOption('Divider', {
|
|
226
|
+
icon: <i className="icon horizontal-rule" />,
|
|
227
|
+
keywords: ['horizontal rule', 'divider', 'hr'],
|
|
228
|
+
onSelect: () =>
|
|
229
|
+
editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined),
|
|
230
|
+
}),
|
|
231
|
+
new ComponentPickerOption('Page Break', {
|
|
232
|
+
icon: <i className="icon page-break" />,
|
|
233
|
+
keywords: ['page break', 'divider'],
|
|
234
|
+
onSelect: () => editor.dispatchCommand(INSERT_PAGE_BREAK, undefined),
|
|
235
|
+
}),
|
|
236
|
+
new ComponentPickerOption('Poll', {
|
|
237
|
+
icon: <i className="icon poll" />,
|
|
238
|
+
keywords: ['poll', 'vote'],
|
|
239
|
+
onSelect: () => modal.show('Insert Poll', InsertPollDialog, { editor }),
|
|
240
|
+
}),
|
|
241
|
+
...EmbedConfigs.map(
|
|
242
|
+
(embedConfig) =>
|
|
243
|
+
new ComponentPickerOption(`Embed ${embedConfig.contentName}`, {
|
|
244
|
+
icon: embedConfig.icon,
|
|
245
|
+
keywords: [...embedConfig.keywords, 'embed'],
|
|
246
|
+
onSelect: () =>
|
|
247
|
+
editor.dispatchCommand(INSERT_EMBED_COMMAND, embedConfig.type),
|
|
248
|
+
}),
|
|
249
|
+
),
|
|
250
|
+
// new ComponentPickerOption('Equation', {
|
|
251
|
+
// icon: <i className="icon equation" />,
|
|
252
|
+
// keywords: ['equation', 'latex', 'math'],
|
|
253
|
+
// onSelect: () =>
|
|
254
|
+
// showModal('Insert Equation', (onClose) => (
|
|
255
|
+
// <InsertEquationDialog activeEditor={editor} onClose={onClose} />
|
|
256
|
+
// )),
|
|
257
|
+
// }),
|
|
258
|
+
new ComponentPickerOption('Image', {
|
|
259
|
+
icon: <i className="icon image" />,
|
|
260
|
+
keywords: ['image', 'photo', 'picture', 'file'],
|
|
261
|
+
onSelect: () => modal.show('Insert Image', InsertImageDialog, { editor }),
|
|
262
|
+
}),
|
|
263
|
+
new ComponentPickerOption('Collapsible', {
|
|
264
|
+
icon: <i className="icon caret-right" />,
|
|
265
|
+
keywords: ['collapse', 'collapsible', 'toggle'],
|
|
266
|
+
onSelect: () => editor.dispatchCommand(INSERT_COLLAPSIBLE_COMMAND, undefined),
|
|
267
|
+
}),
|
|
268
|
+
new ComponentPickerOption('Columns Layout', {
|
|
269
|
+
icon: <i className="icon columns" />,
|
|
270
|
+
keywords: ['columns', 'layout', 'grid'],
|
|
271
|
+
onSelect: () => modal.show('Insert Columns Layout', InsertLayoutDialog, { editor }),
|
|
272
|
+
}),
|
|
273
|
+
...(['left', 'center', 'right', 'justify'] as const).map(
|
|
274
|
+
(alignment) =>
|
|
275
|
+
new ComponentPickerOption(`Align ${alignment}`, {
|
|
276
|
+
icon: <i className={`icon ${alignment}-align`} />,
|
|
277
|
+
keywords: ['align', 'justify', alignment],
|
|
278
|
+
onSelect: () => editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, alignment),
|
|
279
|
+
}),
|
|
280
|
+
),
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export default function ComponentPickerMenuPlugin(): JSX.Element {
|
|
285
|
+
|
|
286
|
+
const [editor] = useLexicalComposerContext();
|
|
287
|
+
const { modal, context } = useContext();
|
|
288
|
+
|
|
289
|
+
const [queryString, setQueryString] = useState<string | null>(null);
|
|
290
|
+
|
|
291
|
+
const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', {
|
|
292
|
+
minLength: 0,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const options = useMemo(() => {
|
|
296
|
+
|
|
297
|
+
const baseOptions = getBaseOptions(editor, context);
|
|
298
|
+
|
|
299
|
+
if (!queryString) {
|
|
300
|
+
return baseOptions;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const regex = new RegExp(queryString, 'i');
|
|
304
|
+
|
|
305
|
+
return [
|
|
306
|
+
...getDynamicOptions(editor, queryString),
|
|
307
|
+
...baseOptions.filter(
|
|
308
|
+
(option) =>
|
|
309
|
+
regex.test(option.title) ||
|
|
310
|
+
option.keywords.some((keyword) => regex.test(keyword)),
|
|
311
|
+
),
|
|
312
|
+
];
|
|
313
|
+
}, [editor, queryString]);
|
|
314
|
+
|
|
315
|
+
const onSelectOption = useCallback(
|
|
316
|
+
(
|
|
317
|
+
selectedOption: ComponentPickerOption,
|
|
318
|
+
nodeToRemove: TextNode | null,
|
|
319
|
+
closeMenu: () => void,
|
|
320
|
+
matchingString: string,
|
|
321
|
+
) => {
|
|
322
|
+
editor.update(() => {
|
|
323
|
+
nodeToRemove?.remove();
|
|
324
|
+
selectedOption.onSelect(matchingString);
|
|
325
|
+
closeMenu();
|
|
326
|
+
});
|
|
327
|
+
},
|
|
328
|
+
[editor],
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
return (
|
|
332
|
+
<>
|
|
333
|
+
<LexicalTypeaheadMenuPlugin<ComponentPickerOption>
|
|
334
|
+
onQueryChange={setQueryString}
|
|
335
|
+
onSelectOption={onSelectOption}
|
|
336
|
+
triggerFn={checkForTriggerMatch}
|
|
337
|
+
options={options}
|
|
338
|
+
menuRenderFn={(
|
|
339
|
+
anchorElementRef,
|
|
340
|
+
{ selectedIndex, selectOptionAndCleanUp, setHighlightedIndex },
|
|
341
|
+
) =>
|
|
342
|
+
anchorElementRef.current && options.length
|
|
343
|
+
? ReactDOM.createPortal(
|
|
344
|
+
<div className="typeahead-popover component-picker-menu">
|
|
345
|
+
<ul>
|
|
346
|
+
{options.map((option, i: number) => (
|
|
347
|
+
<ComponentPickerMenuItem
|
|
348
|
+
index={i}
|
|
349
|
+
isSelected={selectedIndex === i}
|
|
350
|
+
onClick={() => {
|
|
351
|
+
setHighlightedIndex(i);
|
|
352
|
+
selectOptionAndCleanUp(option);
|
|
353
|
+
}}
|
|
354
|
+
onMouseEnter={() => {
|
|
355
|
+
setHighlightedIndex(i);
|
|
356
|
+
}}
|
|
357
|
+
key={option.key}
|
|
358
|
+
option={option}
|
|
359
|
+
/>
|
|
360
|
+
))}
|
|
361
|
+
</ul>
|
|
362
|
+
</div>,
|
|
363
|
+
anchorElementRef.current,
|
|
364
|
+
)
|
|
365
|
+
: null
|
|
366
|
+
}
|
|
367
|
+
/>
|
|
368
|
+
</>
|
|
369
|
+
);
|
|
370
|
+
}
|