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.
Files changed (187) hide show
  1. package/package.json +5 -1
  2. package/src/client/assets/css/components/table.less +2 -0
  3. package/src/client/components/Form.ts +1 -1
  4. package/src/client/components/button.tsx +2 -1
  5. package/src/client/components/containers/Popover/index.tsx +2 -2
  6. package/src/client/components/dropdown/index.tsx +16 -6
  7. package/src/client/components/input/Slider/index.tsx +0 -2
  8. package/src/client/components/inputv3/Rte/Editor.tsx +271 -0
  9. package/src/client/components/inputv3/Rte/ToolbarPlugin/BlockFormat.tsx +220 -0
  10. package/src/client/components/inputv3/Rte/ToolbarPlugin/ElementFormat.tsx +107 -0
  11. package/src/client/components/inputv3/Rte/ToolbarPlugin/index.tsx +768 -0
  12. package/src/client/components/inputv3/Rte/appSettings.ts +36 -0
  13. package/src/client/components/inputv3/Rte/context/FlashMessageContext.tsx +68 -0
  14. package/src/client/components/inputv3/Rte/context/SettingsContext.tsx +71 -0
  15. package/src/client/components/inputv3/Rte/context/SharedAutocompleteContext.tsx +71 -0
  16. package/src/client/components/inputv3/Rte/context/SharedHistoryContext.tsx +35 -0
  17. package/src/client/components/inputv3/Rte/currentEditor.ts +42 -0
  18. package/src/client/components/inputv3/Rte/hooks/useFlashMessage.tsx +16 -0
  19. package/src/client/components/inputv3/Rte/hooks/useReport.ts +67 -0
  20. package/src/client/components/inputv3/Rte/images/emoji/1F600.png +0 -0
  21. package/src/client/components/inputv3/Rte/images/emoji/1F641.png +0 -0
  22. package/src/client/components/inputv3/Rte/images/emoji/1F642.png +0 -0
  23. package/src/client/components/inputv3/Rte/images/emoji/2764.png +0 -0
  24. package/src/client/components/inputv3/Rte/images/emoji/LICENSE.md +5 -0
  25. package/src/client/components/inputv3/Rte/images/icons/draggable-block-menu.svg +1 -0
  26. package/src/client/components/inputv3/Rte/images/icons/prettier-error.svg +1 -0
  27. package/src/client/components/inputv3/Rte/images/icons/prettier.svg +1 -0
  28. package/src/client/components/inputv3/Rte/images/image/LICENSE.md +5 -0
  29. package/src/client/components/inputv3/Rte/images/image-broken.svg +4 -0
  30. package/src/client/components/inputv3/Rte/images/logo.svg +1 -0
  31. package/src/client/components/inputv3/Rte/index.tsx +63 -79
  32. package/src/client/components/inputv3/Rte/nodes/AutocompleteNode.tsx +119 -0
  33. package/src/client/components/inputv3/Rte/nodes/EmojiNode.tsx +102 -0
  34. package/src/client/components/inputv3/Rte/nodes/EquationComponent.tsx +141 -0
  35. package/src/client/components/inputv3/Rte/nodes/EquationNode.tsx +174 -0
  36. package/src/client/components/inputv3/Rte/nodes/FigmaNode.tsx +135 -0
  37. package/src/client/components/inputv3/Rte/nodes/ImageComponent.tsx +468 -0
  38. package/src/client/components/inputv3/Rte/nodes/ImageNode.css +43 -0
  39. package/src/client/components/inputv3/Rte/nodes/ImageNode.tsx +266 -0
  40. package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageComponent.tsx +402 -0
  41. package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageNode.css +94 -0
  42. package/src/client/components/inputv3/Rte/nodes/InlineImageNode/InlineImageNode.tsx +294 -0
  43. package/src/client/components/inputv3/Rte/nodes/KeywordNode.ts +67 -0
  44. package/src/client/components/inputv3/Rte/nodes/LayoutContainerNode.ts +137 -0
  45. package/src/client/components/inputv3/Rte/nodes/LayoutItemNode.ts +71 -0
  46. package/src/client/components/inputv3/Rte/nodes/MentionNode.ts +130 -0
  47. package/src/client/components/inputv3/Rte/nodes/PageBreakNode/index.css +62 -0
  48. package/src/client/components/inputv3/Rte/nodes/PageBreakNode/index.tsx +170 -0
  49. package/src/client/components/inputv3/Rte/nodes/PlaygroundNodes.ts +76 -0
  50. package/src/client/components/inputv3/Rte/nodes/PollComponent.tsx +249 -0
  51. package/src/client/components/inputv3/Rte/nodes/PollNode.css +187 -0
  52. package/src/client/components/inputv3/Rte/nodes/PollNode.tsx +209 -0
  53. package/src/client/components/inputv3/Rte/nodes/StickyComponent.tsx +261 -0
  54. package/src/client/components/inputv3/Rte/nodes/StickyNode.css +37 -0
  55. package/src/client/components/inputv3/Rte/nodes/StickyNode.tsx +150 -0
  56. package/src/client/components/inputv3/Rte/nodes/TweetNode.tsx +223 -0
  57. package/src/client/components/inputv3/Rte/nodes/YouTubeNode.tsx +184 -0
  58. package/src/client/components/inputv3/Rte/plugins/ActionsPlugin/index.tsx +334 -0
  59. package/src/client/components/inputv3/Rte/plugins/AutoEmbedPlugin/index.tsx +352 -0
  60. package/src/client/components/inputv3/Rte/plugins/AutoLinkPlugin/index.tsx +32 -0
  61. package/src/client/components/inputv3/Rte/plugins/AutocompletePlugin/index.tsx +2529 -0
  62. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/CopyButton/index.tsx +70 -0
  63. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/PrettierButton/index.css +14 -0
  64. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/components/PrettierButton/index.tsx +156 -0
  65. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/index.css +54 -0
  66. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/index.tsx +190 -0
  67. package/src/client/components/inputv3/Rte/plugins/CodeActionMenuPlugin/utils.ts +33 -0
  68. package/src/client/components/inputv3/Rte/plugins/CodeHighlightPlugin/index.ts +21 -0
  69. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/Collapsible.css +57 -0
  70. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleContainerNode.ts +168 -0
  71. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleContentNode.ts +127 -0
  72. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleTitleNode.ts +152 -0
  73. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/CollapsibleUtils.ts +17 -0
  74. package/src/client/components/inputv3/Rte/plugins/CollapsiblePlugin/index.ts +284 -0
  75. package/src/client/components/inputv3/Rte/plugins/ComponentPickerPlugin/index.tsx +370 -0
  76. package/src/client/components/inputv3/Rte/plugins/ContextMenuPlugin/index.tsx +270 -0
  77. package/src/client/components/inputv3/Rte/plugins/DocsPlugin/index.tsx +20 -0
  78. package/src/client/components/inputv3/Rte/plugins/DragDropPastePlugin/index.ts +51 -0
  79. package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.css +36 -0
  80. package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.tsx +43 -0
  81. package/src/client/components/inputv3/Rte/plugins/EmojiPickerPlugin/index.tsx +198 -0
  82. package/src/client/components/inputv3/Rte/plugins/EmojisPlugin/index.ts +75 -0
  83. package/src/client/components/inputv3/Rte/plugins/EquationsPlugin/index.tsx +82 -0
  84. package/src/client/components/inputv3/Rte/plugins/FigmaPlugin/index.tsx +40 -0
  85. package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.css +41 -0
  86. package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.tsx +393 -0
  87. package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.css +141 -0
  88. package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.tsx +388 -0
  89. package/src/client/components/inputv3/Rte/plugins/ImagesPlugin/index.tsx +350 -0
  90. package/src/client/components/inputv3/Rte/plugins/InlineImagePlugin/index.tsx +336 -0
  91. package/src/client/components/inputv3/Rte/plugins/KeywordsPlugin/index.ts +56 -0
  92. package/src/client/components/inputv3/Rte/plugins/LayoutPlugin/InsertLayoutDialog.tsx +58 -0
  93. package/src/client/components/inputv3/Rte/plugins/LayoutPlugin/LayoutPlugin.tsx +219 -0
  94. package/src/client/components/inputv3/Rte/plugins/LinkPlugin/index.tsx +34 -0
  95. package/src/client/components/inputv3/Rte/plugins/ListMaxIndentLevelPlugin/index.ts +85 -0
  96. package/src/client/components/inputv3/Rte/plugins/MarkdownShortcutPlugin/index.tsx +16 -0
  97. package/src/client/components/inputv3/Rte/plugins/MarkdownTransformers/index.ts +324 -0
  98. package/src/client/components/inputv3/Rte/plugins/MaxLengthPlugin/index.tsx +53 -0
  99. package/src/client/components/inputv3/Rte/plugins/MentionsPlugin/index.tsx +696 -0
  100. package/src/client/components/inputv3/Rte/plugins/PageBreakPlugin/index.tsx +57 -0
  101. package/src/client/components/inputv3/Rte/plugins/PasteLogPlugin/index.tsx +54 -0
  102. package/src/client/components/inputv3/Rte/plugins/PollPlugin/index.tsx +86 -0
  103. package/src/client/components/inputv3/Rte/plugins/SpeechToTextPlugin/index.ts +125 -0
  104. package/src/client/components/inputv3/Rte/plugins/StickyPlugin/index.ts +22 -0
  105. package/src/client/components/inputv3/Rte/plugins/TabFocusPlugin/index.tsx +65 -0
  106. package/src/client/components/inputv3/Rte/plugins/TableActionMenuPlugin/index.tsx +773 -0
  107. package/src/client/components/inputv3/Rte/plugins/TableCellResizer/index.css +12 -0
  108. package/src/client/components/inputv3/Rte/plugins/TableCellResizer/index.tsx +436 -0
  109. package/src/client/components/inputv3/Rte/plugins/TableHoverActionsPlugin/index.tsx +287 -0
  110. package/src/client/components/inputv3/Rte/plugins/TableOfContentsPlugin/index.css +95 -0
  111. package/src/client/components/inputv3/Rte/plugins/TableOfContentsPlugin/index.tsx +197 -0
  112. package/src/client/components/inputv3/Rte/plugins/TablePlugin.tsx +178 -0
  113. package/src/client/components/inputv3/Rte/plugins/TestRecorderPlugin/index.tsx +468 -0
  114. package/src/client/components/inputv3/Rte/plugins/TreeViewPlugin/index.tsx +26 -0
  115. package/src/client/components/inputv3/Rte/plugins/TwitterPlugin/index.ts +41 -0
  116. package/src/client/components/inputv3/Rte/plugins/TypingPerfPlugin/index.ts +117 -0
  117. package/src/client/components/inputv3/Rte/plugins/YouTubePlugin/index.ts +41 -0
  118. package/src/client/components/inputv3/Rte/shared/canUseDOM.ts +4 -0
  119. package/src/client/components/inputv3/Rte/shared/caretFromPoint.ts +40 -0
  120. package/src/client/components/inputv3/Rte/shared/environment.ts +56 -0
  121. package/src/client/components/inputv3/Rte/shared/invariant.ts +26 -0
  122. package/src/client/components/inputv3/Rte/shared/normalizeClassNames.ts +21 -0
  123. package/src/client/components/inputv3/Rte/shared/react-test-utils.ts +18 -0
  124. package/src/client/components/inputv3/Rte/shared/reactPatches.ts +22 -0
  125. package/src/client/components/inputv3/Rte/shared/simpleDiffWithCursor.ts +49 -0
  126. package/src/client/components/inputv3/Rte/shared/useLayoutEffect.ts +19 -0
  127. package/src/client/components/inputv3/Rte/shared/warnOnlyOnce.ts +20 -0
  128. package/src/client/components/inputv3/Rte/style.less +30 -60
  129. package/src/client/components/inputv3/Rte/themes/CommentEditorTheme.css +13 -0
  130. package/src/client/components/inputv3/Rte/themes/CommentEditorTheme.ts +20 -0
  131. package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.css +447 -0
  132. package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.ts +120 -0
  133. package/src/client/components/inputv3/Rte/themes/StickyEditorTheme.css +13 -0
  134. package/src/client/components/inputv3/Rte/themes/StickyEditorTheme.ts +20 -0
  135. package/src/client/components/inputv3/Rte/ui/ColorPicker.css +88 -0
  136. package/src/client/components/inputv3/Rte/ui/ColorPicker.tsx +365 -0
  137. package/src/client/components/inputv3/Rte/ui/ContentEditable.css +44 -0
  138. package/src/client/components/inputv3/Rte/ui/ContentEditable.tsx +36 -0
  139. package/src/client/components/inputv3/Rte/ui/DropDown.tsx +259 -0
  140. package/src/client/components/inputv3/Rte/ui/DropdownColorPicker.tsx +41 -0
  141. package/src/client/components/inputv3/Rte/ui/EquationEditor.css +38 -0
  142. package/src/client/components/inputv3/Rte/ui/EquationEditor.tsx +56 -0
  143. package/src/client/components/inputv3/Rte/ui/FileInput.tsx +38 -0
  144. package/src/client/components/inputv3/Rte/ui/FlashMessage.css +28 -0
  145. package/src/client/components/inputv3/Rte/ui/FlashMessage.tsx +29 -0
  146. package/src/client/components/inputv3/Rte/ui/ImageResizer.tsx +316 -0
  147. package/src/client/components/inputv3/Rte/ui/Input.css +32 -0
  148. package/src/client/components/inputv3/Rte/ui/KatexRenderer.tsx +54 -0
  149. package/src/client/components/inputv3/Rte/ui/Switch.tsx +36 -0
  150. package/src/client/components/inputv3/Rte/utils/docSerialization.ts +77 -0
  151. package/src/client/components/inputv3/Rte/utils/emoji-list.ts +16615 -0
  152. package/src/client/components/inputv3/Rte/utils/getDOMRangeRect.ts +27 -0
  153. package/src/client/components/inputv3/Rte/utils/getSelectedNode.ts +27 -0
  154. package/src/client/components/inputv3/Rte/utils/guard.ts +10 -0
  155. package/src/client/components/inputv3/Rte/utils/isMobileWidth.ts +7 -0
  156. package/src/client/components/inputv3/Rte/utils/joinClasses.ts +13 -0
  157. package/src/client/components/inputv3/Rte/utils/setFloatingElemPosition.ts +51 -0
  158. package/src/client/components/inputv3/Rte/utils/setFloatingElemPositionForLinkEditor.ts +46 -0
  159. package/src/client/components/inputv3/Rte/utils/swipe.ts +127 -0
  160. package/src/client/components/inputv3/Rte/utils/url.ts +38 -0
  161. package/src/client/components/inputv3/base.tsx +8 -5
  162. package/src/client/components/inputv3/file/index.tsx +11 -5
  163. package/src/common/data/rte/nodes.ts +60 -9
  164. package/src/common/validation/index.ts +21 -2
  165. package/src/common/validation/schema.ts +42 -10
  166. package/src/common/validation/validator.ts +12 -4
  167. package/src/common/validation/validators.ts +82 -53
  168. package/src/server/services/router/http/multipart.ts +0 -1
  169. package/src/server/services/schema/index.ts +24 -2
  170. package/src/server/services/schema/request.ts +3 -2
  171. package/src/server/services/schema/rte.ts +110 -0
  172. package/src/{common/data/rte/index.ts → server/utils/rte.ts} +27 -16
  173. package/src/client/components/inputv3/Rte/ExampleTheme.tsx +0 -42
  174. package/src/client/components/inputv3/Rte/ToolbarPlugin.tsx +0 -167
  175. package/src/client/components/inputv3/Rte/icons/LICENSE.md +0 -5
  176. package/src/client/components/inputv3/Rte/icons/arrow-clockwise.svg +0 -4
  177. package/src/client/components/inputv3/Rte/icons/arrow-counterclockwise.svg +0 -4
  178. package/src/client/components/inputv3/Rte/icons/journal-text.svg +0 -5
  179. package/src/client/components/inputv3/Rte/icons/justify.svg +0 -3
  180. package/src/client/components/inputv3/Rte/icons/text-center.svg +0 -3
  181. package/src/client/components/inputv3/Rte/icons/text-left.svg +0 -3
  182. package/src/client/components/inputv3/Rte/icons/text-paragraph.svg +0 -3
  183. package/src/client/components/inputv3/Rte/icons/text-right.svg +0 -3
  184. package/src/client/components/inputv3/Rte/icons/type-bold.svg +0 -3
  185. package/src/client/components/inputv3/Rte/icons/type-italic.svg +0 -3
  186. package/src/client/components/inputv3/Rte/icons/type-strikethrough.svg +0 -3
  187. 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
+ }