@collabchron/notiq 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/README.md +71 -0
  2. package/components.json +21 -0
  3. package/eslint.config.mjs +16 -0
  4. package/next.config.ts +12 -0
  5. package/package.json +108 -0
  6. package/postcss.config.mjs +5 -0
  7. package/public/file.svg +1 -0
  8. package/public/globe.svg +1 -0
  9. package/public/images/icons/plus.svg +10 -0
  10. package/public/next.svg +1 -0
  11. package/public/vercel.svg +1 -0
  12. package/public/window.svg +1 -0
  13. package/src/app/actions.ts +2 -0
  14. package/src/app/api/ai/route.ts +175 -0
  15. package/src/app/api/edgestore/[...edgestore]/route.ts +28 -0
  16. package/src/app/favicon.ico +0 -0
  17. package/src/app/globals.css +205 -0
  18. package/src/app/layout.tsx +38 -0
  19. package/src/app/page.tsx +12 -0
  20. package/src/components/editor/Core.tsx +220 -0
  21. package/src/components/editor/hooks/instructions-messages.ts +300 -0
  22. package/src/components/editor/hooks/use-mobile.ts +19 -0
  23. package/src/components/editor/hooks/useReport.ts +67 -0
  24. package/src/components/editor/hooks/useResizeObservert.ts +22 -0
  25. package/src/components/editor/index.tsx +39 -0
  26. package/src/components/editor/lexical-on-change.tsx +28 -0
  27. package/src/components/editor/nodes/CollapsibleNode/CollapsibleContainerNode.ts +92 -0
  28. package/src/components/editor/nodes/CollapsibleNode/CollapsibleContentNode.ts +65 -0
  29. package/src/components/editor/nodes/CollapsibleNode/CollapsibleTitleNode.ts +105 -0
  30. package/src/components/editor/nodes/EquationNode/EquationComponent.tsx +143 -0
  31. package/src/components/editor/nodes/EquationNode/EquationNode.tsx +170 -0
  32. package/src/components/editor/nodes/ExcalidrawNode/ExcalidrawComponent.tsx +228 -0
  33. package/src/components/editor/nodes/ExcalidrawNode/ExcalidrawImage.tsx +137 -0
  34. package/src/components/editor/nodes/ExcalidrawNode/ImageResizer.tsx +317 -0
  35. package/src/components/editor/nodes/ExcalidrawNode/index.tsx +204 -0
  36. package/src/components/editor/nodes/FigmaNode/FigmaNode.tsx +134 -0
  37. package/src/components/editor/nodes/Hint/HintComponet.tsx +221 -0
  38. package/src/components/editor/nodes/Hint/index.tsx +190 -0
  39. package/src/components/editor/nodes/ImageNode/index.tsx +328 -0
  40. package/src/components/editor/nodes/InlineImageNode/InlineImageComponent.tsx +383 -0
  41. package/src/components/editor/nodes/InlineImageNode/InlineImageNode.css +94 -0
  42. package/src/components/editor/nodes/InlineImageNode/InlineImageNode.tsx +309 -0
  43. package/src/components/editor/nodes/LayoutNode/LayoutContainerNode.ts +146 -0
  44. package/src/components/editor/nodes/LayoutNode/LayoutItemNode.ts +79 -0
  45. package/src/components/editor/nodes/PollNode/index.tsx +204 -0
  46. package/src/components/editor/nodes/Stepper/index.tsx +260 -0
  47. package/src/components/editor/nodes/TweetNode/index.tsx +214 -0
  48. package/src/components/editor/nodes/index.ts +81 -0
  49. package/src/components/editor/plugins/AutoEmbedPlugin/index.tsx +350 -0
  50. package/src/components/editor/plugins/AutoLinkPlugin/index.tsx +56 -0
  51. package/src/components/editor/plugins/CodeActionMenuPlugin/components/CopyButton.tsx +70 -0
  52. package/src/components/editor/plugins/CodeActionMenuPlugin/components/PrettierButton.tsx +192 -0
  53. package/src/components/editor/plugins/CodeActionMenuPlugin/index.tsx +217 -0
  54. package/src/components/editor/plugins/CodeActionMenuPlugin/utils.ts +26 -0
  55. package/src/components/editor/plugins/CodeHighlightPlugin/index.ts +21 -0
  56. package/src/components/editor/plugins/CollapsiblePlugin/Collapsible.css +76 -0
  57. package/src/components/editor/plugins/CollapsiblePlugin/index.ts +228 -0
  58. package/src/components/editor/plugins/DragDropPastePlugin/index.tsx +44 -0
  59. package/src/components/editor/plugins/DraggableBlockPlugin/index.tsx +52 -0
  60. package/src/components/editor/plugins/EquationsPlugin/index.tsx +85 -0
  61. package/src/components/editor/plugins/ExcalidrawPlugin/index.tsx +98 -0
  62. package/src/components/editor/plugins/FigmaPlugin/index.tsx +42 -0
  63. package/src/components/editor/plugins/FloatingLinkEditorPlugin/index.tsx +445 -0
  64. package/src/components/editor/plugins/FloatingTextFormatToolbarPlugin/index.tsx +275 -0
  65. package/src/components/editor/plugins/ImagesPlugin/index.tsx +222 -0
  66. package/src/components/editor/plugins/InlineImagePlugin/index.tsx +351 -0
  67. package/src/components/editor/plugins/LayoutPlugin/index.tsx +238 -0
  68. package/src/components/editor/plugins/LinkPlugin/index.tsx +36 -0
  69. package/src/components/editor/plugins/LinkWithMetaData/index.tsx +271 -0
  70. package/src/components/editor/plugins/MarkdownShortcutPlugin/index.tsx +11 -0
  71. package/src/components/editor/plugins/MarkdownTransformers/index.tsx +304 -0
  72. package/src/components/editor/plugins/PollPlugin/index.tsx +49 -0
  73. package/src/components/editor/plugins/ShortcutsPlugin/index.tsx +180 -0
  74. package/src/components/editor/plugins/ShortcutsPlugin/shortcuts.ts +253 -0
  75. package/src/components/editor/plugins/SlashCommand/index.tsx +621 -0
  76. package/src/components/editor/plugins/SpeechToTextPlugin/index.ts +127 -0
  77. package/src/components/editor/plugins/TabFocusPlugin/index.ts +58 -0
  78. package/src/components/editor/plugins/TableCellActionMenuPlugin/index.tsx +759 -0
  79. package/src/components/editor/plugins/TableCellResizer/index.tsx +438 -0
  80. package/src/components/editor/plugins/TableHoverActionsPlugin/index.tsx +314 -0
  81. package/src/components/editor/plugins/TablePlugin/index.tsx +99 -0
  82. package/src/components/editor/plugins/ToolbarPlugin/index.tsx +522 -0
  83. package/src/components/editor/plugins/TwitterPlugin/index.ts +35 -0
  84. package/src/components/editor/plugins/YouTubeNode/index.tsx +179 -0
  85. package/src/components/editor/plugins/YouTubePlugin/index.ts +41 -0
  86. package/src/components/editor/themes/editor-theme.ts +113 -0
  87. package/src/components/editor/themes/theme.css +377 -0
  88. package/src/components/editor/utils/ai.ts +291 -0
  89. package/src/components/editor/utils/canUseDOM.ts +12 -0
  90. package/src/components/editor/utils/editorFormatting.ts +282 -0
  91. package/src/components/editor/utils/environment.ts +50 -0
  92. package/src/components/editor/utils/extract-data.ts +166 -0
  93. package/src/components/editor/utils/getAllLexicalChildren.ts +13 -0
  94. package/src/components/editor/utils/getDOMRangeRect.ts +27 -0
  95. package/src/components/editor/utils/getSelectedNode.ts +27 -0
  96. package/src/components/editor/utils/gif.ts +29 -0
  97. package/src/components/editor/utils/invariant.ts +15 -0
  98. package/src/components/editor/utils/setFloatingElemPosition.ts +51 -0
  99. package/src/components/editor/utils/setFloatingElemPositionForLinkEditor.ts +40 -0
  100. package/src/components/editor/utils/setNodePlaceholderFromSelection/getNodePlaceholder.ts +51 -0
  101. package/src/components/editor/utils/setNodePlaceholderFromSelection/setNodePlaceholderFromSelection.ts +15 -0
  102. package/src/components/editor/utils/setNodePlaceholderFromSelection/setPlaceholderOnSelection.ts +114 -0
  103. package/src/components/editor/utils/setNodePlaceholderFromSelection/styles.css +6 -0
  104. package/src/components/editor/utils/url.ts +109 -0
  105. package/src/components/editor/utils/useLayoutEffect.ts +13 -0
  106. package/src/components/providers/QueryProvider.tsx +15 -0
  107. package/src/components/providers/SharedHistoryContext.tsx +28 -0
  108. package/src/components/providers/ToolbarContext.tsx +123 -0
  109. package/src/components/providers/theme-provider.tsx +11 -0
  110. package/src/components/theme/ModeToggle.tsx +40 -0
  111. package/src/components/ui/FileInput.tsx +40 -0
  112. package/src/components/ui/Input.css +32 -0
  113. package/src/components/ui/Select.css +42 -0
  114. package/src/components/ui/Select.tsx +36 -0
  115. package/src/components/ui/TextInput.tsx +48 -0
  116. package/src/components/ui/ai/ai-button.tsx +574 -0
  117. package/src/components/ui/ai/border.tsx +99 -0
  118. package/src/components/ui/ai/placeholder-input-vanish.tsx +282 -0
  119. package/src/components/ui/button.tsx +89 -0
  120. package/src/components/ui/card.tsx +76 -0
  121. package/src/components/ui/checkbox.tsx +30 -0
  122. package/src/components/ui/command.tsx +153 -0
  123. package/src/components/ui/dialog/Dialog.css +25 -0
  124. package/src/components/ui/dialog/Dialog.tsx +34 -0
  125. package/src/components/ui/dialog.tsx +122 -0
  126. package/src/components/ui/drop-downs/background-color.tsx +183 -0
  127. package/src/components/ui/drop-downs/block-format.tsx +159 -0
  128. package/src/components/ui/drop-downs/code.tsx +42 -0
  129. package/src/components/ui/drop-downs/color.tsx +177 -0
  130. package/src/components/ui/drop-downs/font-size.tsx +138 -0
  131. package/src/components/ui/drop-downs/font.tsx +155 -0
  132. package/src/components/ui/drop-downs/index.tsx +122 -0
  133. package/src/components/ui/drop-downs/insert-node.tsx +213 -0
  134. package/src/components/ui/drop-downs/text-align.tsx +123 -0
  135. package/src/components/ui/drop-downs/text-format.tsx +104 -0
  136. package/src/components/ui/dropdown-menu.tsx +201 -0
  137. package/src/components/ui/equation/EquationEditor.css +38 -0
  138. package/src/components/ui/equation/EquationEditor.tsx +56 -0
  139. package/src/components/ui/equation/KatexEquationAlterer.css +41 -0
  140. package/src/components/ui/equation/KatexEquationAlterer.tsx +83 -0
  141. package/src/components/ui/equation/KatexRenderer.tsx +66 -0
  142. package/src/components/ui/excalidraw/ExcalidrawModal.css +64 -0
  143. package/src/components/ui/excalidraw/ExcalidrawModal.tsx +234 -0
  144. package/src/components/ui/excalidraw/Modal.css +62 -0
  145. package/src/components/ui/excalidraw/Modal.tsx +110 -0
  146. package/src/components/ui/hover-card.tsx +29 -0
  147. package/src/components/ui/image/error-image.tsx +17 -0
  148. package/src/components/ui/image/file-upload.tsx +240 -0
  149. package/src/components/ui/image/image-resizer.tsx +297 -0
  150. package/src/components/ui/image/image-toolbar.tsx +264 -0
  151. package/src/components/ui/image/index.tsx +408 -0
  152. package/src/components/ui/image/lazy-image.tsx +68 -0
  153. package/src/components/ui/image/lazy-video.tsx +71 -0
  154. package/src/components/ui/input.tsx +22 -0
  155. package/src/components/ui/models/custom-dialog.tsx +320 -0
  156. package/src/components/ui/models/insert-gif.tsx +90 -0
  157. package/src/components/ui/models/insert-image.tsx +52 -0
  158. package/src/components/ui/models/insert-poll.tsx +29 -0
  159. package/src/components/ui/models/insert-table.tsx +62 -0
  160. package/src/components/ui/models/use-model.tsx +91 -0
  161. package/src/components/ui/poll/poll-component.tsx +304 -0
  162. package/src/components/ui/popover.tsx +33 -0
  163. package/src/components/ui/progress.tsx +28 -0
  164. package/src/components/ui/scroll-area.tsx +48 -0
  165. package/src/components/ui/separator.tsx +31 -0
  166. package/src/components/ui/skeleton.tsx +15 -0
  167. package/src/components/ui/sonner.tsx +31 -0
  168. package/src/components/ui/stepper/step.tsx +179 -0
  169. package/src/components/ui/stepper/stepper.tsx +89 -0
  170. package/src/components/ui/textarea.tsx +22 -0
  171. package/src/components/ui/toggle.tsx +71 -0
  172. package/src/components/ui/tooltip.tsx +32 -0
  173. package/src/components/ui/write/text-format-floting-toolbar.tsx +346 -0
  174. package/src/lib/edgestore.ts +9 -0
  175. package/src/lib/pinecone-client.ts +0 -0
  176. package/src/lib/utils.ts +6 -0
  177. package/src/utils/docSerialization.ts +77 -0
  178. package/src/utils/emoji-list.ts +16615 -0
  179. package/src/utils/getDOMRangeRect.ts +27 -0
  180. package/src/utils/getSelectedNode.ts +27 -0
  181. package/src/utils/getThemeSelector.ts +25 -0
  182. package/src/utils/isMobileWidth.ts +7 -0
  183. package/src/utils/joinClasses.ts +13 -0
  184. package/src/utils/setFloatingElemPosition.ts +74 -0
  185. package/src/utils/setFloatingElemPositionForLinkEditor.ts +46 -0
  186. package/src/utils/swipe.ts +127 -0
  187. package/src/utils/url.ts +38 -0
  188. package/tsconfig.json +27 -0
@@ -0,0 +1,275 @@
1
+ import { $isCodeHighlightNode } from "@lexical/code";
2
+ import { $isLinkNode } from "@lexical/link";
3
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
4
+ import {
5
+
6
+ $findMatchingParent,
7
+ $getNearestNodeOfType,
8
+ mergeRegister,
9
+ } from "@lexical/utils";
10
+ import {
11
+ $getSelection,
12
+ $isParagraphNode,
13
+ $isRangeSelection,
14
+ $isRootOrShadowRoot,
15
+ $isTextNode,
16
+ getDOMSelection,
17
+ LexicalEditor,
18
+ } from "lexical";
19
+ import { Dispatch, useCallback, useEffect, useState } from "react";
20
+ import * as React from "react";
21
+ import { createPortal } from "react-dom";
22
+
23
+ import { getSelectedNode } from "../../utils/getSelectedNode";
24
+
25
+ import { blockTypeToBlockName } from "@/components/providers/ToolbarContext";
26
+ // import TextFormatFloatingToolbar from "@/components/ui/floting-text/write/text-format-floting-toolbar";
27
+ import {
28
+ $getSelectionStyleValueForProperty,
29
+ } from "@lexical/selection";
30
+
31
+ import { useRef } from "react";
32
+ import { $isTableNode } from "@lexical/table";
33
+ import { $isListNode, ListNode } from "@lexical/list";
34
+ import { $isHeadingNode } from "@lexical/rich-text";
35
+ import TextFormatFloatingToolbar from "@/components/ui/write/text-format-floting-toolbar";
36
+
37
+ function useFloatingTextFormatToolbar(
38
+ editor: LexicalEditor,
39
+ anchorElem: HTMLElement,
40
+ setIsLinkEditMode: Dispatch<boolean>
41
+ ): React.JSX.Element | null {
42
+ const [isText, setIsText] = useState(false);
43
+ const [isLink, setIsLink] = useState(false);
44
+ const [isBold, setIsBold] = useState(false);
45
+ const [isItalic, setIsItalic] = useState(false);
46
+ const [isUnderline, setIsUnderline] = useState(false);
47
+ const [isStrikethrough, setIsStrikethrough] = useState(false);
48
+ const [isCode, setIsCode] = useState(false);
49
+ const [fontColor, setFontColor] = useState<string>("#000000");
50
+ const [bgColor, setBgColor] = useState<string>("#ffffff");
51
+ const [fontFamily, setFontFamily] = useState<string>("Arial");
52
+ const [blockType, setBlockType] =useState<keyof typeof blockTypeToBlockName>("paragraph");
53
+ const [isUppercase, setIsUppercase] = useState(false);
54
+ const [isLowercase, setIsLowercase] = useState(false);
55
+ const [isCapitalize, setIsCapitalize] = useState(false);
56
+ const [isSubscript, setIsSubscript] = useState(false);
57
+ const [isSuperscript, setIsSuperscript] = useState(false);
58
+ const [rootType, setRootType] = useState<"root" | "table">("root");
59
+ const [textAlign, setTextAlign] = useState<
60
+ "left" | "center" | "right" | "justify" | null
61
+ >(null);
62
+ const [fontSize, setFontSize] = useState<string>("16px");
63
+
64
+ const toolbarRef = useRef<HTMLDivElement>(null);
65
+
66
+ const updatePopup = useCallback(() => {
67
+ editor.getEditorState().read(() => {
68
+ if (editor.isComposing()) {
69
+ return;
70
+ }
71
+ const selection = $getSelection();
72
+ const nativeSelection = getDOMSelection(editor._window);
73
+ const rootElement = editor.getRootElement();
74
+ const aiElement = document.querySelector(".AI-format");
75
+
76
+
77
+
78
+
79
+
80
+
81
+ if (
82
+ nativeSelection &&
83
+ nativeSelection.anchorNode instanceof HTMLElement &&
84
+ nativeSelection.anchorNode.closest(".dropdown-portal")
85
+ ) {
86
+ return;
87
+ }
88
+
89
+ if (
90
+ nativeSelection !== null &&
91
+ (!$isRangeSelection(selection) ||
92
+ rootElement === null ||
93
+ !rootElement.contains(nativeSelection.anchorNode)) &&
94
+ !toolbarRef?.current?.contains(nativeSelection.anchorNode) && !(aiElement && aiElement.contains(nativeSelection.anchorNode))
95
+
96
+ ) {
97
+ setIsText(false);
98
+ return;
99
+ }
100
+
101
+ if (!$isRangeSelection(selection)) {
102
+ return;
103
+ }
104
+
105
+ const node = getSelectedNode(selection);
106
+
107
+ // Update text format
108
+ setIsBold(selection.hasFormat('bold'));
109
+ setIsItalic(selection.hasFormat('italic'));
110
+ setIsUnderline(selection.hasFormat('underline'));
111
+ setIsStrikethrough(selection.hasFormat('strikethrough'));
112
+ setIsSubscript(selection.hasFormat('subscript'));
113
+ setIsLowercase(selection.hasFormat('lowercase'));
114
+ setIsUppercase(selection.hasFormat('uppercase'));
115
+ setIsCapitalize(selection.hasFormat('capitalize'));
116
+ setIsSubscript(selection.hasFormat('subscript'));
117
+ setIsSuperscript(selection.hasFormat('superscript'));
118
+
119
+ setIsCode(selection.hasFormat('code'));
120
+ const size = $getSelectionStyleValueForProperty(
121
+ selection,
122
+ "font-size",
123
+ "16px"
124
+ );
125
+ setFontSize(size);
126
+ const alignment = $getSelectionStyleValueForProperty(
127
+ selection,
128
+ "text-align",
129
+ "left"
130
+ );
131
+ setTextAlign(alignment as "left" | "center" | "right" | "justify" | null);
132
+
133
+ // Update links
134
+ const parent = node.getParent();
135
+ if ($isLinkNode(parent) || $isLinkNode(node)) {
136
+ setIsLink(true);
137
+ } else {
138
+ setIsLink(false);
139
+ }
140
+
141
+ if (
142
+ !$isCodeHighlightNode(selection.anchor.getNode()) &&
143
+ selection.getTextContent() !== ''
144
+ ) {
145
+ setIsText($isTextNode(node) || $isParagraphNode(node));
146
+ } else {
147
+ setIsText(false);
148
+ }
149
+
150
+ const rawTextContent = selection.getTextContent().replace(/\n/g, '');
151
+ if (!selection.isCollapsed() && rawTextContent === '') {
152
+ setIsText(false);
153
+ return;
154
+ }
155
+ setFontFamily(
156
+ $getSelectionStyleValueForProperty(selection, "font-family", "Arial")
157
+ );
158
+ setFontColor($getSelectionStyleValueForProperty(selection, "color"));
159
+ setBgColor(
160
+ $getSelectionStyleValueForProperty(selection, "background-color")
161
+ );
162
+ const tableNode = $findMatchingParent(node, $isTableNode);
163
+ if ($isTableNode(tableNode)) {
164
+ setRootType("table");
165
+ } else {
166
+ setRootType("root");
167
+ }
168
+
169
+ const anchorNode = selection.anchor.getNode();
170
+ let element =
171
+ anchorNode.getKey() === "root"
172
+ ? anchorNode
173
+ : $findMatchingParent(anchorNode, (e) => {
174
+ const parent = e.getParent();
175
+ return parent !== null && $isRootOrShadowRoot(parent);
176
+ });
177
+ if ($isListNode(element)) {
178
+ const parentList = $getNearestNodeOfType<ListNode>(
179
+ anchorNode,
180
+ ListNode
181
+ );
182
+
183
+ const type = parentList
184
+ ? parentList.getListType()
185
+ : element.getListType();
186
+ setBlockType(type);
187
+ } else {
188
+ const type = $isHeadingNode(element)
189
+ ? element.getTag()
190
+ : // @ts-ignore
191
+ element.getType();
192
+ setBlockType(type as keyof typeof blockTypeToBlockName);
193
+ }
194
+ });
195
+ }, [editor]);
196
+
197
+ useEffect(() => {
198
+ const handleSelectionChange = () => {
199
+ if (toolbarRef.current && toolbarRef.current.contains(document.activeElement)) {
200
+ return;
201
+ }
202
+ updatePopup();
203
+ };
204
+
205
+ document.addEventListener('selectionchange', handleSelectionChange);
206
+ return () => {
207
+ document.removeEventListener('selectionchange', handleSelectionChange);
208
+ };
209
+ }, [updatePopup]);
210
+
211
+ useEffect(() => {
212
+ return mergeRegister(
213
+ editor.registerUpdateListener(() => {
214
+ updatePopup();
215
+ }),
216
+ editor.registerRootListener(() => {
217
+ if (editor.getRootElement() === null) {
218
+ setIsText(false);
219
+ }
220
+ })
221
+ );
222
+ }, [editor, updatePopup]);
223
+
224
+
225
+
226
+ if (!isText) {
227
+ return null;
228
+ }
229
+
230
+ return createPortal(
231
+ <div ref={toolbarRef}>
232
+ <TextFormatFloatingToolbar
233
+ editor={editor}
234
+ anchorElem={anchorElem}
235
+ isLink={isLink}
236
+ isBold={isBold}
237
+ isItalic={isItalic}
238
+ isStrikethrough={isStrikethrough}
239
+ isUnderline={isUnderline}
240
+ isCode={isCode}
241
+ fontColor={fontColor}
242
+ bgColor={bgColor}
243
+ blockType={blockType}
244
+ fontFamily={fontFamily}
245
+ setIsLinkEditMode={setIsLinkEditMode}
246
+ isUppercase={isUppercase}
247
+ rootType={rootType}
248
+ textAlign={textAlign}
249
+ isLowercase={isLowercase}
250
+ isCapitalize={isCapitalize}
251
+ isSubscript={isSubscript}
252
+ fontSize={fontSize}
253
+ isSuperscript={isSuperscript}
254
+ />
255
+ </div>,
256
+ anchorElem
257
+ );
258
+ }
259
+
260
+
261
+ export default function FloatingTextFormatToolbarPlugin({
262
+ anchorElem = document.body,
263
+ setIsLinkEditMode,
264
+ }: {
265
+ anchorElem?: HTMLElement;
266
+ setIsLinkEditMode: Dispatch<boolean>;
267
+ }): React.JSX.Element | null {
268
+ const [editor] = useLexicalComposerContext();
269
+
270
+ return useFloatingTextFormatToolbar(editor, anchorElem, setIsLinkEditMode);
271
+
272
+ // if (editor.isEditable())
273
+ // return useFloatingTextFormatToolbar(editor, anchorElem, setIsLinkEditMode);
274
+ // else return useFloatingTextReadMode(editor, anchorElem);
275
+ }
@@ -0,0 +1,222 @@
1
+
2
+ import type { JSX } from "react";
3
+
4
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
5
+ import { $wrapNodeInElement, mergeRegister } from "@lexical/utils";
6
+ import {
7
+ $createParagraphNode,
8
+ $createRangeSelection,
9
+ $getSelection,
10
+ $insertNodes,
11
+ $isNodeSelection,
12
+ $isRootOrShadowRoot,
13
+ $setSelection,
14
+ COMMAND_PRIORITY_EDITOR,
15
+ COMMAND_PRIORITY_HIGH,
16
+ COMMAND_PRIORITY_LOW,
17
+ createCommand,
18
+ DRAGOVER_COMMAND,
19
+ DRAGSTART_COMMAND,
20
+ DROP_COMMAND,
21
+ getDOMSelectionFromTarget,
22
+ isHTMLElement,
23
+ LexicalCommand,
24
+ LexicalEditor,
25
+ } from "lexical";
26
+ import { useEffect} from "react";
27
+
28
+
29
+ import {
30
+ $createImageNode,
31
+ $isImageNode,
32
+ ImageNode,
33
+ ImagePayload,
34
+ } from "../../nodes/ImageNode";
35
+
36
+ export type InsertImagePayload = Readonly<ImagePayload>;
37
+
38
+ export const INSERT_IMAGE_COMMAND: LexicalCommand<InsertImagePayload> =
39
+ createCommand("INSERT_IMAGE_COMMAND");
40
+
41
+
42
+
43
+ export default function ImagesPlugin({
44
+ captionsEnabled,
45
+ }: {
46
+ captionsEnabled?: boolean;
47
+ }): JSX.Element | null {
48
+ const [editor] = useLexicalComposerContext();
49
+
50
+ useEffect(() => {
51
+ if (!editor.hasNodes([ImageNode])) {
52
+ throw new Error("ImagesPlugin: ImageNode not registered on editor");
53
+ }
54
+
55
+ return mergeRegister(
56
+ editor.registerCommand<InsertImagePayload>(
57
+ INSERT_IMAGE_COMMAND,
58
+ (payload) => {
59
+ const imageNode = $createImageNode(payload);
60
+ $insertNodes([imageNode]);
61
+ if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) {
62
+ $wrapNodeInElement(imageNode, $createParagraphNode).selectEnd();
63
+ }
64
+
65
+ return true;
66
+ },
67
+ COMMAND_PRIORITY_EDITOR
68
+ ),
69
+ editor.registerCommand<DragEvent>(
70
+ DRAGSTART_COMMAND,
71
+ (event) => {
72
+ return $onDragStart(event);
73
+ },
74
+ COMMAND_PRIORITY_HIGH
75
+ ),
76
+ editor.registerCommand<DragEvent>(
77
+ DRAGOVER_COMMAND,
78
+ (event) => {
79
+ return $onDragover(event);
80
+ },
81
+ COMMAND_PRIORITY_LOW
82
+ ),
83
+ editor.registerCommand<DragEvent>(
84
+ DROP_COMMAND,
85
+ (event) => {
86
+ return $onDrop(event, editor);
87
+ },
88
+ COMMAND_PRIORITY_HIGH
89
+ )
90
+ );
91
+ }, [captionsEnabled, editor]);
92
+
93
+ return null;
94
+ }
95
+
96
+ const TRANSPARENT_IMAGE =
97
+ "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
98
+ let img: HTMLImageElement | null = null;
99
+ if (typeof window !== "undefined") {
100
+ img = document.createElement("img");
101
+ img.src = TRANSPARENT_IMAGE;
102
+ }
103
+
104
+ function $onDragStart(event: DragEvent): boolean {
105
+ const node = $getImageNodeInSelection();
106
+ if (!node) {
107
+ return false;
108
+ }
109
+ const dataTransfer = event.dataTransfer;
110
+ if (!dataTransfer) {
111
+ return false;
112
+ }
113
+ dataTransfer.setData("text/plain", "_");
114
+ dataTransfer.setDragImage(img!, 0, 0);
115
+ dataTransfer.setData(
116
+ "application/x-lexical-drag",
117
+ JSON.stringify({
118
+ data: {
119
+ altText: node.__altText,
120
+ caption: node.__caption,
121
+ height: node.__height,
122
+ key: node.getKey(),
123
+ maxWidth: node.__maxWidth,
124
+ showCaption: node.__showCaption,
125
+ src: node.__src,
126
+ width: node.__width,
127
+ },
128
+ type: "image",
129
+ })
130
+ );
131
+
132
+ return true;
133
+ }
134
+
135
+ function $onDragover(event: DragEvent): boolean {
136
+ const node = $getImageNodeInSelection();
137
+ if (!node) {
138
+ return false;
139
+ }
140
+ if (!canDropImage(event)) {
141
+ event.preventDefault();
142
+ }
143
+ return true;
144
+ }
145
+
146
+ function $onDrop(event: DragEvent, editor: LexicalEditor): boolean {
147
+ const node = $getImageNodeInSelection();
148
+ if (!node) {
149
+ return false;
150
+ }
151
+ const data = getDragImageData(event);
152
+ if (!data) {
153
+ return false;
154
+ }
155
+ event.preventDefault();
156
+ if (canDropImage(event)) {
157
+ const range = getDragSelection(event);
158
+ node.remove();
159
+ const rangeSelection = $createRangeSelection();
160
+ if (range !== null && range !== undefined) {
161
+ rangeSelection.applyDOMRange(range);
162
+ }
163
+ $setSelection(rangeSelection);
164
+ editor.dispatchCommand(INSERT_IMAGE_COMMAND, data);
165
+ }
166
+ return true;
167
+ }
168
+
169
+ function $getImageNodeInSelection(): ImageNode | null {
170
+ const selection = $getSelection();
171
+ if (!$isNodeSelection(selection)) {
172
+ return null;
173
+ }
174
+ const nodes = selection.getNodes();
175
+ const node = nodes[0];
176
+ return $isImageNode(node) ? node : null;
177
+ }
178
+
179
+ function getDragImageData(event: DragEvent): null | InsertImagePayload {
180
+ const dragData = event.dataTransfer?.getData("application/x-lexical-drag");
181
+ if (!dragData) {
182
+ return null;
183
+ }
184
+ const { type, data } = JSON.parse(dragData);
185
+ if (type !== "image") {
186
+ return null;
187
+ }
188
+
189
+ return data;
190
+ }
191
+
192
+ declare global {
193
+ interface DragEvent {
194
+ rangeOffset?: number;
195
+ rangeParent?: Node;
196
+ }
197
+ }
198
+
199
+ function canDropImage(event: DragEvent): boolean {
200
+ const target = event.target;
201
+ return !!(
202
+ isHTMLElement(target) &&
203
+ !target.closest("code, span.editor-image") &&
204
+ isHTMLElement(target.parentElement) &&
205
+ target.parentElement.closest("div.ContentEditable__root")
206
+ );
207
+ }
208
+
209
+ function getDragSelection(event: DragEvent): Range | null | undefined {
210
+ let range;
211
+ const domSelection = getDOMSelectionFromTarget(event.target);
212
+ if (document.caretRangeFromPoint) {
213
+ range = document.caretRangeFromPoint(event.clientX, event.clientY);
214
+ } else if (event.rangeParent && domSelection !== null) {
215
+ domSelection.collapse(event.rangeParent, event.rangeOffset || 0);
216
+ range = domSelection.getRangeAt(0);
217
+ } else {
218
+ throw Error(`Cannot get the selection when dragging`);
219
+ }
220
+
221
+ return range;
222
+ }