5htp-core 0.4.9-2 → 0.4.9-6

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 (30) hide show
  1. package/package.json +4 -1
  2. package/src/client/assets/css/components/lists.less +3 -3
  3. package/src/client/components/Form.ts +1 -1
  4. package/src/client/components/Select/ChoiceElement.tsx +20 -27
  5. package/src/client/components/Select/index.tsx +24 -42
  6. package/src/client/components/inputv3/Rte/Editor.tsx +16 -8
  7. package/src/client/components/inputv3/Rte/ToolbarPlugin/index.tsx +8 -12
  8. package/src/client/components/inputv3/Rte/nodes/HeadingNode.ts +55 -0
  9. package/src/client/components/inputv3/Rte/plugins/ComponentPickerPlugin/index.tsx +9 -6
  10. package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.css +23 -23
  11. package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.tsx +26 -24
  12. package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.css +10 -38
  13. package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.tsx +349 -356
  14. package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.css +80 -84
  15. package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.tsx +308 -347
  16. package/src/client/components/inputv3/Rte/style.less +0 -2
  17. package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.css +0 -11
  18. package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.ts +2 -2
  19. package/src/client/components/inputv3/base.less +0 -6
  20. package/src/client/components/inputv3/file/index.tsx +50 -48
  21. package/src/client/components/inputv3/index.tsx +1 -1
  22. package/src/client/services/router/request/api.ts +29 -13
  23. package/src/common/data/rte/nodes.ts +9 -1
  24. package/src/common/router/request/api.ts +6 -4
  25. package/src/server/services/disks/driver.ts +2 -0
  26. package/src/server/services/disks/drivers/s3/index.ts +30 -1
  27. package/src/server/utils/rte.ts +316 -46
  28. package/src/server/utils/slug.ts +67 -0
  29. package/src/client/components/inputv3/Rte/nodes/PlaygroundNodes.ts +0 -76
  30. package/src/server/services/schema/rte.ts +0 -110
@@ -8,381 +8,342 @@
8
8
 
9
9
  import './index.css';
10
10
 
11
- import {$isCodeHighlightNode} from '@lexical/code';
12
- import {$isLinkNode, TOGGLE_LINK_COMMAND} from '@lexical/link';
13
- import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
14
- import {mergeRegister} from '@lexical/utils';
11
+ import { $isCodeHighlightNode } from '@lexical/code';
12
+ import { $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
13
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
14
+ import { mergeRegister } from '@lexical/utils';
15
15
  import {
16
- $getSelection,
17
- $isParagraphNode,
18
- $isRangeSelection,
19
- $isTextNode,
20
- COMMAND_PRIORITY_LOW,
21
- FORMAT_TEXT_COMMAND,
22
- LexicalEditor,
23
- SELECTION_CHANGE_COMMAND,
16
+ $getSelection,
17
+ $isParagraphNode,
18
+ $isRangeSelection,
19
+ $isTextNode,
20
+ COMMAND_PRIORITY_LOW,
21
+ FORMAT_TEXT_COMMAND,
22
+ LexicalEditor,
23
+ SELECTION_CHANGE_COMMAND,
24
24
  } from 'lexical';
25
- import {Dispatch, useCallback, useEffect, useRef, useState} from 'react';
25
+ import { Dispatch, useCallback, useEffect, useRef, useState } from 'react';
26
26
  import * as React from 'react';
27
- import {createPortal} from 'react-dom';
27
+ import { createPortal } from 'react-dom';
28
28
 
29
- import {getDOMRangeRect} from '../../utils/getDOMRangeRect';
30
- import {getSelectedNode} from '../../utils/getSelectedNode';
31
- import {setFloatingElemPosition} from '../../utils/setFloatingElemPosition';
29
+ import { getDOMRangeRect } from '../../utils/getDOMRangeRect';
30
+ import { getSelectedNode } from '../../utils/getSelectedNode';
31
+ import { setFloatingElemPosition } from '../../utils/setFloatingElemPosition';
32
+
33
+ import Button from '@client/components/button';
32
34
 
33
35
  function TextFormatFloatingToolbar({
34
- editor,
35
- anchorElem,
36
- isLink,
37
- isBold,
38
- isItalic,
39
- isUnderline,
40
- isCode,
41
- isStrikethrough,
42
- isSubscript,
43
- isSuperscript,
44
- setIsLinkEditMode,
36
+ editor,
37
+ anchorElem,
38
+ isLink,
39
+ isBold,
40
+ isItalic,
41
+ isUnderline,
42
+ isCode,
43
+ isStrikethrough,
44
+ isSubscript,
45
+ isSuperscript,
46
+ setIsLinkEditMode,
45
47
  }: {
46
- editor: LexicalEditor;
47
- anchorElem: HTMLElement;
48
- isBold: boolean;
49
- isCode: boolean;
50
- isItalic: boolean;
51
- isLink: boolean;
52
- isStrikethrough: boolean;
53
- isSubscript: boolean;
54
- isSuperscript: boolean;
55
- isUnderline: boolean;
56
- setIsLinkEditMode: Dispatch<boolean>;
57
- }): JSX.Element {
58
- const popupCharStylesEditorRef = useRef<HTMLDivElement | null>(null);
59
-
60
- const insertLink = useCallback(() => {
61
- if (!isLink) {
62
- setIsLinkEditMode(true);
63
- editor.dispatchCommand(TOGGLE_LINK_COMMAND, 'https://');
64
- } else {
65
- setIsLinkEditMode(false);
66
- editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
67
- }
68
- }, [editor, isLink, setIsLinkEditMode]);
69
-
70
- function mouseMoveListener(e: MouseEvent) {
71
- if (
72
- popupCharStylesEditorRef?.current &&
73
- (e.buttons === 1 || e.buttons === 3)
74
- ) {
75
- if (popupCharStylesEditorRef.current.style.pointerEvents !== 'none') {
76
- const x = e.clientX;
77
- const y = e.clientY;
78
- const elementUnderMouse = document.elementFromPoint(x, y);
79
-
80
- if (!popupCharStylesEditorRef.current.contains(elementUnderMouse)) {
81
- // Mouse is not over the target element => not a normal click, but probably a drag
82
- popupCharStylesEditorRef.current.style.pointerEvents = 'none';
48
+ editor: LexicalEditor;
49
+ anchorElem: HTMLElement;
50
+ isBold: boolean;
51
+ isCode: boolean;
52
+ isItalic: boolean;
53
+ isLink: boolean;
54
+ isStrikethrough: boolean;
55
+ isSubscript: boolean;
56
+ isSuperscript: boolean;
57
+ isUnderline: boolean;
58
+ setIsLinkEditMode: Dispatch<boolean>;
59
+ }): React.JSX.Element {
60
+ const popupCharStylesEditorRef = useRef<HTMLDivElement | null>(null);
61
+
62
+ const insertLink = useCallback(() => {
63
+ if (!isLink) {
64
+ setIsLinkEditMode(true);
65
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, 'https://');
66
+ } else {
67
+ setIsLinkEditMode(false);
68
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
69
+ }
70
+ }, [editor, isLink, setIsLinkEditMode]);
71
+
72
+ function mouseMoveListener(e: MouseEvent) {
73
+ if (
74
+ popupCharStylesEditorRef?.current &&
75
+ (e.buttons === 1 || e.buttons === 3)
76
+ ) {
77
+ if (popupCharStylesEditorRef.current.style.pointerEvents !== 'none') {
78
+ const x = e.clientX;
79
+ const y = e.clientY;
80
+ const elementUnderMouse = document.elementFromPoint(x, y);
81
+
82
+ if (!popupCharStylesEditorRef.current.contains(elementUnderMouse)) {
83
+ // Mouse is not over the target element => not a normal click, but probably a drag
84
+ popupCharStylesEditorRef.current.style.pointerEvents = 'none';
85
+ }
86
+ }
83
87
  }
84
- }
85
88
  }
86
- }
87
- function mouseUpListener(e: MouseEvent) {
88
- if (popupCharStylesEditorRef?.current) {
89
- if (popupCharStylesEditorRef.current.style.pointerEvents !== 'auto') {
90
- popupCharStylesEditorRef.current.style.pointerEvents = 'auto';
91
- }
89
+ function mouseUpListener(e: MouseEvent) {
90
+ if (popupCharStylesEditorRef?.current) {
91
+ if (popupCharStylesEditorRef.current.style.pointerEvents !== 'auto') {
92
+ popupCharStylesEditorRef.current.style.pointerEvents = 'auto';
93
+ }
94
+ }
92
95
  }
93
- }
94
96
 
95
- useEffect(() => {
96
- if (popupCharStylesEditorRef?.current) {
97
- document.addEventListener('mousemove', mouseMoveListener);
98
- document.addEventListener('mouseup', mouseUpListener);
97
+ useEffect(() => {
98
+ if (popupCharStylesEditorRef?.current) {
99
+ document.addEventListener('mousemove', mouseMoveListener);
100
+ document.addEventListener('mouseup', mouseUpListener);
99
101
 
100
- return () => {
101
- document.removeEventListener('mousemove', mouseMoveListener);
102
- document.removeEventListener('mouseup', mouseUpListener);
103
- };
104
- }
105
- }, [popupCharStylesEditorRef]);
102
+ return () => {
103
+ document.removeEventListener('mousemove', mouseMoveListener);
104
+ document.removeEventListener('mouseup', mouseUpListener);
105
+ };
106
+ }
107
+ }, [popupCharStylesEditorRef]);
106
108
 
107
- const $updateTextFormatFloatingToolbar = useCallback(() => {
108
- const selection = $getSelection();
109
+ const $updateTextFormatFloatingToolbar = useCallback(() => {
110
+ const selection = $getSelection();
109
111
 
110
- const popupCharStylesEditorElem = popupCharStylesEditorRef.current;
111
- const nativeSelection = window.getSelection();
112
+ const popupCharStylesEditorElem = popupCharStylesEditorRef.current;
113
+ const nativeSelection = window.getSelection();
112
114
 
113
- if (popupCharStylesEditorElem === null) {
114
- return;
115
- }
115
+ if (popupCharStylesEditorElem === null) {
116
+ return;
117
+ }
116
118
 
117
- const rootElement = editor.getRootElement();
118
- if (
119
- selection !== null &&
120
- nativeSelection !== null &&
121
- !nativeSelection.isCollapsed &&
122
- rootElement !== null &&
123
- rootElement.contains(nativeSelection.anchorNode)
124
- ) {
125
- const rangeRect = getDOMRangeRect(nativeSelection, rootElement);
126
-
127
- setFloatingElemPosition(
128
- rangeRect,
129
- popupCharStylesEditorElem,
130
- anchorElem,
131
- isLink,
132
- );
133
- }
134
- }, [editor, anchorElem, isLink]);
119
+ const rootElement = editor.getRootElement();
120
+ if (
121
+ selection !== null &&
122
+ nativeSelection !== null &&
123
+ !nativeSelection.isCollapsed &&
124
+ rootElement !== null &&
125
+ rootElement.contains(nativeSelection.anchorNode)
126
+ ) {
127
+ const rangeRect = getDOMRangeRect(nativeSelection, rootElement);
128
+
129
+ setFloatingElemPosition(
130
+ rangeRect,
131
+ popupCharStylesEditorElem,
132
+ anchorElem,
133
+ isLink,
134
+ );
135
+ }
136
+ }, [editor, anchorElem, isLink]);
135
137
 
136
- useEffect(() => {
137
- const scrollerElem = anchorElem.parentElement;
138
+ useEffect(() => {
139
+ const scrollerElem = anchorElem.parentElement;
138
140
 
139
- const update = () => {
140
- editor.getEditorState().read(() => {
141
- $updateTextFormatFloatingToolbar();
142
- });
143
- };
141
+ const update = () => {
142
+ editor.getEditorState().read(() => {
143
+ $updateTextFormatFloatingToolbar();
144
+ });
145
+ };
144
146
 
145
- window.addEventListener('resize', update);
146
- if (scrollerElem) {
147
- scrollerElem.addEventListener('scroll', update);
148
- }
147
+ window.addEventListener('resize', update);
148
+ if (scrollerElem) {
149
+ scrollerElem.addEventListener('scroll', update);
150
+ }
149
151
 
150
- return () => {
151
- window.removeEventListener('resize', update);
152
- if (scrollerElem) {
153
- scrollerElem.removeEventListener('scroll', update);
154
- }
155
- };
156
- }, [editor, $updateTextFormatFloatingToolbar, anchorElem]);
157
-
158
- useEffect(() => {
159
- editor.getEditorState().read(() => {
160
- $updateTextFormatFloatingToolbar();
161
- });
162
- return mergeRegister(
163
- editor.registerUpdateListener(({editorState}) => {
164
- editorState.read(() => {
165
- $updateTextFormatFloatingToolbar();
152
+ return () => {
153
+ window.removeEventListener('resize', update);
154
+ if (scrollerElem) {
155
+ scrollerElem.removeEventListener('scroll', update);
156
+ }
157
+ };
158
+ }, [editor, $updateTextFormatFloatingToolbar, anchorElem]);
159
+
160
+ useEffect(() => {
161
+ editor.getEditorState().read(() => {
162
+ $updateTextFormatFloatingToolbar();
166
163
  });
167
- }),
168
-
169
- editor.registerCommand(
170
- SELECTION_CHANGE_COMMAND,
171
- () => {
172
- $updateTextFormatFloatingToolbar();
173
- return false;
174
- },
175
- COMMAND_PRIORITY_LOW,
176
- ),
164
+ return mergeRegister(
165
+ editor.registerUpdateListener(({ editorState }) => {
166
+ editorState.read(() => {
167
+ $updateTextFormatFloatingToolbar();
168
+ });
169
+ }),
170
+
171
+ editor.registerCommand(
172
+ SELECTION_CHANGE_COMMAND,
173
+ () => {
174
+ $updateTextFormatFloatingToolbar();
175
+ return false;
176
+ },
177
+ COMMAND_PRIORITY_LOW,
178
+ ),
179
+ );
180
+ }, [editor, $updateTextFormatFloatingToolbar]);
181
+
182
+ return (
183
+ <div ref={popupCharStylesEditorRef} className="floating-text-format-popup card pd-05 row menu">
184
+ {editor.isEditable() && (
185
+ <>
186
+ <Button size="s" icon="bold" active={isBold} onClick={() => {
187
+ editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold');
188
+ }} title="Format text as bold" />
189
+
190
+ <Button size="s" icon="italic" active={isItalic} onClick={() => {
191
+ editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic');
192
+ }} title="Format text as italics" />
193
+
194
+ <Button size="s" icon="underline" active={isUnderline} onClick={() => {
195
+ editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'underline');
196
+ }} title="Format text to underlined" />
197
+
198
+ <Button size="s" icon="strikethrough" active={isStrikethrough} onClick={() => {
199
+ editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'strikethrough');
200
+ }} title="Format text with a strikethrough" />
201
+
202
+ <Button size="s" icon="subscript" active={isSubscript} onClick={() => {
203
+ editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'subscript');
204
+ }} title="Format Subscript" />
205
+
206
+ <Button size="s" icon="superscript" active={isSuperscript} onClick={() => {
207
+ editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'superscript');
208
+ }} title="Format Superscript" />
209
+
210
+ <Button size="s" icon="code" active={isCode} onClick={() => {
211
+ editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'code');
212
+ }} title="Insert code block" />
213
+
214
+ <Button size="s" icon="link" active={isLink} onClick={insertLink}
215
+ title="Insert link" />
216
+ </>
217
+ )}
218
+ </div>
177
219
  );
178
- }, [editor, $updateTextFormatFloatingToolbar]);
179
-
180
- return (
181
- <div ref={popupCharStylesEditorRef} className="floating-text-format-popup">
182
- {editor.isEditable() && (
183
- <>
184
- <button
185
- type="button"
186
- onClick={() => {
187
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold');
188
- }}
189
- className={'popup-item spaced ' + (isBold ? 'active' : '')}
190
- aria-label="Format text as bold">
191
- <i className="format bold" />
192
- </button>
193
- <button
194
- type="button"
195
- onClick={() => {
196
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic');
197
- }}
198
- className={'popup-item spaced ' + (isItalic ? 'active' : '')}
199
- aria-label="Format text as italics">
200
- <i className="format italic" />
201
- </button>
202
- <button
203
- type="button"
204
- onClick={() => {
205
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'underline');
206
- }}
207
- className={'popup-item spaced ' + (isUnderline ? 'active' : '')}
208
- aria-label="Format text to underlined">
209
- <i className="format underline" />
210
- </button>
211
- <button
212
- type="button"
213
- onClick={() => {
214
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'strikethrough');
215
- }}
216
- className={'popup-item spaced ' + (isStrikethrough ? 'active' : '')}
217
- aria-label="Format text with a strikethrough">
218
- <i className="format strikethrough" />
219
- </button>
220
- <button
221
- type="button"
222
- onClick={() => {
223
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'subscript');
224
- }}
225
- className={'popup-item spaced ' + (isSubscript ? 'active' : '')}
226
- title="Subscript"
227
- aria-label="Format Subscript">
228
- <i className="format subscript" />
229
- </button>
230
- <button
231
- type="button"
232
- onClick={() => {
233
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'superscript');
234
- }}
235
- className={'popup-item spaced ' + (isSuperscript ? 'active' : '')}
236
- title="Superscript"
237
- aria-label="Format Superscript">
238
- <i className="format superscript" />
239
- </button>
240
- <button
241
- type="button"
242
- onClick={() => {
243
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'code');
244
- }}
245
- className={'popup-item spaced ' + (isCode ? 'active' : '')}
246
- aria-label="Insert code block">
247
- <i className="format code" />
248
- </button>
249
- <button
250
- type="button"
251
- onClick={insertLink}
252
- className={'popup-item spaced ' + (isLink ? 'active' : '')}
253
- aria-label="Insert link">
254
- <i className="format link" />
255
- </button>
256
- </>
257
- )}
258
- </div>
259
- );
260
220
  }
261
221
 
262
222
  function useFloatingTextFormatToolbar(
263
- editor: LexicalEditor,
264
- anchorElem: HTMLElement,
265
- setIsLinkEditMode: Dispatch<boolean>,
266
- ): JSX.Element | null {
267
- const [isText, setIsText] = useState(false);
268
- const [isLink, setIsLink] = useState(false);
269
- const [isBold, setIsBold] = useState(false);
270
- const [isItalic, setIsItalic] = useState(false);
271
- const [isUnderline, setIsUnderline] = useState(false);
272
- const [isStrikethrough, setIsStrikethrough] = useState(false);
273
- const [isSubscript, setIsSubscript] = useState(false);
274
- const [isSuperscript, setIsSuperscript] = useState(false);
275
- const [isCode, setIsCode] = useState(false);
276
-
277
- const updatePopup = useCallback(() => {
278
- editor.getEditorState().read(() => {
279
- // Should not to pop up the floating toolbar when using IME input
280
- if (editor.isComposing()) {
281
- return;
282
- }
283
- const selection = $getSelection();
284
- const nativeSelection = window.getSelection();
285
- const rootElement = editor.getRootElement();
286
-
287
- if (
288
- nativeSelection !== null &&
289
- (!$isRangeSelection(selection) ||
290
- rootElement === null ||
291
- !rootElement.contains(nativeSelection.anchorNode))
292
- ) {
293
- setIsText(false);
294
- return;
295
- }
296
-
297
- if (!$isRangeSelection(selection)) {
298
- return;
299
- }
300
-
301
- const node = getSelectedNode(selection);
302
-
303
- // Update text format
304
- setIsBold(selection.hasFormat('bold'));
305
- setIsItalic(selection.hasFormat('italic'));
306
- setIsUnderline(selection.hasFormat('underline'));
307
- setIsStrikethrough(selection.hasFormat('strikethrough'));
308
- setIsSubscript(selection.hasFormat('subscript'));
309
- setIsSuperscript(selection.hasFormat('superscript'));
310
- setIsCode(selection.hasFormat('code'));
311
-
312
- // Update links
313
- const parent = node.getParent();
314
- if ($isLinkNode(parent) || $isLinkNode(node)) {
315
- setIsLink(true);
316
- } else {
317
- setIsLink(false);
318
- }
319
-
320
- if (
321
- !$isCodeHighlightNode(selection.anchor.getNode()) &&
322
- selection.getTextContent() !== ''
323
- ) {
324
- setIsText($isTextNode(node) || $isParagraphNode(node));
325
- } else {
326
- setIsText(false);
327
- }
328
-
329
- const rawTextContent = selection.getTextContent().replace(/\n/g, '');
330
- if (!selection.isCollapsed() && rawTextContent === '') {
331
- setIsText(false);
332
- return;
333
- }
334
- });
335
- }, [editor]);
336
-
337
- useEffect(() => {
338
- document.addEventListener('selectionchange', updatePopup);
339
- return () => {
340
- document.removeEventListener('selectionchange', updatePopup);
341
- };
342
- }, [updatePopup]);
343
-
344
- useEffect(() => {
345
- return mergeRegister(
346
- editor.registerUpdateListener(() => {
347
- updatePopup();
348
- }),
349
- editor.registerRootListener(() => {
350
- if (editor.getRootElement() === null) {
351
- setIsText(false);
352
- }
353
- }),
223
+ editor: LexicalEditor,
224
+ anchorElem: HTMLElement,
225
+ setIsLinkEditMode: Dispatch<boolean>,
226
+ ): React.JSX.Element | null {
227
+
228
+ const [isText, setIsText] = useState(false);
229
+ const [isLink, setIsLink] = useState(false);
230
+ const [isBold, setIsBold] = useState(false);
231
+ const [isItalic, setIsItalic] = useState(false);
232
+ const [isUnderline, setIsUnderline] = useState(false);
233
+ const [isStrikethrough, setIsStrikethrough] = useState(false);
234
+ const [isSubscript, setIsSubscript] = useState(false);
235
+ const [isSuperscript, setIsSuperscript] = useState(false);
236
+ const [isCode, setIsCode] = useState(false);
237
+
238
+ const updatePopup = useCallback(() => {
239
+ editor.getEditorState().read(() => {
240
+ // Should not to pop up the floating toolbar when using IME input
241
+ if (editor.isComposing()) {
242
+ return;
243
+ }
244
+ const selection = $getSelection();
245
+ const nativeSelection = window.getSelection();
246
+ const rootElement = editor.getRootElement();
247
+
248
+ if (
249
+ nativeSelection !== null &&
250
+ (!$isRangeSelection(selection) ||
251
+ rootElement === null ||
252
+ !rootElement.contains(nativeSelection.anchorNode))
253
+ ) {
254
+ setIsText(false);
255
+ return;
256
+ }
257
+
258
+ if (!$isRangeSelection(selection)) {
259
+ return;
260
+ }
261
+
262
+ const node = getSelectedNode(selection);
263
+
264
+ // Update text format
265
+ setIsBold(selection.hasFormat('bold'));
266
+ setIsItalic(selection.hasFormat('italic'));
267
+ setIsUnderline(selection.hasFormat('underline'));
268
+ setIsStrikethrough(selection.hasFormat('strikethrough'));
269
+ setIsSubscript(selection.hasFormat('subscript'));
270
+ setIsSuperscript(selection.hasFormat('superscript'));
271
+ setIsCode(selection.hasFormat('code'));
272
+
273
+ // Update links
274
+ const parent = node.getParent();
275
+ if ($isLinkNode(parent) || $isLinkNode(node)) {
276
+ setIsLink(true);
277
+ } else {
278
+ setIsLink(false);
279
+ }
280
+
281
+ if (
282
+ !$isCodeHighlightNode(selection.anchor.getNode()) &&
283
+ selection.getTextContent() !== ''
284
+ ) {
285
+ setIsText($isTextNode(node) || $isParagraphNode(node));
286
+ } else {
287
+ setIsText(false);
288
+ }
289
+
290
+ const rawTextContent = selection.getTextContent().replace(/\n/g, '');
291
+ if (!selection.isCollapsed() && rawTextContent === '') {
292
+ setIsText(false);
293
+ return;
294
+ }
295
+ });
296
+ }, [editor]);
297
+
298
+ useEffect(() => {
299
+ document.addEventListener('selectionchange', updatePopup);
300
+ return () => {
301
+ document.removeEventListener('selectionchange', updatePopup);
302
+ };
303
+ }, [updatePopup]);
304
+
305
+ useEffect(() => {
306
+ return mergeRegister(
307
+ editor.registerUpdateListener(() => {
308
+ updatePopup();
309
+ }),
310
+ editor.registerRootListener(() => {
311
+ if (editor.getRootElement() === null) {
312
+ setIsText(false);
313
+ }
314
+ }),
315
+ );
316
+ }, [editor, updatePopup]);
317
+
318
+ if (!isText) {
319
+ return null;
320
+ }
321
+
322
+ return createPortal(
323
+ <TextFormatFloatingToolbar
324
+ editor={editor}
325
+ anchorElem={anchorElem}
326
+ isLink={isLink}
327
+ isBold={isBold}
328
+ isItalic={isItalic}
329
+ isStrikethrough={isStrikethrough}
330
+ isSubscript={isSubscript}
331
+ isSuperscript={isSuperscript}
332
+ isUnderline={isUnderline}
333
+ isCode={isCode}
334
+ setIsLinkEditMode={setIsLinkEditMode}
335
+ />,
336
+ anchorElem,
354
337
  );
355
- }, [editor, updatePopup]);
356
-
357
- if (!isText) {
358
- return null;
359
- }
360
-
361
- return createPortal(
362
- <TextFormatFloatingToolbar
363
- editor={editor}
364
- anchorElem={anchorElem}
365
- isLink={isLink}
366
- isBold={isBold}
367
- isItalic={isItalic}
368
- isStrikethrough={isStrikethrough}
369
- isSubscript={isSubscript}
370
- isSuperscript={isSuperscript}
371
- isUnderline={isUnderline}
372
- isCode={isCode}
373
- setIsLinkEditMode={setIsLinkEditMode}
374
- />,
375
- anchorElem,
376
- );
377
338
  }
378
339
 
379
340
  export default function FloatingTextFormatToolbarPlugin({
380
- anchorElem = document.body,
381
- setIsLinkEditMode,
341
+ anchorElem = document.body,
342
+ setIsLinkEditMode,
382
343
  }: {
383
- anchorElem?: HTMLElement;
384
- setIsLinkEditMode: Dispatch<boolean>;
385
- }): JSX.Element | null {
386
- const [editor] = useLexicalComposerContext();
387
- return useFloatingTextFormatToolbar(editor, anchorElem, setIsLinkEditMode);
344
+ anchorElem?: HTMLElement;
345
+ setIsLinkEditMode: Dispatch<boolean>;
346
+ }): React.JSX.Element | null {
347
+ const [editor] = useLexicalComposerContext();
348
+ return useFloatingTextFormatToolbar(editor, anchorElem, setIsLinkEditMode);
388
349
  }