5htp-core 0.4.8 → 0.4.9
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/assets/css/components/table.less +2 -0
- package/src/client/components/Form.ts +1 -1
- 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 +42 -0
- 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 +63 -79
- 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/file/index.tsx +11 -5
- package/src/common/data/rte/nodes.ts +60 -9
- package/src/common/validation/index.ts +21 -2
- package/src/common/validation/schema.ts +42 -10
- package/src/common/validation/validator.ts +12 -4
- package/src/common/validation/validators.ts +82 -53
- package/src/server/services/router/http/multipart.ts +0 -1
- package/src/server/services/schema/index.ts +24 -2
- package/src/server/services/schema/request.ts +3 -2
- package/src/server/services/schema/rte.ts +110 -0
- package/src/{common/data/rte/index.ts → server/utils/rte.ts} +27 -16
- 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,119 @@
|
|
|
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 type {
|
|
10
|
+
EditorConfig,
|
|
11
|
+
EditorThemeClassName,
|
|
12
|
+
LexicalEditor,
|
|
13
|
+
NodeKey,
|
|
14
|
+
SerializedLexicalNode,
|
|
15
|
+
Spread,
|
|
16
|
+
} from 'lexical';
|
|
17
|
+
|
|
18
|
+
import {DecoratorNode} from 'lexical';
|
|
19
|
+
import * as React from 'react';
|
|
20
|
+
|
|
21
|
+
import {useSharedAutocompleteContext} from '../context/SharedAutocompleteContext';
|
|
22
|
+
import {uuid as UUID} from '../plugins/AutocompletePlugin';
|
|
23
|
+
|
|
24
|
+
declare global {
|
|
25
|
+
interface Navigator {
|
|
26
|
+
userAgentData?: {
|
|
27
|
+
mobile: boolean;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type SerializedAutocompleteNode = Spread<
|
|
33
|
+
{
|
|
34
|
+
uuid: string;
|
|
35
|
+
},
|
|
36
|
+
SerializedLexicalNode
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
export class AutocompleteNode extends DecoratorNode<JSX.Element | null> {
|
|
40
|
+
/**
|
|
41
|
+
* A unique uuid is generated for each session and assigned to the instance.
|
|
42
|
+
* This helps to:
|
|
43
|
+
* - Ensures max one Autocomplete node per session.
|
|
44
|
+
* - Ensure that when collaboration is enabled, this node is not shown in
|
|
45
|
+
* other sessions.
|
|
46
|
+
* See https://github.com/facebook/lexical/blob/master/packages/lexical-playground/src/plugins/AutocompletePlugin/index.tsx#L39
|
|
47
|
+
*/
|
|
48
|
+
__uuid: string;
|
|
49
|
+
|
|
50
|
+
static clone(node: AutocompleteNode): AutocompleteNode {
|
|
51
|
+
return new AutocompleteNode(node.__uuid, node.__key);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static getType(): 'autocomplete' {
|
|
55
|
+
return 'autocomplete';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static importJSON(
|
|
59
|
+
serializedNode: SerializedAutocompleteNode,
|
|
60
|
+
): AutocompleteNode {
|
|
61
|
+
const node = $createAutocompleteNode(serializedNode.uuid);
|
|
62
|
+
return node;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
exportJSON(): SerializedAutocompleteNode {
|
|
66
|
+
return {
|
|
67
|
+
...super.exportJSON(),
|
|
68
|
+
type: 'autocomplete',
|
|
69
|
+
uuid: this.__uuid,
|
|
70
|
+
version: 1,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
constructor(uuid: string, key?: NodeKey) {
|
|
75
|
+
super(key);
|
|
76
|
+
this.__uuid = uuid;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
updateDOM(
|
|
80
|
+
prevNode: unknown,
|
|
81
|
+
dom: HTMLElement,
|
|
82
|
+
config: EditorConfig,
|
|
83
|
+
): boolean {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
createDOM(config: EditorConfig): HTMLElement {
|
|
88
|
+
return document.createElement('span');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element | null {
|
|
92
|
+
if (this.__uuid !== UUID) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
return <AutocompleteComponent className={config.theme.autocomplete} />;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function $createAutocompleteNode(uuid: string): AutocompleteNode {
|
|
100
|
+
return new AutocompleteNode(uuid);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function AutocompleteComponent({
|
|
104
|
+
className,
|
|
105
|
+
}: {
|
|
106
|
+
className: EditorThemeClassName;
|
|
107
|
+
}): JSX.Element {
|
|
108
|
+
const [suggestion] = useSharedAutocompleteContext();
|
|
109
|
+
const userAgentData = window.navigator.userAgentData;
|
|
110
|
+
const isMobile =
|
|
111
|
+
userAgentData !== undefined
|
|
112
|
+
? userAgentData.mobile
|
|
113
|
+
: window.innerWidth <= 800 && window.innerHeight <= 600;
|
|
114
|
+
return (
|
|
115
|
+
<span className={className} spellCheck="false">
|
|
116
|
+
{suggestion} {isMobile ? '(SWIPE \u2B95)' : '(TAB)'}
|
|
117
|
+
</span>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
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 type {
|
|
10
|
+
EditorConfig,
|
|
11
|
+
LexicalNode,
|
|
12
|
+
NodeKey,
|
|
13
|
+
SerializedTextNode,
|
|
14
|
+
Spread,
|
|
15
|
+
} from 'lexical';
|
|
16
|
+
|
|
17
|
+
import {$applyNodeReplacement, TextNode} from 'lexical';
|
|
18
|
+
|
|
19
|
+
export type SerializedEmojiNode = Spread<
|
|
20
|
+
{
|
|
21
|
+
className: string;
|
|
22
|
+
},
|
|
23
|
+
SerializedTextNode
|
|
24
|
+
>;
|
|
25
|
+
|
|
26
|
+
export class EmojiNode extends TextNode {
|
|
27
|
+
__className: string;
|
|
28
|
+
|
|
29
|
+
static getType(): string {
|
|
30
|
+
return 'emoji';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static clone(node: EmojiNode): EmojiNode {
|
|
34
|
+
return new EmojiNode(node.__className, node.__text, node.__key);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
constructor(className: string, text: string, key?: NodeKey) {
|
|
38
|
+
super(text, key);
|
|
39
|
+
this.__className = className;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
createDOM(config: EditorConfig): HTMLElement {
|
|
43
|
+
const dom = document.createElement('span');
|
|
44
|
+
const inner = super.createDOM(config);
|
|
45
|
+
dom.className = this.__className;
|
|
46
|
+
inner.className = 'emoji-inner';
|
|
47
|
+
dom.appendChild(inner);
|
|
48
|
+
return dom;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
updateDOM(
|
|
52
|
+
prevNode: TextNode,
|
|
53
|
+
dom: HTMLElement,
|
|
54
|
+
config: EditorConfig,
|
|
55
|
+
): boolean {
|
|
56
|
+
const inner = dom.firstChild;
|
|
57
|
+
if (inner === null) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
super.updateDOM(prevNode, inner as HTMLElement, config);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static importJSON(serializedNode: SerializedEmojiNode): EmojiNode {
|
|
65
|
+
const node = $createEmojiNode(
|
|
66
|
+
serializedNode.className,
|
|
67
|
+
serializedNode.text,
|
|
68
|
+
);
|
|
69
|
+
node.setFormat(serializedNode.format);
|
|
70
|
+
node.setDetail(serializedNode.detail);
|
|
71
|
+
node.setMode(serializedNode.mode);
|
|
72
|
+
node.setStyle(serializedNode.style);
|
|
73
|
+
return node;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
exportJSON(): SerializedEmojiNode {
|
|
77
|
+
return {
|
|
78
|
+
...super.exportJSON(),
|
|
79
|
+
className: this.getClassName(),
|
|
80
|
+
type: 'emoji',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getClassName(): string {
|
|
85
|
+
const self = this.getLatest();
|
|
86
|
+
return self.__className;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function $isEmojiNode(
|
|
91
|
+
node: LexicalNode | null | undefined,
|
|
92
|
+
): node is EmojiNode {
|
|
93
|
+
return node instanceof EmojiNode;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function $createEmojiNode(
|
|
97
|
+
className: string,
|
|
98
|
+
emojiText: string,
|
|
99
|
+
): EmojiNode {
|
|
100
|
+
const node = new EmojiNode(className, emojiText).setMode('token');
|
|
101
|
+
return $applyNodeReplacement(node);
|
|
102
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
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 {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
|
|
10
|
+
import {useLexicalEditable} from '@lexical/react/useLexicalEditable';
|
|
11
|
+
import {mergeRegister} from '@lexical/utils';
|
|
12
|
+
import {
|
|
13
|
+
$getNodeByKey,
|
|
14
|
+
$getSelection,
|
|
15
|
+
$isNodeSelection,
|
|
16
|
+
COMMAND_PRIORITY_HIGH,
|
|
17
|
+
KEY_ESCAPE_COMMAND,
|
|
18
|
+
NodeKey,
|
|
19
|
+
SELECTION_CHANGE_COMMAND,
|
|
20
|
+
} from 'lexical';
|
|
21
|
+
import * as React from 'react';
|
|
22
|
+
import {useCallback, useEffect, useRef, useState} from 'react';
|
|
23
|
+
import {ErrorBoundary} from 'react-error-boundary';
|
|
24
|
+
|
|
25
|
+
import EquationEditor from '../ui/EquationEditor';
|
|
26
|
+
import KatexRenderer from '../ui/KatexRenderer';
|
|
27
|
+
import {$isEquationNode} from './EquationNode';
|
|
28
|
+
|
|
29
|
+
type EquationComponentProps = {
|
|
30
|
+
equation: string;
|
|
31
|
+
inline: boolean;
|
|
32
|
+
nodeKey: NodeKey;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default function EquationComponent({
|
|
36
|
+
equation,
|
|
37
|
+
inline,
|
|
38
|
+
nodeKey,
|
|
39
|
+
}: EquationComponentProps): JSX.Element {
|
|
40
|
+
const [editor] = useLexicalComposerContext();
|
|
41
|
+
const isEditable = useLexicalEditable();
|
|
42
|
+
const [equationValue, setEquationValue] = useState(equation);
|
|
43
|
+
const [showEquationEditor, setShowEquationEditor] = useState<boolean>(false);
|
|
44
|
+
const inputRef = useRef(null);
|
|
45
|
+
|
|
46
|
+
const onHide = useCallback(
|
|
47
|
+
(restoreSelection?: boolean) => {
|
|
48
|
+
setShowEquationEditor(false);
|
|
49
|
+
editor.update(() => {
|
|
50
|
+
const node = $getNodeByKey(nodeKey);
|
|
51
|
+
if ($isEquationNode(node)) {
|
|
52
|
+
node.setEquation(equationValue);
|
|
53
|
+
if (restoreSelection) {
|
|
54
|
+
node.selectNext(0, 0);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
[editor, equationValue, nodeKey],
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (!showEquationEditor && equationValue !== equation) {
|
|
64
|
+
setEquationValue(equation);
|
|
65
|
+
}
|
|
66
|
+
}, [showEquationEditor, equation, equationValue]);
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (!isEditable) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (showEquationEditor) {
|
|
73
|
+
return mergeRegister(
|
|
74
|
+
editor.registerCommand(
|
|
75
|
+
SELECTION_CHANGE_COMMAND,
|
|
76
|
+
(payload) => {
|
|
77
|
+
const activeElement = document.activeElement;
|
|
78
|
+
const inputElem = inputRef.current;
|
|
79
|
+
if (inputElem !== activeElement) {
|
|
80
|
+
onHide();
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
},
|
|
84
|
+
COMMAND_PRIORITY_HIGH,
|
|
85
|
+
),
|
|
86
|
+
editor.registerCommand(
|
|
87
|
+
KEY_ESCAPE_COMMAND,
|
|
88
|
+
(payload) => {
|
|
89
|
+
const activeElement = document.activeElement;
|
|
90
|
+
const inputElem = inputRef.current;
|
|
91
|
+
if (inputElem === activeElement) {
|
|
92
|
+
onHide(true);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
},
|
|
97
|
+
COMMAND_PRIORITY_HIGH,
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
} else {
|
|
101
|
+
return editor.registerUpdateListener(({editorState}) => {
|
|
102
|
+
const isSelected = editorState.read(() => {
|
|
103
|
+
const selection = $getSelection();
|
|
104
|
+
return (
|
|
105
|
+
$isNodeSelection(selection) &&
|
|
106
|
+
selection.has(nodeKey) &&
|
|
107
|
+
selection.getNodes().length === 1
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
if (isSelected) {
|
|
111
|
+
setShowEquationEditor(true);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}, [editor, nodeKey, onHide, showEquationEditor, isEditable]);
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<>
|
|
119
|
+
{showEquationEditor && isEditable ? (
|
|
120
|
+
<EquationEditor
|
|
121
|
+
equation={equationValue}
|
|
122
|
+
setEquation={setEquationValue}
|
|
123
|
+
inline={inline}
|
|
124
|
+
ref={inputRef}
|
|
125
|
+
/>
|
|
126
|
+
) : (
|
|
127
|
+
<ErrorBoundary onError={(e) => editor._onError(e)} fallback={null}>
|
|
128
|
+
<KatexRenderer
|
|
129
|
+
equation={equationValue}
|
|
130
|
+
inline={inline}
|
|
131
|
+
onDoubleClick={() => {
|
|
132
|
+
if (isEditable) {
|
|
133
|
+
setShowEquationEditor(true);
|
|
134
|
+
}
|
|
135
|
+
}}
|
|
136
|
+
/>
|
|
137
|
+
</ErrorBoundary>
|
|
138
|
+
)}
|
|
139
|
+
</>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
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 type {
|
|
10
|
+
DOMConversionMap,
|
|
11
|
+
DOMConversionOutput,
|
|
12
|
+
EditorConfig,
|
|
13
|
+
LexicalNode,
|
|
14
|
+
NodeKey,
|
|
15
|
+
SerializedLexicalNode,
|
|
16
|
+
Spread,
|
|
17
|
+
} from 'lexical';
|
|
18
|
+
|
|
19
|
+
import katex from 'katex';
|
|
20
|
+
import {$applyNodeReplacement, DecoratorNode, DOMExportOutput} from 'lexical';
|
|
21
|
+
import * as React from 'react';
|
|
22
|
+
import {Suspense} from 'react';
|
|
23
|
+
|
|
24
|
+
const EquationComponent = React.lazy(() => import('./EquationComponent'));
|
|
25
|
+
|
|
26
|
+
export type SerializedEquationNode = Spread<
|
|
27
|
+
{
|
|
28
|
+
equation: string;
|
|
29
|
+
inline: boolean;
|
|
30
|
+
},
|
|
31
|
+
SerializedLexicalNode
|
|
32
|
+
>;
|
|
33
|
+
|
|
34
|
+
function $convertEquationElement(
|
|
35
|
+
domNode: HTMLElement,
|
|
36
|
+
): null | DOMConversionOutput {
|
|
37
|
+
let equation = domNode.getAttribute('data-lexical-equation');
|
|
38
|
+
const inline = domNode.getAttribute('data-lexical-inline') === 'true';
|
|
39
|
+
// Decode the equation from base64
|
|
40
|
+
equation = atob(equation || '');
|
|
41
|
+
if (equation) {
|
|
42
|
+
const node = $createEquationNode(equation, inline);
|
|
43
|
+
return {node};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class EquationNode extends DecoratorNode<JSX.Element> {
|
|
50
|
+
__equation: string;
|
|
51
|
+
__inline: boolean;
|
|
52
|
+
|
|
53
|
+
static getType(): string {
|
|
54
|
+
return 'equation';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static clone(node: EquationNode): EquationNode {
|
|
58
|
+
return new EquationNode(node.__equation, node.__inline, node.__key);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
constructor(equation: string, inline?: boolean, key?: NodeKey) {
|
|
62
|
+
super(key);
|
|
63
|
+
this.__equation = equation;
|
|
64
|
+
this.__inline = inline ?? false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static importJSON(serializedNode: SerializedEquationNode): EquationNode {
|
|
68
|
+
const node = $createEquationNode(
|
|
69
|
+
serializedNode.equation,
|
|
70
|
+
serializedNode.inline,
|
|
71
|
+
);
|
|
72
|
+
return node;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
exportJSON(): SerializedEquationNode {
|
|
76
|
+
return {
|
|
77
|
+
equation: this.getEquation(),
|
|
78
|
+
inline: this.__inline,
|
|
79
|
+
type: 'equation',
|
|
80
|
+
version: 1,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
createDOM(_config: EditorConfig): HTMLElement {
|
|
85
|
+
const element = document.createElement(this.__inline ? 'span' : 'div');
|
|
86
|
+
// EquationNodes should implement `user-action:none` in their CSS to avoid issues with deletion on Android.
|
|
87
|
+
element.className = 'editor-equation';
|
|
88
|
+
return element;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
exportDOM(): DOMExportOutput {
|
|
92
|
+
const element = document.createElement(this.__inline ? 'span' : 'div');
|
|
93
|
+
// Encode the equation as base64 to avoid issues with special characters
|
|
94
|
+
const equation = btoa(this.__equation);
|
|
95
|
+
element.setAttribute('data-lexical-equation', equation);
|
|
96
|
+
element.setAttribute('data-lexical-inline', `${this.__inline}`);
|
|
97
|
+
katex.render(this.__equation, element, {
|
|
98
|
+
displayMode: !this.__inline, // true === block display //
|
|
99
|
+
errorColor: '#cc0000',
|
|
100
|
+
output: 'html',
|
|
101
|
+
strict: 'warn',
|
|
102
|
+
throwOnError: false,
|
|
103
|
+
trust: false,
|
|
104
|
+
});
|
|
105
|
+
return {element};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
static importDOM(): DOMConversionMap | null {
|
|
109
|
+
return {
|
|
110
|
+
div: (domNode: HTMLElement) => {
|
|
111
|
+
if (!domNode.hasAttribute('data-lexical-equation')) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
conversion: $convertEquationElement,
|
|
116
|
+
priority: 2,
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
span: (domNode: HTMLElement) => {
|
|
120
|
+
if (!domNode.hasAttribute('data-lexical-equation')) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
conversion: $convertEquationElement,
|
|
125
|
+
priority: 1,
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
updateDOM(prevNode: EquationNode): boolean {
|
|
132
|
+
// If the inline property changes, replace the element
|
|
133
|
+
return this.__inline !== prevNode.__inline;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getTextContent(): string {
|
|
137
|
+
return this.__equation;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getEquation(): string {
|
|
141
|
+
return this.__equation;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
setEquation(equation: string): void {
|
|
145
|
+
const writable = this.getWritable();
|
|
146
|
+
writable.__equation = equation;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
decorate(): JSX.Element {
|
|
150
|
+
return (
|
|
151
|
+
<Suspense fallback={null}>
|
|
152
|
+
<EquationComponent
|
|
153
|
+
equation={this.__equation}
|
|
154
|
+
inline={this.__inline}
|
|
155
|
+
nodeKey={this.__key}
|
|
156
|
+
/>
|
|
157
|
+
</Suspense>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function $createEquationNode(
|
|
163
|
+
equation = '',
|
|
164
|
+
inline = false,
|
|
165
|
+
): EquationNode {
|
|
166
|
+
const equationNode = new EquationNode(equation, inline);
|
|
167
|
+
return $applyNodeReplacement(equationNode);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function $isEquationNode(
|
|
171
|
+
node: LexicalNode | null | undefined,
|
|
172
|
+
): node is EquationNode {
|
|
173
|
+
return node instanceof EquationNode;
|
|
174
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
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 type {
|
|
10
|
+
EditorConfig,
|
|
11
|
+
ElementFormatType,
|
|
12
|
+
LexicalEditor,
|
|
13
|
+
LexicalNode,
|
|
14
|
+
NodeKey,
|
|
15
|
+
Spread,
|
|
16
|
+
} from 'lexical';
|
|
17
|
+
|
|
18
|
+
import {BlockWithAlignableContents} from '@lexical/react/LexicalBlockWithAlignableContents';
|
|
19
|
+
import {
|
|
20
|
+
DecoratorBlockNode,
|
|
21
|
+
SerializedDecoratorBlockNode,
|
|
22
|
+
} from '@lexical/react/LexicalDecoratorBlockNode';
|
|
23
|
+
import * as React from 'react';
|
|
24
|
+
|
|
25
|
+
type FigmaComponentProps = Readonly<{
|
|
26
|
+
className: Readonly<{
|
|
27
|
+
base: string;
|
|
28
|
+
focus: string;
|
|
29
|
+
}>;
|
|
30
|
+
format: ElementFormatType | null;
|
|
31
|
+
nodeKey: NodeKey;
|
|
32
|
+
documentID: string;
|
|
33
|
+
}>;
|
|
34
|
+
|
|
35
|
+
function FigmaComponent({
|
|
36
|
+
className,
|
|
37
|
+
format,
|
|
38
|
+
nodeKey,
|
|
39
|
+
documentID,
|
|
40
|
+
}: FigmaComponentProps) {
|
|
41
|
+
return (
|
|
42
|
+
<BlockWithAlignableContents
|
|
43
|
+
className={className}
|
|
44
|
+
format={format}
|
|
45
|
+
nodeKey={nodeKey}>
|
|
46
|
+
<iframe
|
|
47
|
+
width="560"
|
|
48
|
+
height="315"
|
|
49
|
+
src={`https://www.figma.com/embed?embed_host=lexical&url=\
|
|
50
|
+
https://www.figma.com/file/${documentID}`}
|
|
51
|
+
allowFullScreen={true}
|
|
52
|
+
/>
|
|
53
|
+
</BlockWithAlignableContents>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type SerializedFigmaNode = Spread<
|
|
58
|
+
{
|
|
59
|
+
documentID: string;
|
|
60
|
+
},
|
|
61
|
+
SerializedDecoratorBlockNode
|
|
62
|
+
>;
|
|
63
|
+
|
|
64
|
+
export class FigmaNode extends DecoratorBlockNode {
|
|
65
|
+
__id: string;
|
|
66
|
+
|
|
67
|
+
static getType(): string {
|
|
68
|
+
return 'figma';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static clone(node: FigmaNode): FigmaNode {
|
|
72
|
+
return new FigmaNode(node.__id, node.__format, node.__key);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static importJSON(serializedNode: SerializedFigmaNode): FigmaNode {
|
|
76
|
+
const node = $createFigmaNode(serializedNode.documentID);
|
|
77
|
+
node.setFormat(serializedNode.format);
|
|
78
|
+
return node;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
exportJSON(): SerializedFigmaNode {
|
|
82
|
+
return {
|
|
83
|
+
...super.exportJSON(),
|
|
84
|
+
documentID: this.__id,
|
|
85
|
+
type: 'figma',
|
|
86
|
+
version: 1,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
constructor(id: string, format?: ElementFormatType, key?: NodeKey) {
|
|
91
|
+
super(format, key);
|
|
92
|
+
this.__id = id;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
updateDOM(): false {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getId(): string {
|
|
100
|
+
return this.__id;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getTextContent(
|
|
104
|
+
_includeInert?: boolean | undefined,
|
|
105
|
+
_includeDirectionless?: false | undefined,
|
|
106
|
+
): string {
|
|
107
|
+
return `https://www.figma.com/file/${this.__id}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
decorate(_editor: LexicalEditor, config: EditorConfig): JSX.Element {
|
|
111
|
+
const embedBlockTheme = config.theme.embedBlock || {};
|
|
112
|
+
const className = {
|
|
113
|
+
base: embedBlockTheme.base || '',
|
|
114
|
+
focus: embedBlockTheme.focus || '',
|
|
115
|
+
};
|
|
116
|
+
return (
|
|
117
|
+
<FigmaComponent
|
|
118
|
+
className={className}
|
|
119
|
+
format={this.__format}
|
|
120
|
+
nodeKey={this.getKey()}
|
|
121
|
+
documentID={this.__id}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function $createFigmaNode(documentID: string): FigmaNode {
|
|
128
|
+
return new FigmaNode(documentID);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function $isFigmaNode(
|
|
132
|
+
node: FigmaNode | LexicalNode | null | undefined,
|
|
133
|
+
): node is FigmaNode {
|
|
134
|
+
return node instanceof FigmaNode;
|
|
135
|
+
}
|