neuphlo-editor 1.8.2 → 2.0.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.
- package/dist/chunk-2DWEJI45.js +1296 -0
- package/dist/chunk-2DWEJI45.js.map +1 -0
- package/dist/chunk-457ETWB6.js +1351 -0
- package/dist/chunk-457ETWB6.js.map +1 -0
- package/dist/chunk-62DYB7FY.js +1305 -0
- package/dist/chunk-62DYB7FY.js.map +1 -0
- package/dist/chunk-DWGPGRTQ.js +1302 -0
- package/dist/chunk-DWGPGRTQ.js.map +1 -0
- package/dist/chunk-EG7NQJRA.js +1324 -0
- package/dist/chunk-EG7NQJRA.js.map +1 -0
- package/dist/chunk-FLLPFFI5.js +1296 -0
- package/dist/chunk-FLLPFFI5.js.map +1 -0
- package/dist/chunk-FVQHB6VC.js +1128 -0
- package/dist/chunk-FVQHB6VC.js.map +1 -0
- package/dist/chunk-GXJGZHKR.js +1326 -0
- package/dist/chunk-GXJGZHKR.js.map +1 -0
- package/dist/chunk-KCPPTLGY.js +1299 -0
- package/dist/chunk-KCPPTLGY.js.map +1 -0
- package/dist/chunk-LHG2NX6C.js +1123 -0
- package/dist/chunk-LHG2NX6C.js.map +1 -0
- package/dist/chunk-OCNM37WJ.js +1289 -0
- package/dist/chunk-OCNM37WJ.js.map +1 -0
- package/dist/chunk-RW6QBMJB.js +1300 -0
- package/dist/chunk-RW6QBMJB.js.map +1 -0
- package/dist/chunk-SOXTEP7H.js +6705 -0
- package/dist/chunk-SOXTEP7H.js.map +1 -0
- package/dist/headless/index.cjs +207 -144
- package/dist/headless/index.cjs.map +1 -1
- package/dist/headless/index.d.cts +9 -25
- package/dist/headless/index.d.ts +9 -25
- package/dist/headless/index.js +1 -1
- package/dist/react/index.cjs +1839 -723
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.css +364 -8
- package/dist/react/index.css.map +1 -1
- package/dist/react/index.d.cts +10 -2
- package/dist/react/index.d.ts +10 -2
- package/dist/react/index.js +1434 -547
- package/dist/react/index.js.map +1 -1
- package/dist/styles.css +410 -8
- package/package.json +7 -2
package/dist/react/index.js
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CodeBlock,
|
|
3
3
|
Command,
|
|
4
|
+
DragHandle,
|
|
4
5
|
EditorCommand,
|
|
5
|
-
EditorCommandEmpty,
|
|
6
6
|
EditorCommandItem,
|
|
7
7
|
EditorCommandList,
|
|
8
8
|
EditorContent,
|
|
9
9
|
EditorRoot,
|
|
10
10
|
ImageBlock,
|
|
11
11
|
Link,
|
|
12
|
+
MarkdownPaste,
|
|
12
13
|
Placeholder,
|
|
13
14
|
StarterKit,
|
|
15
|
+
TableKit,
|
|
14
16
|
createMentionExtension,
|
|
15
17
|
handleCommandNavigation,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
novelStore,
|
|
19
|
+
queryAtom,
|
|
20
|
+
setDragHandleCallbacks,
|
|
21
|
+
store_exports
|
|
22
|
+
} from "../chunk-457ETWB6.js";
|
|
18
23
|
|
|
19
24
|
// src/headless/extensions/extension-kit.ts
|
|
20
25
|
import Collaboration from "@tiptap/extension-collaboration";
|
|
26
|
+
import CollaborationCaret from "@tiptap/extension-collaboration-caret";
|
|
27
|
+
import Underline from "@tiptap/extension-underline";
|
|
21
28
|
|
|
22
29
|
// src/headless/extensions/VideoBlock/VideoBlock.ts
|
|
23
30
|
import { mergeAttributes, Node } from "@tiptap/core";
|
|
@@ -100,6 +107,7 @@ var ExtensionKit = (options) => {
|
|
|
100
107
|
StarterKit.configure({ codeBlock: false, link: false }),
|
|
101
108
|
CodeBlock,
|
|
102
109
|
Link,
|
|
110
|
+
Underline,
|
|
103
111
|
ImageBlock.configure({
|
|
104
112
|
uploadImage: options?.uploadImage,
|
|
105
113
|
nodeView: options?.imageBlockView
|
|
@@ -118,14 +126,29 @@ var ExtensionKit = (options) => {
|
|
|
118
126
|
return enableSlashCommand ? "Press '/' for commands" : "";
|
|
119
127
|
},
|
|
120
128
|
includeChildren: true
|
|
121
|
-
})
|
|
129
|
+
}),
|
|
130
|
+
MarkdownPaste
|
|
122
131
|
];
|
|
132
|
+
if (options?.table !== false) {
|
|
133
|
+
extensions.push(
|
|
134
|
+
TableKit.configure({
|
|
135
|
+
resizable: true,
|
|
136
|
+
lastColumnResizable: true,
|
|
137
|
+
allowTableNodeSelection: true
|
|
138
|
+
})
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
if (options?.dragHandle !== false) {
|
|
142
|
+
extensions.push(DragHandle);
|
|
143
|
+
if (options?.dragHandleCallbacks) {
|
|
144
|
+
setDragHandleCallbacks(options.dragHandleCallbacks);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
123
147
|
if (enableSlashCommand) {
|
|
124
148
|
extensions.push(
|
|
125
149
|
Command.configure({
|
|
126
150
|
suggestion: {
|
|
127
151
|
char: "/",
|
|
128
|
-
render: renderItems,
|
|
129
152
|
allowSpaces: true,
|
|
130
153
|
allowedPrefixes: null
|
|
131
154
|
}
|
|
@@ -151,18 +174,45 @@ var ExtensionKit = (options) => {
|
|
|
151
174
|
field: options.collaboration.field
|
|
152
175
|
})
|
|
153
176
|
);
|
|
177
|
+
if (options.collaboration.provider && options.collaboration.user) {
|
|
178
|
+
extensions.push(
|
|
179
|
+
CollaborationCaret.configure({
|
|
180
|
+
provider: options.collaboration.provider,
|
|
181
|
+
user: options.collaboration.user,
|
|
182
|
+
render: (user) => {
|
|
183
|
+
const cursor = document.createElement("span");
|
|
184
|
+
cursor.classList.add("nph-collab-caret");
|
|
185
|
+
cursor.setAttribute("style", `border-color: ${user.color || "#3b82f6"}`);
|
|
186
|
+
const label = document.createElement("div");
|
|
187
|
+
label.classList.add("nph-collab-caret__label");
|
|
188
|
+
label.setAttribute("style", `background-color: ${user.color || "#3b82f6"}`);
|
|
189
|
+
label.insertBefore(document.createTextNode(user.name || "Anonymous"), null);
|
|
190
|
+
cursor.insertBefore(label, null);
|
|
191
|
+
return cursor;
|
|
192
|
+
},
|
|
193
|
+
selectionRender: (user) => ({
|
|
194
|
+
nodeName: "span",
|
|
195
|
+
class: "nph-collab-selection",
|
|
196
|
+
style: `background-color: ${user.color || "#3b82f6"}20`
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
);
|
|
200
|
+
}
|
|
154
201
|
}
|
|
155
202
|
return extensions;
|
|
156
203
|
};
|
|
157
204
|
var extension_kit_default = ExtensionKit;
|
|
158
205
|
|
|
159
206
|
// src/react/menus/TextMenu.tsx
|
|
207
|
+
import { NodeSelection } from "@tiptap/pm/state";
|
|
160
208
|
import { useCurrentEditor, useEditorState } from "@tiptap/react";
|
|
161
209
|
import { BubbleMenu } from "@tiptap/react/menus";
|
|
162
210
|
import {
|
|
163
211
|
IconBold,
|
|
164
212
|
IconItalic,
|
|
213
|
+
IconUnderline,
|
|
165
214
|
IconStrikethrough,
|
|
215
|
+
IconCode as IconCode2,
|
|
166
216
|
IconArrowBackUp,
|
|
167
217
|
IconLink,
|
|
168
218
|
IconCheck,
|
|
@@ -435,6 +485,9 @@ function MenuList({
|
|
|
435
485
|
|
|
436
486
|
// src/react/menus/TextMenu.tsx
|
|
437
487
|
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
488
|
+
function Separator() {
|
|
489
|
+
return /* @__PURE__ */ jsx2("div", { className: "nph-bubble-separator" });
|
|
490
|
+
}
|
|
438
491
|
function TextMenu({
|
|
439
492
|
className,
|
|
440
493
|
leadingExtras,
|
|
@@ -451,6 +504,7 @@ function TextMenu({
|
|
|
451
504
|
return {
|
|
452
505
|
isBold: ctx.editor.isActive("bold"),
|
|
453
506
|
isItalic: ctx.editor.isActive("italic"),
|
|
507
|
+
isUnderline: ctx.editor.isActive("underline"),
|
|
454
508
|
isStrike: ctx.editor.isActive("strike"),
|
|
455
509
|
isCode: ctx.editor.isActive("code"),
|
|
456
510
|
isCodeBlock: ctx.editor.isActive("codeBlock"),
|
|
@@ -496,6 +550,7 @@ function TextMenu({
|
|
|
496
550
|
}
|
|
497
551
|
return has;
|
|
498
552
|
};
|
|
553
|
+
const isInCode = editorState.isCode || editorState.isCodeBlock;
|
|
499
554
|
return /* @__PURE__ */ jsx2(
|
|
500
555
|
BubbleMenu,
|
|
501
556
|
{
|
|
@@ -504,7 +559,7 @@ function TextMenu({
|
|
|
504
559
|
if (e.isActive("imageBlock")) return false;
|
|
505
560
|
if (e.isActive("videoBlock")) return false;
|
|
506
561
|
const { selection } = state;
|
|
507
|
-
if (selection
|
|
562
|
+
if (selection instanceof NodeSelection) return false;
|
|
508
563
|
if (from === to) return false;
|
|
509
564
|
if (e.isActive("link")) return false;
|
|
510
565
|
let hasImage = false;
|
|
@@ -520,6 +575,7 @@ function TextMenu({
|
|
|
520
575
|
children: /* @__PURE__ */ jsxs2("div", { className: className ? `bubble-menu ${className}` : "bubble-menu", children: [
|
|
521
576
|
leadingExtras && editor ? leadingExtras.map((renderExtra, index) => /* @__PURE__ */ jsx2(Fragment, { children: renderExtra(editor) }, `leading-extra-${index}`)) : null,
|
|
522
577
|
/* @__PURE__ */ jsx2(MenuList, { editor }),
|
|
578
|
+
/* @__PURE__ */ jsx2(Separator, {}),
|
|
523
579
|
/* @__PURE__ */ jsx2(
|
|
524
580
|
"button",
|
|
525
581
|
{
|
|
@@ -527,8 +583,8 @@ function TextMenu({
|
|
|
527
583
|
onMouseDown: (e) => e.preventDefault(),
|
|
528
584
|
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
529
585
|
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editorState.isBold ? " is-active" : ""}`,
|
|
530
|
-
disabled:
|
|
531
|
-
"aria-disabled":
|
|
586
|
+
disabled: isInCode,
|
|
587
|
+
"aria-disabled": isInCode,
|
|
532
588
|
"aria-pressed": editorState.isBold,
|
|
533
589
|
"aria-label": "Toggle bold",
|
|
534
590
|
children: /* @__PURE__ */ jsx2(IconBold, { size: 16 })
|
|
@@ -541,13 +597,27 @@ function TextMenu({
|
|
|
541
597
|
onMouseDown: (e) => e.preventDefault(),
|
|
542
598
|
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
543
599
|
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editorState.isItalic ? " is-active" : ""}`,
|
|
544
|
-
disabled:
|
|
545
|
-
"aria-disabled":
|
|
600
|
+
disabled: isInCode,
|
|
601
|
+
"aria-disabled": isInCode,
|
|
546
602
|
"aria-pressed": editorState.isItalic,
|
|
547
603
|
"aria-label": "Toggle italic",
|
|
548
604
|
children: /* @__PURE__ */ jsx2(IconItalic, { size: 16 })
|
|
549
605
|
}
|
|
550
606
|
),
|
|
607
|
+
/* @__PURE__ */ jsx2(
|
|
608
|
+
"button",
|
|
609
|
+
{
|
|
610
|
+
type: "button",
|
|
611
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
612
|
+
onClick: () => editor.chain().focus().toggleUnderline().run(),
|
|
613
|
+
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editorState.isUnderline ? " is-active" : ""}`,
|
|
614
|
+
disabled: isInCode,
|
|
615
|
+
"aria-disabled": isInCode,
|
|
616
|
+
"aria-pressed": editorState.isUnderline,
|
|
617
|
+
"aria-label": "Toggle underline",
|
|
618
|
+
children: /* @__PURE__ */ jsx2(IconUnderline, { size: 16 })
|
|
619
|
+
}
|
|
620
|
+
),
|
|
551
621
|
/* @__PURE__ */ jsx2(
|
|
552
622
|
"button",
|
|
553
623
|
{
|
|
@@ -555,13 +625,28 @@ function TextMenu({
|
|
|
555
625
|
onMouseDown: (e) => e.preventDefault(),
|
|
556
626
|
onClick: () => editor.chain().focus().toggleStrike().run(),
|
|
557
627
|
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editorState.isStrike ? " is-active" : ""}`,
|
|
558
|
-
disabled:
|
|
559
|
-
"aria-disabled":
|
|
628
|
+
disabled: isInCode,
|
|
629
|
+
"aria-disabled": isInCode,
|
|
560
630
|
"aria-pressed": editorState.isStrike,
|
|
561
631
|
"aria-label": "Toggle strike",
|
|
562
632
|
children: /* @__PURE__ */ jsx2(IconStrikethrough, { size: 16 })
|
|
563
633
|
}
|
|
564
634
|
),
|
|
635
|
+
/* @__PURE__ */ jsx2(
|
|
636
|
+
"button",
|
|
637
|
+
{
|
|
638
|
+
type: "button",
|
|
639
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
640
|
+
onClick: () => editor.chain().focus().toggleCode().run(),
|
|
641
|
+
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editorState.isCode ? " is-active" : ""}`,
|
|
642
|
+
disabled: editorState.isCodeBlock,
|
|
643
|
+
"aria-disabled": editorState.isCodeBlock,
|
|
644
|
+
"aria-pressed": editorState.isCode,
|
|
645
|
+
"aria-label": "Toggle inline code",
|
|
646
|
+
children: /* @__PURE__ */ jsx2(IconCode2, { size: 16 })
|
|
647
|
+
}
|
|
648
|
+
),
|
|
649
|
+
/* @__PURE__ */ jsx2(Separator, {}),
|
|
565
650
|
!isAddingLink ? /* @__PURE__ */ jsx2(
|
|
566
651
|
"button",
|
|
567
652
|
{
|
|
@@ -569,8 +654,8 @@ function TextMenu({
|
|
|
569
654
|
onMouseDown: (e) => e.preventDefault(),
|
|
570
655
|
onClick: () => setIsAddingLink(true),
|
|
571
656
|
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editor.isActive("link") ? " is-active" : ""}`,
|
|
572
|
-
disabled:
|
|
573
|
-
"aria-disabled":
|
|
657
|
+
disabled: isInCode,
|
|
658
|
+
"aria-disabled": isInCode,
|
|
574
659
|
"aria-pressed": editor.isActive("link"),
|
|
575
660
|
"aria-label": "Add link",
|
|
576
661
|
children: /* @__PURE__ */ jsx2(IconLink, { size: 16 })
|
|
@@ -632,20 +717,23 @@ function TextMenu({
|
|
|
632
717
|
(() => {
|
|
633
718
|
const hasInlineMarks = hasAnyMarksInSelection();
|
|
634
719
|
const isPlainParagraph = editorState.isParagraph && !editorState.isHeading && !editorState.isList && !editorState.isBlockquote && !editorState.isCodeBlock && !hasInlineMarks;
|
|
635
|
-
return /* @__PURE__ */
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
720
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
721
|
+
/* @__PURE__ */ jsx2(Separator, {}),
|
|
722
|
+
/* @__PURE__ */ jsx2(
|
|
723
|
+
"button",
|
|
724
|
+
{
|
|
725
|
+
type: "button",
|
|
726
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
727
|
+
onClick: () => editor.chain().focus().clearNodes().setParagraph().unsetAllMarks().run(),
|
|
728
|
+
className: "nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon",
|
|
729
|
+
"aria-label": "Revert to paragraph",
|
|
730
|
+
title: "Revert to paragraph",
|
|
731
|
+
disabled: isPlainParagraph,
|
|
732
|
+
"aria-disabled": isPlainParagraph,
|
|
733
|
+
children: /* @__PURE__ */ jsx2(IconArrowBackUp, { size: 16 })
|
|
734
|
+
}
|
|
735
|
+
)
|
|
736
|
+
] });
|
|
649
737
|
})(),
|
|
650
738
|
trailingExtras && editor ? trailingExtras.map((renderExtra, index) => /* @__PURE__ */ jsx2(Fragment, { children: renderExtra(editor) }, `trailing-extra-${index}`)) : null
|
|
651
739
|
] })
|
|
@@ -655,10 +743,9 @@ function TextMenu({
|
|
|
655
743
|
|
|
656
744
|
// src/react/menus/SlashMenu.tsx
|
|
657
745
|
import { useCurrentEditor as useCurrentEditor2 } from "@tiptap/react";
|
|
746
|
+
import { useAtomValue } from "jotai";
|
|
658
747
|
import {
|
|
659
|
-
|
|
660
|
-
IconItalic as IconItalic2,
|
|
661
|
-
IconStrikethrough as IconStrikethrough2,
|
|
748
|
+
IconTypography as IconTypography2,
|
|
662
749
|
IconH1 as IconH12,
|
|
663
750
|
IconH2 as IconH22,
|
|
664
751
|
IconH3 as IconH32,
|
|
@@ -666,323 +753,182 @@ import {
|
|
|
666
753
|
IconList as IconList2,
|
|
667
754
|
IconListNumbers as IconListNumbers2,
|
|
668
755
|
IconBlockquote as IconBlockquote2,
|
|
669
|
-
IconCode as
|
|
756
|
+
IconCode as IconCode3,
|
|
670
757
|
IconSourceCode as IconSourceCode2,
|
|
671
758
|
IconPhoto,
|
|
672
|
-
IconVideo
|
|
759
|
+
IconVideo,
|
|
760
|
+
IconMinus,
|
|
761
|
+
IconTable,
|
|
762
|
+
IconListCheck
|
|
673
763
|
} from "@tabler/icons-react";
|
|
764
|
+
import { useMemo } from "react";
|
|
674
765
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
766
|
+
var SLASH_COMMANDS = [
|
|
767
|
+
{
|
|
768
|
+
group: "Format",
|
|
769
|
+
items: [
|
|
770
|
+
{
|
|
771
|
+
value: "paragraph text",
|
|
772
|
+
label: "Paragraph",
|
|
773
|
+
description: "Plain text block",
|
|
774
|
+
icon: IconTypography2,
|
|
775
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).setParagraph().run()
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
value: "heading1 h1",
|
|
779
|
+
label: "Heading 1",
|
|
780
|
+
description: "Large section heading",
|
|
781
|
+
icon: IconH12,
|
|
782
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleHeading({ level: 1 }).run()
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
value: "heading2 h2",
|
|
786
|
+
label: "Heading 2",
|
|
787
|
+
description: "Medium section heading",
|
|
788
|
+
icon: IconH22,
|
|
789
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleHeading({ level: 2 }).run()
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
value: "heading3 h3",
|
|
793
|
+
label: "Heading 3",
|
|
794
|
+
description: "Small section heading",
|
|
795
|
+
icon: IconH32,
|
|
796
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleHeading({ level: 3 }).run()
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
value: "heading4 h4",
|
|
800
|
+
label: "Heading 4",
|
|
801
|
+
description: "Subsection heading",
|
|
802
|
+
icon: IconH42,
|
|
803
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleHeading({ level: 4 }).run()
|
|
804
|
+
}
|
|
805
|
+
]
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
group: "Lists",
|
|
809
|
+
items: [
|
|
810
|
+
{
|
|
811
|
+
value: "bullet list ul",
|
|
812
|
+
label: "Bullet List",
|
|
813
|
+
description: "Unordered list of items",
|
|
814
|
+
icon: IconList2,
|
|
815
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleBulletList().run()
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
value: "ordered list ol numbered",
|
|
819
|
+
label: "Numbered List",
|
|
820
|
+
description: "Ordered list of items",
|
|
821
|
+
icon: IconListNumbers2,
|
|
822
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleOrderedList().run()
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
value: "task list todo checklist",
|
|
826
|
+
label: "Task List",
|
|
827
|
+
description: "Checklist of to-do items",
|
|
828
|
+
icon: IconListCheck,
|
|
829
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleTaskList().run()
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
value: "quote blockquote",
|
|
833
|
+
label: "Blockquote",
|
|
834
|
+
description: "Highlight a quote or excerpt",
|
|
835
|
+
icon: IconBlockquote2,
|
|
836
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleBlockquote().run()
|
|
837
|
+
}
|
|
838
|
+
]
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
group: "Insert",
|
|
842
|
+
items: [
|
|
843
|
+
{
|
|
844
|
+
value: "image photo picture",
|
|
845
|
+
label: "Image",
|
|
846
|
+
description: "Upload or embed an image",
|
|
847
|
+
icon: IconPhoto,
|
|
848
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).setImageBlock({ src: "" }).run()
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
value: "video embed youtube vimeo",
|
|
852
|
+
label: "Video",
|
|
853
|
+
description: "Embed a YouTube or Vimeo video",
|
|
854
|
+
icon: IconVideo,
|
|
855
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).setVideoBlock({ src: "" }).run()
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
value: "table grid",
|
|
859
|
+
label: "Table",
|
|
860
|
+
description: "Insert a table with rows and columns",
|
|
861
|
+
icon: IconTable,
|
|
862
|
+
onCommand: ({ editor, range }) => {
|
|
863
|
+
editor.chain().focus().deleteRange(range).run();
|
|
864
|
+
if (editor.commands.insertTable) {
|
|
865
|
+
editor.commands.insertTable({ rows: 3, cols: 3, withHeaderRow: true });
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
value: "code block codeblock",
|
|
871
|
+
label: "Code Block",
|
|
872
|
+
description: "Syntax-highlighted code block",
|
|
873
|
+
icon: IconSourceCode2,
|
|
874
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleCodeBlock().run()
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
value: "code inline",
|
|
878
|
+
label: "Inline Code",
|
|
879
|
+
description: "Mark text as inline code",
|
|
880
|
+
icon: IconCode3,
|
|
881
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleCode().run()
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
value: "divider horizontal rule separator",
|
|
885
|
+
label: "Divider",
|
|
886
|
+
description: "Horizontal separator line",
|
|
887
|
+
icon: IconMinus,
|
|
888
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).setHorizontalRule().run()
|
|
889
|
+
}
|
|
890
|
+
]
|
|
891
|
+
}
|
|
892
|
+
];
|
|
675
893
|
function SlashMenu({ className }) {
|
|
676
894
|
const { editor } = useCurrentEditor2();
|
|
895
|
+
const query = useAtomValue(queryAtom, { store: novelStore });
|
|
896
|
+
const filteredGroups = useMemo(() => {
|
|
897
|
+
if (!query) return SLASH_COMMANDS;
|
|
898
|
+
const q = query.toLowerCase();
|
|
899
|
+
return SLASH_COMMANDS.map((group) => ({
|
|
900
|
+
...group,
|
|
901
|
+
items: group.items.filter((item) => item.value.toLowerCase().includes(q))
|
|
902
|
+
})).filter((group) => group.items.length > 0);
|
|
903
|
+
}, [query]);
|
|
677
904
|
if (!editor) return null;
|
|
678
|
-
return /* @__PURE__ */ jsx3(EditorCommand, { className: className ?? "nph-command", children: /* @__PURE__ */
|
|
905
|
+
return /* @__PURE__ */ jsx3(EditorCommand, { className: className ?? "nph-command", children: /* @__PURE__ */ jsx3(
|
|
679
906
|
EditorCommandList,
|
|
680
907
|
{
|
|
681
908
|
className: "nph-command__list",
|
|
682
909
|
style: { display: "flex", flexDirection: "column", gap: 2 },
|
|
683
|
-
children: [
|
|
684
|
-
/* @__PURE__ */ jsx3(
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
/* @__PURE__ */ jsx3("span", { children: "Bold" })
|
|
701
|
-
]
|
|
702
|
-
}
|
|
703
|
-
)
|
|
704
|
-
}
|
|
705
|
-
),
|
|
706
|
-
/* @__PURE__ */ jsx3(
|
|
707
|
-
EditorCommandItem,
|
|
708
|
-
{
|
|
709
|
-
value: "italic",
|
|
710
|
-
className: "nph-command__item",
|
|
711
|
-
onCommand: ({
|
|
712
|
-
editor: editor2,
|
|
713
|
-
range
|
|
714
|
-
}) => editor2.chain().focus().deleteRange(range).toggleItalic().run(),
|
|
715
|
-
children: /* @__PURE__ */ jsxs3(
|
|
716
|
-
"span",
|
|
717
|
-
{
|
|
718
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
719
|
-
children: [
|
|
720
|
-
/* @__PURE__ */ jsx3(IconItalic2, { size: 16 }),
|
|
721
|
-
/* @__PURE__ */ jsx3("span", { children: "Italic" })
|
|
722
|
-
]
|
|
723
|
-
}
|
|
724
|
-
)
|
|
725
|
-
}
|
|
726
|
-
),
|
|
727
|
-
/* @__PURE__ */ jsx3(
|
|
728
|
-
EditorCommandItem,
|
|
729
|
-
{
|
|
730
|
-
value: "strike",
|
|
731
|
-
className: "nph-command__item",
|
|
732
|
-
onCommand: ({
|
|
733
|
-
editor: editor2,
|
|
734
|
-
range
|
|
735
|
-
}) => editor2.chain().focus().deleteRange(range).toggleStrike().run(),
|
|
736
|
-
children: /* @__PURE__ */ jsxs3(
|
|
737
|
-
"span",
|
|
738
|
-
{
|
|
739
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
740
|
-
children: [
|
|
741
|
-
/* @__PURE__ */ jsx3(IconStrikethrough2, { size: 16 }),
|
|
742
|
-
/* @__PURE__ */ jsx3("span", { children: "Strike" })
|
|
743
|
-
]
|
|
744
|
-
}
|
|
745
|
-
)
|
|
746
|
-
}
|
|
747
|
-
),
|
|
748
|
-
/* @__PURE__ */ jsx3(
|
|
749
|
-
EditorCommandItem,
|
|
750
|
-
{
|
|
751
|
-
value: "heading1 h1",
|
|
752
|
-
className: "nph-command__item",
|
|
753
|
-
onCommand: ({
|
|
754
|
-
editor: editor2,
|
|
755
|
-
range
|
|
756
|
-
}) => editor2.chain().focus().deleteRange(range).toggleHeading({ level: 1 }).run(),
|
|
757
|
-
children: /* @__PURE__ */ jsxs3(
|
|
758
|
-
"span",
|
|
759
|
-
{
|
|
760
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
761
|
-
children: [
|
|
762
|
-
/* @__PURE__ */ jsx3(IconH12, { size: 16 }),
|
|
763
|
-
/* @__PURE__ */ jsx3("span", { children: "Heading 1" })
|
|
764
|
-
]
|
|
765
|
-
}
|
|
766
|
-
)
|
|
767
|
-
}
|
|
768
|
-
),
|
|
769
|
-
/* @__PURE__ */ jsx3(
|
|
770
|
-
EditorCommandItem,
|
|
771
|
-
{
|
|
772
|
-
value: "heading2 h2",
|
|
773
|
-
className: "nph-command__item",
|
|
774
|
-
onCommand: ({
|
|
775
|
-
editor: editor2,
|
|
776
|
-
range
|
|
777
|
-
}) => editor2.chain().focus().deleteRange(range).toggleHeading({ level: 2 }).run(),
|
|
778
|
-
children: /* @__PURE__ */ jsxs3(
|
|
779
|
-
"span",
|
|
780
|
-
{
|
|
781
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
782
|
-
children: [
|
|
783
|
-
/* @__PURE__ */ jsx3(IconH22, { size: 16 }),
|
|
784
|
-
/* @__PURE__ */ jsx3("span", { children: "Heading 2" })
|
|
785
|
-
]
|
|
786
|
-
}
|
|
787
|
-
)
|
|
788
|
-
}
|
|
789
|
-
),
|
|
790
|
-
/* @__PURE__ */ jsx3(
|
|
791
|
-
EditorCommandItem,
|
|
792
|
-
{
|
|
793
|
-
value: "heading3 h3",
|
|
794
|
-
className: "nph-command__item",
|
|
795
|
-
onCommand: ({
|
|
796
|
-
editor: editor2,
|
|
797
|
-
range
|
|
798
|
-
}) => editor2.chain().focus().deleteRange(range).toggleHeading({ level: 3 }).run(),
|
|
799
|
-
children: /* @__PURE__ */ jsxs3(
|
|
800
|
-
"span",
|
|
801
|
-
{
|
|
802
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
803
|
-
children: [
|
|
804
|
-
/* @__PURE__ */ jsx3(IconH32, { size: 16 }),
|
|
805
|
-
/* @__PURE__ */ jsx3("span", { children: "Heading 3" })
|
|
806
|
-
]
|
|
807
|
-
}
|
|
808
|
-
)
|
|
809
|
-
}
|
|
810
|
-
),
|
|
811
|
-
/* @__PURE__ */ jsx3(
|
|
812
|
-
EditorCommandItem,
|
|
813
|
-
{
|
|
814
|
-
value: "heading4 h4",
|
|
815
|
-
className: "nph-command__item",
|
|
816
|
-
onCommand: ({
|
|
817
|
-
editor: editor2,
|
|
818
|
-
range
|
|
819
|
-
}) => editor2.chain().focus().deleteRange(range).toggleHeading({ level: 4 }).run(),
|
|
820
|
-
children: /* @__PURE__ */ jsxs3(
|
|
821
|
-
"span",
|
|
822
|
-
{
|
|
823
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
824
|
-
children: [
|
|
825
|
-
/* @__PURE__ */ jsx3(IconH42, { size: 16 }),
|
|
826
|
-
/* @__PURE__ */ jsx3("span", { children: "Heading 4" })
|
|
827
|
-
]
|
|
828
|
-
}
|
|
829
|
-
)
|
|
830
|
-
}
|
|
831
|
-
),
|
|
832
|
-
/* @__PURE__ */ jsx3(
|
|
833
|
-
EditorCommandItem,
|
|
834
|
-
{
|
|
835
|
-
value: "bullet list ul",
|
|
836
|
-
className: "nph-command__item",
|
|
837
|
-
onCommand: ({
|
|
838
|
-
editor: editor2,
|
|
839
|
-
range
|
|
840
|
-
}) => editor2.chain().focus().deleteRange(range).toggleBulletList().run(),
|
|
841
|
-
children: /* @__PURE__ */ jsxs3(
|
|
842
|
-
"span",
|
|
843
|
-
{
|
|
844
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
845
|
-
children: [
|
|
846
|
-
/* @__PURE__ */ jsx3(IconList2, { size: 16 }),
|
|
847
|
-
/* @__PURE__ */ jsx3("span", { children: "Bullet list" })
|
|
848
|
-
]
|
|
849
|
-
}
|
|
850
|
-
)
|
|
851
|
-
}
|
|
852
|
-
),
|
|
853
|
-
/* @__PURE__ */ jsx3(
|
|
854
|
-
EditorCommandItem,
|
|
855
|
-
{
|
|
856
|
-
value: "ordered list ol",
|
|
857
|
-
className: "nph-command__item",
|
|
858
|
-
onCommand: ({
|
|
859
|
-
editor: editor2,
|
|
860
|
-
range
|
|
861
|
-
}) => editor2.chain().focus().deleteRange(range).toggleOrderedList().run(),
|
|
862
|
-
children: /* @__PURE__ */ jsxs3(
|
|
863
|
-
"span",
|
|
864
|
-
{
|
|
865
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
866
|
-
children: [
|
|
867
|
-
/* @__PURE__ */ jsx3(IconListNumbers2, { size: 16 }),
|
|
868
|
-
/* @__PURE__ */ jsx3("span", { children: "Ordered list" })
|
|
869
|
-
]
|
|
870
|
-
}
|
|
871
|
-
)
|
|
872
|
-
}
|
|
873
|
-
),
|
|
874
|
-
/* @__PURE__ */ jsx3(
|
|
875
|
-
EditorCommandItem,
|
|
876
|
-
{
|
|
877
|
-
value: "quote blockquote",
|
|
878
|
-
className: "nph-command__item",
|
|
879
|
-
onCommand: ({
|
|
880
|
-
editor: editor2,
|
|
881
|
-
range
|
|
882
|
-
}) => editor2.chain().focus().deleteRange(range).toggleBlockquote().run(),
|
|
883
|
-
children: /* @__PURE__ */ jsxs3(
|
|
884
|
-
"span",
|
|
885
|
-
{
|
|
886
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
887
|
-
children: [
|
|
888
|
-
/* @__PURE__ */ jsx3(IconBlockquote2, { size: 16 }),
|
|
889
|
-
/* @__PURE__ */ jsx3("span", { children: "Quote" })
|
|
890
|
-
]
|
|
891
|
-
}
|
|
892
|
-
)
|
|
893
|
-
}
|
|
894
|
-
),
|
|
895
|
-
/* @__PURE__ */ jsx3(
|
|
896
|
-
EditorCommandItem,
|
|
897
|
-
{
|
|
898
|
-
value: "code inline",
|
|
899
|
-
className: "nph-command__item",
|
|
900
|
-
onCommand: ({
|
|
901
|
-
editor: editor2,
|
|
902
|
-
range
|
|
903
|
-
}) => editor2.chain().focus().deleteRange(range).toggleCode().run(),
|
|
904
|
-
children: /* @__PURE__ */ jsxs3(
|
|
905
|
-
"span",
|
|
906
|
-
{
|
|
907
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
908
|
-
children: [
|
|
909
|
-
/* @__PURE__ */ jsx3(IconCode2, { size: 16 }),
|
|
910
|
-
/* @__PURE__ */ jsx3("span", { children: "Code" })
|
|
911
|
-
]
|
|
912
|
-
}
|
|
913
|
-
)
|
|
914
|
-
}
|
|
915
|
-
),
|
|
916
|
-
/* @__PURE__ */ jsx3(
|
|
917
|
-
EditorCommandItem,
|
|
918
|
-
{
|
|
919
|
-
value: "code block codeblock",
|
|
920
|
-
className: "nph-command__item",
|
|
921
|
-
onCommand: ({
|
|
922
|
-
editor: editor2,
|
|
923
|
-
range
|
|
924
|
-
}) => editor2.chain().focus().deleteRange(range).toggleCodeBlock().run(),
|
|
925
|
-
children: /* @__PURE__ */ jsxs3(
|
|
926
|
-
"span",
|
|
927
|
-
{
|
|
928
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
929
|
-
children: [
|
|
930
|
-
/* @__PURE__ */ jsx3(IconSourceCode2, { size: 16 }),
|
|
931
|
-
/* @__PURE__ */ jsx3("span", { children: "Code Block" })
|
|
932
|
-
]
|
|
933
|
-
}
|
|
934
|
-
)
|
|
935
|
-
}
|
|
936
|
-
),
|
|
937
|
-
/* @__PURE__ */ jsx3(
|
|
938
|
-
EditorCommandItem,
|
|
939
|
-
{
|
|
940
|
-
value: "image photo picture block",
|
|
941
|
-
className: "nph-command__item",
|
|
942
|
-
onCommand: ({
|
|
943
|
-
editor: editor2,
|
|
944
|
-
range
|
|
945
|
-
}) => {
|
|
946
|
-
;
|
|
947
|
-
editor2.chain().focus().deleteRange(range).setImageBlock({ src: "" }).run();
|
|
948
|
-
},
|
|
949
|
-
children: /* @__PURE__ */ jsxs3(
|
|
950
|
-
"span",
|
|
951
|
-
{
|
|
952
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
953
|
-
children: [
|
|
954
|
-
/* @__PURE__ */ jsx3(IconPhoto, { size: 16 }),
|
|
955
|
-
/* @__PURE__ */ jsx3("span", { children: "Image" })
|
|
956
|
-
]
|
|
957
|
-
}
|
|
958
|
-
)
|
|
959
|
-
}
|
|
960
|
-
),
|
|
961
|
-
/* @__PURE__ */ jsx3(
|
|
962
|
-
EditorCommandItem,
|
|
963
|
-
{
|
|
964
|
-
value: "video embed youtube vimeo",
|
|
965
|
-
className: "nph-command__item",
|
|
966
|
-
onCommand: ({
|
|
967
|
-
editor: editor2,
|
|
968
|
-
range
|
|
969
|
-
}) => {
|
|
970
|
-
;
|
|
971
|
-
editor2.chain().focus().deleteRange(range).setVideoBlock({ src: "" }).run();
|
|
910
|
+
children: filteredGroups.length === 0 ? /* @__PURE__ */ jsx3("div", { style: { padding: "8px 12px", fontSize: 14, color: "var(--muted-foreground, #6b7280)" }, children: "No commands found" }) : filteredGroups.map((group) => /* @__PURE__ */ jsxs3("div", { children: [
|
|
911
|
+
/* @__PURE__ */ jsx3("div", { className: "nph-command__group-header", children: group.group }),
|
|
912
|
+
group.items.map((item) => {
|
|
913
|
+
const Icon = item.icon;
|
|
914
|
+
return /* @__PURE__ */ jsxs3(
|
|
915
|
+
EditorCommandItem,
|
|
916
|
+
{
|
|
917
|
+
value: item.value,
|
|
918
|
+
className: "nph-command__item",
|
|
919
|
+
onCommand: item.onCommand,
|
|
920
|
+
children: [
|
|
921
|
+
/* @__PURE__ */ jsx3("div", { className: "nph-command__item-icon", children: /* @__PURE__ */ jsx3(Icon, { size: 18 }) }),
|
|
922
|
+
/* @__PURE__ */ jsxs3("div", { className: "nph-command__item-content", children: [
|
|
923
|
+
/* @__PURE__ */ jsx3("span", { className: "nph-command__item-title", children: item.label }),
|
|
924
|
+
/* @__PURE__ */ jsx3("span", { className: "nph-command__item-description", children: item.description })
|
|
925
|
+
] })
|
|
926
|
+
]
|
|
972
927
|
},
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
children: [
|
|
978
|
-
/* @__PURE__ */ jsx3(IconVideo, { size: 16 }),
|
|
979
|
-
/* @__PURE__ */ jsx3("span", { children: "Video" })
|
|
980
|
-
]
|
|
981
|
-
}
|
|
982
|
-
)
|
|
983
|
-
}
|
|
984
|
-
)
|
|
985
|
-
]
|
|
928
|
+
item.value
|
|
929
|
+
);
|
|
930
|
+
})
|
|
931
|
+
] }, group.group))
|
|
986
932
|
}
|
|
987
933
|
) });
|
|
988
934
|
}
|
|
@@ -1139,6 +1085,7 @@ function LinkMenu() {
|
|
|
1139
1085
|
}
|
|
1140
1086
|
|
|
1141
1087
|
// src/react/menus/ImageMenu.tsx
|
|
1088
|
+
import { NodeSelection as NodeSelection2 } from "@tiptap/pm/state";
|
|
1142
1089
|
import { useCurrentEditor as useCurrentEditor4 } from "@tiptap/react";
|
|
1143
1090
|
import { BubbleMenu as BubbleMenu3 } from "@tiptap/react/menus";
|
|
1144
1091
|
import {
|
|
@@ -1206,7 +1153,7 @@ function ImageMenu({
|
|
|
1206
1153
|
shouldShow: ({ editor: e, state }) => {
|
|
1207
1154
|
if (!e.isActive("imageBlock")) return false;
|
|
1208
1155
|
const { selection } = state;
|
|
1209
|
-
const isNodeSelection = selection
|
|
1156
|
+
const isNodeSelection = selection instanceof NodeSelection2;
|
|
1210
1157
|
return isNodeSelection;
|
|
1211
1158
|
},
|
|
1212
1159
|
updateDelay: 0,
|
|
@@ -1319,11 +1266,12 @@ function ImageMenu({
|
|
|
1319
1266
|
|
|
1320
1267
|
// src/react/menus/ImageBlock/ImageBlockView.tsx
|
|
1321
1268
|
import { NodeViewWrapper } from "@tiptap/react";
|
|
1322
|
-
import { useCallback as
|
|
1269
|
+
import { useCallback as useCallback7, useRef as useRef7 } from "react";
|
|
1323
1270
|
|
|
1324
1271
|
// src/react/menus/ImageBlock/ImageBlockMenu.tsx
|
|
1325
|
-
import {
|
|
1326
|
-
import {
|
|
1272
|
+
import { NodeSelection as NodeSelection3 } from "@tiptap/pm/state";
|
|
1273
|
+
import { useEditorState as useEditorState2 } from "@tiptap/react";
|
|
1274
|
+
import { useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
1327
1275
|
|
|
1328
1276
|
// src/react/menus/ImageBlock/ImageBlockWidth.tsx
|
|
1329
1277
|
import { memo, useCallback as useCallback3, useEffect as useEffect5, useState as useState5 } from "react";
|
|
@@ -1393,57 +1341,28 @@ import {
|
|
|
1393
1341
|
IconTrash as IconTrash3
|
|
1394
1342
|
} from "@tabler/icons-react";
|
|
1395
1343
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1396
|
-
var ImageBlockMenu = ({ editor, appendTo }) => {
|
|
1344
|
+
var ImageBlockMenu = ({ editor, getPos, appendTo }) => {
|
|
1397
1345
|
const menuRef = useRef4(null);
|
|
1398
|
-
const
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
const
|
|
1405
|
-
|
|
1406
|
-
const
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
editor.off("transaction", update);
|
|
1415
|
-
};
|
|
1416
|
-
}, [editor]);
|
|
1417
|
-
const getReferenceClientRect = useCallback4(() => {
|
|
1418
|
-
if (!editor) return new DOMRect(-1e3, -1e3, 0, 0);
|
|
1419
|
-
const { view } = editor;
|
|
1420
|
-
const { state } = view;
|
|
1421
|
-
const { selection } = state;
|
|
1422
|
-
const node = selection instanceof window.ProseMirror?.state?.NodeSelection ? selection.node : null;
|
|
1423
|
-
if (node && node.type.name === "imageBlock") {
|
|
1424
|
-
const nodePos = selection.from;
|
|
1425
|
-
const domNode = view.nodeDOM(nodePos);
|
|
1426
|
-
if (domNode && domNode instanceof HTMLElement) {
|
|
1427
|
-
return domNode.getBoundingClientRect();
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
const imageBlockElements = document.querySelectorAll("[data-node-view-wrapper]");
|
|
1431
|
-
for (const el of Array.from(imageBlockElements)) {
|
|
1432
|
-
if (el.querySelector("img")) {
|
|
1433
|
-
return el.getBoundingClientRect();
|
|
1346
|
+
const { isVisible, align, width } = useEditorState2({
|
|
1347
|
+
editor,
|
|
1348
|
+
selector: (ctx) => {
|
|
1349
|
+
if (!ctx.editor) return { isVisible: false, align: "center", width: 100 };
|
|
1350
|
+
const { state } = ctx.editor;
|
|
1351
|
+
const { selection } = state;
|
|
1352
|
+
const isNodeSel = selection instanceof NodeSelection3;
|
|
1353
|
+
const isThisNode = isNodeSel && selection.from === getPos();
|
|
1354
|
+
const visible = isThisNode;
|
|
1355
|
+
let currentAlign = "center";
|
|
1356
|
+
let currentWidth = 100;
|
|
1357
|
+
if (visible) {
|
|
1358
|
+
const attrs = ctx.editor.getAttributes("imageBlock");
|
|
1359
|
+
currentAlign = attrs.align || "center";
|
|
1360
|
+
const widthStr = attrs.width || "100%";
|
|
1361
|
+
currentWidth = parseInt(widthStr) || 100;
|
|
1434
1362
|
}
|
|
1363
|
+
return { isVisible: visible, align: currentAlign, width: currentWidth };
|
|
1435
1364
|
}
|
|
1436
|
-
|
|
1437
|
-
}, [editor]);
|
|
1438
|
-
const shouldShow = useCallback4(() => {
|
|
1439
|
-
if (!editor) return false;
|
|
1440
|
-
const isActive = editor.isActive("imageBlock");
|
|
1441
|
-
if (!isActive) return false;
|
|
1442
|
-
const { state } = editor;
|
|
1443
|
-
const { selection } = state;
|
|
1444
|
-
const isNodeSelection = selection.constructor.name === "NodeSelection";
|
|
1445
|
-
return isNodeSelection;
|
|
1446
|
-
}, [editor]);
|
|
1365
|
+
});
|
|
1447
1366
|
const onAlignImageLeft = useCallback4(() => {
|
|
1448
1367
|
editor.chain().focus(void 0, { scrollIntoView: false }).setImageBlockAlign("left").run();
|
|
1449
1368
|
}, [editor]);
|
|
@@ -1462,13 +1381,20 @@ var ImageBlockMenu = ({ editor, appendTo }) => {
|
|
|
1462
1381
|
const onRemoveImage = useCallback4(() => {
|
|
1463
1382
|
editor.chain().focus(void 0, { scrollIntoView: false }).deleteSelection().run();
|
|
1464
1383
|
}, [editor]);
|
|
1465
|
-
|
|
1466
|
-
|
|
1384
|
+
if (!isVisible) return null;
|
|
1385
|
+
return /* @__PURE__ */ jsxs7(
|
|
1386
|
+
"div",
|
|
1467
1387
|
{
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1388
|
+
className: "bubble-menu",
|
|
1389
|
+
ref: menuRef,
|
|
1390
|
+
style: {
|
|
1391
|
+
position: "absolute",
|
|
1392
|
+
top: "-40px",
|
|
1393
|
+
left: "50%",
|
|
1394
|
+
transform: "translateX(-50%)",
|
|
1395
|
+
zIndex: "var(--nph-z, 50)"
|
|
1396
|
+
},
|
|
1397
|
+
children: [
|
|
1472
1398
|
/* @__PURE__ */ jsx7(
|
|
1473
1399
|
"button",
|
|
1474
1400
|
{
|
|
@@ -1528,14 +1454,14 @@ var ImageBlockMenu = ({ editor, appendTo }) => {
|
|
|
1528
1454
|
children: /* @__PURE__ */ jsx7(IconTrash3, { size: 16 })
|
|
1529
1455
|
}
|
|
1530
1456
|
)
|
|
1531
|
-
]
|
|
1457
|
+
]
|
|
1532
1458
|
}
|
|
1533
1459
|
);
|
|
1534
1460
|
};
|
|
1535
1461
|
|
|
1536
1462
|
// src/react/menus/ImageBlock/ImageUploader.tsx
|
|
1537
1463
|
import { IconPhoto as IconPhoto2, IconUpload as IconUpload2 } from "@tabler/icons-react";
|
|
1538
|
-
import { useCallback as useCallback5, useRef as useRef5, useState as
|
|
1464
|
+
import { useCallback as useCallback5, useRef as useRef5, useState as useState6 } from "react";
|
|
1539
1465
|
|
|
1540
1466
|
// src/react/menus/ImageBlock/ImageBlockLoading.tsx
|
|
1541
1467
|
import { IconLoader2 } from "@tabler/icons-react";
|
|
@@ -1553,8 +1479,8 @@ var ImageBlockLoading = () => {
|
|
|
1553
1479
|
// src/react/menus/ImageBlock/ImageUploader.tsx
|
|
1554
1480
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1555
1481
|
var ImageUploader = ({ onUpload, editor }) => {
|
|
1556
|
-
const [loading, setLoading] =
|
|
1557
|
-
const [draggedInside, setDraggedInside] =
|
|
1482
|
+
const [loading, setLoading] = useState6(false);
|
|
1483
|
+
const [draggedInside, setDraggedInside] = useState6(false);
|
|
1558
1484
|
const fileInputRef = useRef5(null);
|
|
1559
1485
|
const uploadFile = useCallback5(
|
|
1560
1486
|
async (file) => {
|
|
@@ -1656,21 +1582,93 @@ var ImageUploader = ({ onUpload, editor }) => {
|
|
|
1656
1582
|
);
|
|
1657
1583
|
};
|
|
1658
1584
|
|
|
1659
|
-
// src/react/menus/ImageBlock/
|
|
1585
|
+
// src/react/menus/ImageBlock/ImageResizeHandle.tsx
|
|
1586
|
+
import { useCallback as useCallback6, useRef as useRef6, useState as useState7 } from "react";
|
|
1660
1587
|
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1588
|
+
function ImageResizeHandle({
|
|
1589
|
+
children,
|
|
1590
|
+
onResize,
|
|
1591
|
+
currentWidth
|
|
1592
|
+
}) {
|
|
1593
|
+
const containerRef = useRef6(null);
|
|
1594
|
+
const [isResizing, setIsResizing] = useState7(false);
|
|
1595
|
+
const handleMouseDown = useCallback6(
|
|
1596
|
+
(e, side) => {
|
|
1597
|
+
e.preventDefault();
|
|
1598
|
+
e.stopPropagation();
|
|
1599
|
+
setIsResizing(true);
|
|
1600
|
+
const startX = e.clientX;
|
|
1601
|
+
const containerEl = containerRef.current;
|
|
1602
|
+
if (!containerEl) return;
|
|
1603
|
+
const editorEl = containerEl.closest(".nph-editor") || containerEl.parentElement;
|
|
1604
|
+
if (!editorEl) return;
|
|
1605
|
+
const editorWidth = editorEl.getBoundingClientRect().width;
|
|
1606
|
+
const startWidth = containerEl.getBoundingClientRect().width;
|
|
1607
|
+
const startPercent = startWidth / editorWidth * 100;
|
|
1608
|
+
const handleMouseMove = (moveEvent) => {
|
|
1609
|
+
const deltaX = moveEvent.clientX - startX;
|
|
1610
|
+
const direction = side === "right" ? 1 : -1;
|
|
1611
|
+
const deltaPercent = deltaX * direction / editorWidth * 100 * 2;
|
|
1612
|
+
const newPercent = Math.max(10, Math.min(100, startPercent + deltaPercent));
|
|
1613
|
+
onResize(Math.round(newPercent));
|
|
1614
|
+
};
|
|
1615
|
+
const handleMouseUp = () => {
|
|
1616
|
+
setIsResizing(false);
|
|
1617
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
1618
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
1619
|
+
};
|
|
1620
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
1621
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
1622
|
+
},
|
|
1623
|
+
[onResize]
|
|
1624
|
+
);
|
|
1625
|
+
return /* @__PURE__ */ jsxs10(
|
|
1626
|
+
"div",
|
|
1627
|
+
{
|
|
1628
|
+
ref: containerRef,
|
|
1629
|
+
className: `nph-resize-wrapper${isResizing ? " nph-resize-wrapper--active" : ""}`,
|
|
1630
|
+
children: [
|
|
1631
|
+
children,
|
|
1632
|
+
/* @__PURE__ */ jsx10(
|
|
1633
|
+
"div",
|
|
1634
|
+
{
|
|
1635
|
+
className: "nph-resize-handle nph-resize-handle--left",
|
|
1636
|
+
onMouseDown: (e) => handleMouseDown(e, "left")
|
|
1637
|
+
}
|
|
1638
|
+
),
|
|
1639
|
+
/* @__PURE__ */ jsx10(
|
|
1640
|
+
"div",
|
|
1641
|
+
{
|
|
1642
|
+
className: "nph-resize-handle nph-resize-handle--right",
|
|
1643
|
+
onMouseDown: (e) => handleMouseDown(e, "right")
|
|
1644
|
+
}
|
|
1645
|
+
)
|
|
1646
|
+
]
|
|
1647
|
+
}
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
// src/react/menus/ImageBlock/ImageBlockView.tsx
|
|
1652
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1661
1653
|
var ImageBlockView = (props) => {
|
|
1662
1654
|
const { editor, getPos, node, updateAttributes } = props;
|
|
1663
|
-
const imageWrapperRef =
|
|
1655
|
+
const imageWrapperRef = useRef7(null);
|
|
1664
1656
|
const { src, width, align, alt, loading } = node.attrs;
|
|
1665
|
-
const handleUpload =
|
|
1657
|
+
const handleUpload = useCallback7(
|
|
1666
1658
|
(url) => {
|
|
1667
1659
|
updateAttributes({ src: url, loading: false });
|
|
1668
1660
|
},
|
|
1669
1661
|
[updateAttributes]
|
|
1670
1662
|
);
|
|
1671
|
-
const onClick =
|
|
1663
|
+
const onClick = useCallback7(() => {
|
|
1672
1664
|
editor.commands.setNodeSelection(getPos());
|
|
1673
1665
|
}, [getPos, editor.commands]);
|
|
1666
|
+
const handleResize = useCallback7(
|
|
1667
|
+
(widthPercent) => {
|
|
1668
|
+
updateAttributes({ width: `${widthPercent}%` });
|
|
1669
|
+
},
|
|
1670
|
+
[updateAttributes]
|
|
1671
|
+
);
|
|
1674
1672
|
const getWrapperStyle = () => {
|
|
1675
1673
|
const baseStyle = {
|
|
1676
1674
|
width: width || "100%",
|
|
@@ -1684,14 +1682,17 @@ var ImageBlockView = (props) => {
|
|
|
1684
1682
|
return { ...baseStyle, marginLeft: "auto", marginRight: "auto" };
|
|
1685
1683
|
}
|
|
1686
1684
|
};
|
|
1685
|
+
const getContentStyle = () => ({
|
|
1686
|
+
position: "relative"
|
|
1687
|
+
});
|
|
1687
1688
|
if (!src || src === "") {
|
|
1688
|
-
return /* @__PURE__ */
|
|
1689
|
+
return /* @__PURE__ */ jsx11(NodeViewWrapper, { style: { width: "100%", marginTop: "0.5rem", marginBottom: "0.5rem" }, children: /* @__PURE__ */ jsx11("div", { ref: imageWrapperRef, children: /* @__PURE__ */ jsx11(ImageUploader, { onUpload: handleUpload, editor }) }) });
|
|
1689
1690
|
}
|
|
1690
1691
|
if (loading) {
|
|
1691
|
-
return /* @__PURE__ */
|
|
1692
|
+
return /* @__PURE__ */ jsx11(NodeViewWrapper, { style: getWrapperStyle(), children: /* @__PURE__ */ jsx11("div", { ref: imageWrapperRef, children: /* @__PURE__ */ jsx11(ImageBlockLoading, {}) }) });
|
|
1692
1693
|
}
|
|
1693
|
-
return /* @__PURE__ */
|
|
1694
|
-
/* @__PURE__ */
|
|
1694
|
+
return /* @__PURE__ */ jsx11(NodeViewWrapper, { style: getWrapperStyle(), children: /* @__PURE__ */ jsxs11("div", { contentEditable: false, ref: imageWrapperRef, style: getContentStyle(), children: [
|
|
1695
|
+
/* @__PURE__ */ jsx11(ImageResizeHandle, { onResize: handleResize, currentWidth: width, children: /* @__PURE__ */ jsx11(
|
|
1695
1696
|
"img",
|
|
1696
1697
|
{
|
|
1697
1698
|
src,
|
|
@@ -1699,81 +1700,81 @@ var ImageBlockView = (props) => {
|
|
|
1699
1700
|
onClick,
|
|
1700
1701
|
className: "nph-image-block"
|
|
1701
1702
|
}
|
|
1702
|
-
) })
|
|
1703
|
-
/* @__PURE__ */
|
|
1704
|
-
] });
|
|
1703
|
+
) }),
|
|
1704
|
+
/* @__PURE__ */ jsx11(ImageBlockMenu, { editor, getPos, appendTo: imageWrapperRef })
|
|
1705
|
+
] }) });
|
|
1705
1706
|
};
|
|
1706
1707
|
|
|
1707
1708
|
// src/react/menus/VideoBlock/VideoBlockView.tsx
|
|
1708
|
-
import {
|
|
1709
|
-
import {
|
|
1709
|
+
import { NodeSelection as NodeSelection5 } from "@tiptap/pm/state";
|
|
1710
|
+
import { NodeViewWrapper as NodeViewWrapper2, useEditorState as useEditorState4 } from "@tiptap/react";
|
|
1711
|
+
import { useCallback as useCallback9, useRef as useRef9, useState as useState8 } from "react";
|
|
1710
1712
|
|
|
1711
1713
|
// src/react/menus/VideoBlock/VideoBlockMenu.tsx
|
|
1712
|
-
import {
|
|
1713
|
-
import {
|
|
1714
|
+
import { NodeSelection as NodeSelection4 } from "@tiptap/pm/state";
|
|
1715
|
+
import { useEditorState as useEditorState3 } from "@tiptap/react";
|
|
1716
|
+
import { useCallback as useCallback8, useRef as useRef8 } from "react";
|
|
1714
1717
|
import {
|
|
1715
1718
|
IconAlignLeft as IconAlignLeft3,
|
|
1716
1719
|
IconAlignCenter as IconAlignCenter3,
|
|
1717
1720
|
IconAlignRight as IconAlignRight3,
|
|
1718
1721
|
IconTrash as IconTrash4
|
|
1719
1722
|
} from "@tabler/icons-react";
|
|
1720
|
-
import { jsx as
|
|
1721
|
-
var VideoBlockMenu = ({ editor }) => {
|
|
1722
|
-
const menuRef =
|
|
1723
|
-
const
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
const
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
const
|
|
1743
|
-
if (!editor) return false;
|
|
1744
|
-
const { state } = editor;
|
|
1745
|
-
const { selection } = state;
|
|
1746
|
-
if (selection.constructor.name !== "NodeSelection") return false;
|
|
1747
|
-
const node = selection.node;
|
|
1748
|
-
if (!node || node.type.name !== "videoBlock") return false;
|
|
1749
|
-
return true;
|
|
1750
|
-
}, [editor]);
|
|
1751
|
-
const onAlignLeft = useCallback7(() => {
|
|
1723
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1724
|
+
var VideoBlockMenu = ({ editor, getPos }) => {
|
|
1725
|
+
const menuRef = useRef8(null);
|
|
1726
|
+
const { isVisible, align, width } = useEditorState3({
|
|
1727
|
+
editor,
|
|
1728
|
+
selector: (ctx) => {
|
|
1729
|
+
if (!ctx.editor) return { isVisible: false, align: "center", width: 100 };
|
|
1730
|
+
const { state } = ctx.editor;
|
|
1731
|
+
const { selection } = state;
|
|
1732
|
+
const isNodeSel = selection instanceof NodeSelection4;
|
|
1733
|
+
const isThisNode = isNodeSel && selection.from === getPos();
|
|
1734
|
+
let currentAlign = "center";
|
|
1735
|
+
let currentWidth = 100;
|
|
1736
|
+
if (isThisNode) {
|
|
1737
|
+
const attrs = ctx.editor.getAttributes("videoBlock");
|
|
1738
|
+
currentAlign = attrs.align || "center";
|
|
1739
|
+
const widthStr = attrs.width || "100%";
|
|
1740
|
+
currentWidth = parseInt(widthStr) || 100;
|
|
1741
|
+
}
|
|
1742
|
+
return { isVisible: isThisNode, align: currentAlign, width: currentWidth };
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
const onAlignLeft = useCallback8(() => {
|
|
1752
1746
|
editor.chain().focus(void 0, { scrollIntoView: false }).setVideoBlockAlign("left").run();
|
|
1753
1747
|
}, [editor]);
|
|
1754
|
-
const onAlignCenter =
|
|
1748
|
+
const onAlignCenter = useCallback8(() => {
|
|
1755
1749
|
editor.chain().focus(void 0, { scrollIntoView: false }).setVideoBlockAlign("center").run();
|
|
1756
1750
|
}, [editor]);
|
|
1757
|
-
const onAlignRight =
|
|
1751
|
+
const onAlignRight = useCallback8(() => {
|
|
1758
1752
|
editor.chain().focus(void 0, { scrollIntoView: false }).setVideoBlockAlign("right").run();
|
|
1759
1753
|
}, [editor]);
|
|
1760
|
-
const onWidthChange =
|
|
1754
|
+
const onWidthChange = useCallback8(
|
|
1761
1755
|
(value) => {
|
|
1762
1756
|
editor.chain().focus(void 0, { scrollIntoView: false }).setVideoBlockWidth(value).run();
|
|
1763
1757
|
},
|
|
1764
1758
|
[editor]
|
|
1765
1759
|
);
|
|
1766
|
-
const onRemove =
|
|
1760
|
+
const onRemove = useCallback8(() => {
|
|
1767
1761
|
editor.chain().focus(void 0, { scrollIntoView: false }).deleteSelection().run();
|
|
1768
1762
|
}, [editor]);
|
|
1769
|
-
|
|
1770
|
-
|
|
1763
|
+
if (!isVisible) return null;
|
|
1764
|
+
return /* @__PURE__ */ jsxs12(
|
|
1765
|
+
"div",
|
|
1771
1766
|
{
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1767
|
+
className: "bubble-menu",
|
|
1768
|
+
ref: menuRef,
|
|
1769
|
+
style: {
|
|
1770
|
+
position: "absolute",
|
|
1771
|
+
top: "-40px",
|
|
1772
|
+
left: "50%",
|
|
1773
|
+
transform: "translateX(-50%)",
|
|
1774
|
+
zIndex: "var(--nph-z, 50)"
|
|
1775
|
+
},
|
|
1776
|
+
children: [
|
|
1777
|
+
/* @__PURE__ */ jsx12(
|
|
1777
1778
|
"button",
|
|
1778
1779
|
{
|
|
1779
1780
|
type: "button",
|
|
@@ -1781,10 +1782,10 @@ var VideoBlockMenu = ({ editor }) => {
|
|
|
1781
1782
|
title: "Align left",
|
|
1782
1783
|
onMouseDown: (e) => e.preventDefault(),
|
|
1783
1784
|
onClick: onAlignLeft,
|
|
1784
|
-
children: /* @__PURE__ */
|
|
1785
|
+
children: /* @__PURE__ */ jsx12(IconAlignLeft3, { size: 16 })
|
|
1785
1786
|
}
|
|
1786
1787
|
),
|
|
1787
|
-
/* @__PURE__ */
|
|
1788
|
+
/* @__PURE__ */ jsx12(
|
|
1788
1789
|
"button",
|
|
1789
1790
|
{
|
|
1790
1791
|
type: "button",
|
|
@@ -1792,10 +1793,10 @@ var VideoBlockMenu = ({ editor }) => {
|
|
|
1792
1793
|
title: "Align center",
|
|
1793
1794
|
onMouseDown: (e) => e.preventDefault(),
|
|
1794
1795
|
onClick: onAlignCenter,
|
|
1795
|
-
children: /* @__PURE__ */
|
|
1796
|
+
children: /* @__PURE__ */ jsx12(IconAlignCenter3, { size: 16 })
|
|
1796
1797
|
}
|
|
1797
1798
|
),
|
|
1798
|
-
/* @__PURE__ */
|
|
1799
|
+
/* @__PURE__ */ jsx12(
|
|
1799
1800
|
"button",
|
|
1800
1801
|
{
|
|
1801
1802
|
type: "button",
|
|
@@ -1803,25 +1804,25 @@ var VideoBlockMenu = ({ editor }) => {
|
|
|
1803
1804
|
title: "Align right",
|
|
1804
1805
|
onMouseDown: (e) => e.preventDefault(),
|
|
1805
1806
|
onClick: onAlignRight,
|
|
1806
|
-
children: /* @__PURE__ */
|
|
1807
|
+
children: /* @__PURE__ */ jsx12(IconAlignRight3, { size: 16 })
|
|
1807
1808
|
}
|
|
1808
1809
|
),
|
|
1809
|
-
/* @__PURE__ */
|
|
1810
|
+
/* @__PURE__ */ jsx12(
|
|
1810
1811
|
"div",
|
|
1811
1812
|
{
|
|
1812
1813
|
className: "nph-link-popover__divider",
|
|
1813
1814
|
style: { margin: "0 4px" }
|
|
1814
1815
|
}
|
|
1815
1816
|
),
|
|
1816
|
-
/* @__PURE__ */
|
|
1817
|
-
/* @__PURE__ */
|
|
1817
|
+
/* @__PURE__ */ jsx12(ImageBlockWidth, { onChange: onWidthChange, value: width }),
|
|
1818
|
+
/* @__PURE__ */ jsx12(
|
|
1818
1819
|
"div",
|
|
1819
1820
|
{
|
|
1820
1821
|
className: "nph-link-popover__divider",
|
|
1821
1822
|
style: { margin: "0 4px" }
|
|
1822
1823
|
}
|
|
1823
1824
|
),
|
|
1824
|
-
/* @__PURE__ */
|
|
1825
|
+
/* @__PURE__ */ jsx12(
|
|
1825
1826
|
"button",
|
|
1826
1827
|
{
|
|
1827
1828
|
type: "button",
|
|
@@ -1829,17 +1830,17 @@ var VideoBlockMenu = ({ editor }) => {
|
|
|
1829
1830
|
title: "Remove video",
|
|
1830
1831
|
onMouseDown: (e) => e.preventDefault(),
|
|
1831
1832
|
onClick: onRemove,
|
|
1832
|
-
children: /* @__PURE__ */
|
|
1833
|
+
children: /* @__PURE__ */ jsx12(IconTrash4, { size: 16 })
|
|
1833
1834
|
}
|
|
1834
1835
|
)
|
|
1835
|
-
]
|
|
1836
|
+
]
|
|
1836
1837
|
}
|
|
1837
1838
|
);
|
|
1838
1839
|
};
|
|
1839
1840
|
|
|
1840
1841
|
// src/react/menus/VideoBlock/VideoBlockView.tsx
|
|
1841
1842
|
import { IconVideo as IconVideo2 } from "@tabler/icons-react";
|
|
1842
|
-
import { jsx as
|
|
1843
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1843
1844
|
function toEmbedUrl(url) {
|
|
1844
1845
|
const ytMatch = url.match(
|
|
1845
1846
|
/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/
|
|
@@ -1855,15 +1856,23 @@ function toEmbedUrl(url) {
|
|
|
1855
1856
|
}
|
|
1856
1857
|
var VideoBlockView = (props) => {
|
|
1857
1858
|
const { editor, getPos, node, updateAttributes } = props;
|
|
1858
|
-
const wrapperRef =
|
|
1859
|
+
const wrapperRef = useRef9(null);
|
|
1859
1860
|
const { src, width, align } = node.attrs;
|
|
1860
|
-
const [inputUrl, setInputUrl] =
|
|
1861
|
-
const
|
|
1861
|
+
const [inputUrl, setInputUrl] = useState8("");
|
|
1862
|
+
const isSelected = useEditorState4({
|
|
1863
|
+
editor,
|
|
1864
|
+
selector: (ctx) => {
|
|
1865
|
+
if (!ctx.editor) return false;
|
|
1866
|
+
const { selection } = ctx.editor.state;
|
|
1867
|
+
return selection instanceof NodeSelection5 && selection.from === getPos();
|
|
1868
|
+
}
|
|
1869
|
+
});
|
|
1870
|
+
const handleEmbed = useCallback9(() => {
|
|
1862
1871
|
if (!inputUrl.trim()) return;
|
|
1863
1872
|
const embedUrl = toEmbedUrl(inputUrl.trim());
|
|
1864
1873
|
updateAttributes({ src: embedUrl });
|
|
1865
1874
|
}, [inputUrl, updateAttributes]);
|
|
1866
|
-
const handleKeyDown =
|
|
1875
|
+
const handleKeyDown = useCallback9(
|
|
1867
1876
|
(e) => {
|
|
1868
1877
|
if (e.key === "Enter") {
|
|
1869
1878
|
e.preventDefault();
|
|
@@ -1872,13 +1881,13 @@ var VideoBlockView = (props) => {
|
|
|
1872
1881
|
},
|
|
1873
1882
|
[handleEmbed]
|
|
1874
1883
|
);
|
|
1875
|
-
const onClick =
|
|
1884
|
+
const onClick = useCallback9(() => {
|
|
1876
1885
|
editor.commands.setNodeSelection(getPos());
|
|
1877
1886
|
}, [getPos, editor.commands]);
|
|
1878
1887
|
const getWrapperStyle = () => {
|
|
1879
1888
|
const baseStyle = {
|
|
1880
|
-
width:
|
|
1881
|
-
maxWidth: "100%"
|
|
1889
|
+
width: "fit-content",
|
|
1890
|
+
maxWidth: width || "100%"
|
|
1882
1891
|
};
|
|
1883
1892
|
if (align === "left") {
|
|
1884
1893
|
return { ...baseStyle, marginLeft: 0, marginRight: "auto" };
|
|
@@ -1889,10 +1898,10 @@ var VideoBlockView = (props) => {
|
|
|
1889
1898
|
}
|
|
1890
1899
|
};
|
|
1891
1900
|
if (!src || src === "") {
|
|
1892
|
-
return /* @__PURE__ */
|
|
1893
|
-
/* @__PURE__ */
|
|
1894
|
-
/* @__PURE__ */
|
|
1895
|
-
/* @__PURE__ */
|
|
1901
|
+
return /* @__PURE__ */ jsx13(NodeViewWrapper2, { style: getWrapperStyle(), children: /* @__PURE__ */ jsx13("div", { ref: wrapperRef, children: /* @__PURE__ */ jsxs13("div", { className: "nph-video-input", children: [
|
|
1902
|
+
/* @__PURE__ */ jsx13("div", { className: "nph-video-input__icon", children: /* @__PURE__ */ jsx13(IconVideo2, { size: 24 }) }),
|
|
1903
|
+
/* @__PURE__ */ jsxs13("div", { className: "nph-video-input__content", children: [
|
|
1904
|
+
/* @__PURE__ */ jsx13(
|
|
1896
1905
|
"input",
|
|
1897
1906
|
{
|
|
1898
1907
|
type: "text",
|
|
@@ -1903,7 +1912,7 @@ var VideoBlockView = (props) => {
|
|
|
1903
1912
|
onKeyDown: handleKeyDown
|
|
1904
1913
|
}
|
|
1905
1914
|
),
|
|
1906
|
-
/* @__PURE__ */
|
|
1915
|
+
/* @__PURE__ */ jsx13(
|
|
1907
1916
|
"button",
|
|
1908
1917
|
{
|
|
1909
1918
|
type: "button",
|
|
@@ -1916,38 +1925,755 @@ var VideoBlockView = (props) => {
|
|
|
1916
1925
|
] })
|
|
1917
1926
|
] }) }) });
|
|
1918
1927
|
}
|
|
1919
|
-
return /* @__PURE__ */
|
|
1920
|
-
|
|
1921
|
-
|
|
1928
|
+
return /* @__PURE__ */ jsx13(NodeViewWrapper2, { style: getWrapperStyle(), children: /* @__PURE__ */ jsxs13(
|
|
1929
|
+
"div",
|
|
1930
|
+
{
|
|
1931
|
+
contentEditable: false,
|
|
1932
|
+
ref: wrapperRef,
|
|
1933
|
+
style: { position: "relative" },
|
|
1934
|
+
children: [
|
|
1935
|
+
/* @__PURE__ */ jsxs13("div", { className: "nph-video-block", children: [
|
|
1936
|
+
/* @__PURE__ */ jsx13(
|
|
1937
|
+
"iframe",
|
|
1938
|
+
{
|
|
1939
|
+
src,
|
|
1940
|
+
className: "nph-video-block__iframe",
|
|
1941
|
+
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
1942
|
+
allowFullScreen: true
|
|
1943
|
+
}
|
|
1944
|
+
),
|
|
1945
|
+
!isSelected && /* @__PURE__ */ jsx13(
|
|
1946
|
+
"div",
|
|
1947
|
+
{
|
|
1948
|
+
className: "nph-video-block__overlay",
|
|
1949
|
+
onClick
|
|
1950
|
+
}
|
|
1951
|
+
)
|
|
1952
|
+
] }),
|
|
1953
|
+
/* @__PURE__ */ jsx13(VideoBlockMenu, { editor, getPos })
|
|
1954
|
+
]
|
|
1955
|
+
}
|
|
1956
|
+
) });
|
|
1957
|
+
};
|
|
1958
|
+
|
|
1959
|
+
// src/react/menus/DragHandle/BlockActionMenu.tsx
|
|
1960
|
+
import {
|
|
1961
|
+
IconCopy,
|
|
1962
|
+
IconTrash as IconTrash5,
|
|
1963
|
+
IconArrowUp,
|
|
1964
|
+
IconArrowDown,
|
|
1965
|
+
IconClipboard
|
|
1966
|
+
} from "@tabler/icons-react";
|
|
1967
|
+
import { useCallback as useCallback10 } from "react";
|
|
1968
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1969
|
+
function BlockActionMenu({ editor, onClose }) {
|
|
1970
|
+
const executeAndClose = useCallback10(
|
|
1971
|
+
(fn) => {
|
|
1972
|
+
fn();
|
|
1973
|
+
onClose();
|
|
1974
|
+
},
|
|
1975
|
+
[onClose]
|
|
1976
|
+
);
|
|
1977
|
+
const handleDelete = useCallback10(() => {
|
|
1978
|
+
executeAndClose(() => {
|
|
1979
|
+
editor.commands.deleteSelection();
|
|
1980
|
+
});
|
|
1981
|
+
}, [editor, executeAndClose]);
|
|
1982
|
+
const handleDuplicate = useCallback10(() => {
|
|
1983
|
+
executeAndClose(() => {
|
|
1984
|
+
const { state } = editor;
|
|
1985
|
+
const { selection } = state;
|
|
1986
|
+
const { $anchor } = selection;
|
|
1987
|
+
const depth = $anchor.depth > 0 ? 1 : 0;
|
|
1988
|
+
const start = $anchor.start(depth);
|
|
1989
|
+
const end = $anchor.end(depth);
|
|
1990
|
+
const node = state.doc.nodeAt(start - 1);
|
|
1991
|
+
if (node) {
|
|
1992
|
+
const insertPos = end + 1;
|
|
1993
|
+
editor.chain().focus().insertContentAt(insertPos, node.toJSON()).run();
|
|
1994
|
+
}
|
|
1995
|
+
});
|
|
1996
|
+
}, [editor, executeAndClose]);
|
|
1997
|
+
const handleMoveUp = useCallback10(() => {
|
|
1998
|
+
executeAndClose(() => {
|
|
1999
|
+
const { state } = editor;
|
|
2000
|
+
const { selection } = state;
|
|
2001
|
+
const { $anchor } = selection;
|
|
2002
|
+
const depth = $anchor.depth > 0 ? 1 : 0;
|
|
2003
|
+
const blockStart = $anchor.start(depth) - 1;
|
|
2004
|
+
if (blockStart <= 0) return;
|
|
2005
|
+
const node = state.doc.nodeAt(blockStart);
|
|
2006
|
+
if (!node) return;
|
|
2007
|
+
const $pos = state.doc.resolve(blockStart);
|
|
2008
|
+
const index = $pos.index($pos.depth);
|
|
2009
|
+
if (index === 0) return;
|
|
2010
|
+
const prevNode = $pos.node($pos.depth).child(index - 1);
|
|
2011
|
+
const prevStart = blockStart - prevNode.nodeSize;
|
|
2012
|
+
editor.chain().focus().command(({ tr }) => {
|
|
2013
|
+
const currentSlice = state.doc.slice(blockStart, blockStart + node.nodeSize);
|
|
2014
|
+
tr.delete(blockStart, blockStart + node.nodeSize);
|
|
2015
|
+
tr.insert(prevStart, currentSlice.content);
|
|
2016
|
+
return true;
|
|
2017
|
+
}).run();
|
|
2018
|
+
});
|
|
2019
|
+
}, [editor, executeAndClose]);
|
|
2020
|
+
const handleMoveDown = useCallback10(() => {
|
|
2021
|
+
executeAndClose(() => {
|
|
2022
|
+
const { state } = editor;
|
|
2023
|
+
const { selection } = state;
|
|
2024
|
+
const { $anchor } = selection;
|
|
2025
|
+
const depth = $anchor.depth > 0 ? 1 : 0;
|
|
2026
|
+
const blockStart = $anchor.start(depth) - 1;
|
|
2027
|
+
const node = state.doc.nodeAt(blockStart);
|
|
2028
|
+
if (!node) return;
|
|
2029
|
+
const blockEnd = blockStart + node.nodeSize;
|
|
2030
|
+
const $pos = state.doc.resolve(blockStart);
|
|
2031
|
+
const parent = $pos.node($pos.depth);
|
|
2032
|
+
const index = $pos.index($pos.depth);
|
|
2033
|
+
if (index >= parent.childCount - 1) return;
|
|
2034
|
+
const nextNode = parent.child(index + 1);
|
|
2035
|
+
const nextEnd = blockEnd + nextNode.nodeSize;
|
|
2036
|
+
editor.chain().focus().command(({ tr }) => {
|
|
2037
|
+
const currentSlice = state.doc.slice(blockStart, blockEnd);
|
|
2038
|
+
tr.delete(blockStart, blockEnd);
|
|
2039
|
+
const insertPos = blockStart + nextNode.nodeSize;
|
|
2040
|
+
tr.insert(insertPos, currentSlice.content);
|
|
2041
|
+
return true;
|
|
2042
|
+
}).run();
|
|
2043
|
+
});
|
|
2044
|
+
}, [editor, executeAndClose]);
|
|
2045
|
+
const handleCopyToClipboard = useCallback10(() => {
|
|
2046
|
+
executeAndClose(() => {
|
|
2047
|
+
const { state } = editor;
|
|
2048
|
+
const { selection } = state;
|
|
2049
|
+
const { $anchor } = selection;
|
|
2050
|
+
const depth = $anchor.depth > 0 ? 1 : 0;
|
|
2051
|
+
const start = $anchor.start(depth) - 1;
|
|
2052
|
+
const node = state.doc.nodeAt(start);
|
|
2053
|
+
if (node) {
|
|
2054
|
+
const text = node.textContent;
|
|
2055
|
+
navigator.clipboard.writeText(text).catch(() => {
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
});
|
|
2059
|
+
}, [editor, executeAndClose]);
|
|
2060
|
+
return /* @__PURE__ */ jsx14("div", { className: "nph-block-action-menu nph-command", children: /* @__PURE__ */ jsxs14("div", { className: "nph-command__list", style: { maxHeight: "none" }, children: [
|
|
2061
|
+
/* @__PURE__ */ jsxs14(
|
|
2062
|
+
"button",
|
|
1922
2063
|
{
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
{
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2064
|
+
type: "button",
|
|
2065
|
+
className: "nph-command__item",
|
|
2066
|
+
onClick: handleDelete,
|
|
2067
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2068
|
+
children: [
|
|
2069
|
+
/* @__PURE__ */ jsx14(IconTrash5, { size: 16 }),
|
|
2070
|
+
/* @__PURE__ */ jsx14("span", { children: "Delete" })
|
|
2071
|
+
]
|
|
2072
|
+
}
|
|
2073
|
+
),
|
|
2074
|
+
/* @__PURE__ */ jsxs14(
|
|
2075
|
+
"button",
|
|
2076
|
+
{
|
|
2077
|
+
type: "button",
|
|
2078
|
+
className: "nph-command__item",
|
|
2079
|
+
onClick: handleDuplicate,
|
|
2080
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2081
|
+
children: [
|
|
2082
|
+
/* @__PURE__ */ jsx14(IconCopy, { size: 16 }),
|
|
2083
|
+
/* @__PURE__ */ jsx14("span", { children: "Duplicate" })
|
|
2084
|
+
]
|
|
2085
|
+
}
|
|
2086
|
+
),
|
|
2087
|
+
/* @__PURE__ */ jsxs14(
|
|
2088
|
+
"button",
|
|
2089
|
+
{
|
|
2090
|
+
type: "button",
|
|
2091
|
+
className: "nph-command__item",
|
|
2092
|
+
onClick: handleCopyToClipboard,
|
|
2093
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2094
|
+
children: [
|
|
2095
|
+
/* @__PURE__ */ jsx14(IconClipboard, { size: 16 }),
|
|
2096
|
+
/* @__PURE__ */ jsx14("span", { children: "Copy to clipboard" })
|
|
2097
|
+
]
|
|
2098
|
+
}
|
|
2099
|
+
),
|
|
2100
|
+
/* @__PURE__ */ jsxs14(
|
|
2101
|
+
"button",
|
|
2102
|
+
{
|
|
2103
|
+
type: "button",
|
|
2104
|
+
className: "nph-command__item",
|
|
2105
|
+
onClick: handleMoveUp,
|
|
2106
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2107
|
+
children: [
|
|
2108
|
+
/* @__PURE__ */ jsx14(IconArrowUp, { size: 16 }),
|
|
2109
|
+
/* @__PURE__ */ jsx14("span", { children: "Move up" })
|
|
2110
|
+
]
|
|
2111
|
+
}
|
|
2112
|
+
),
|
|
2113
|
+
/* @__PURE__ */ jsxs14(
|
|
2114
|
+
"button",
|
|
2115
|
+
{
|
|
2116
|
+
type: "button",
|
|
2117
|
+
className: "nph-command__item",
|
|
2118
|
+
onClick: handleMoveDown,
|
|
2119
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2120
|
+
children: [
|
|
2121
|
+
/* @__PURE__ */ jsx14(IconArrowDown, { size: 16 }),
|
|
2122
|
+
/* @__PURE__ */ jsx14("span", { children: "Move down" })
|
|
2123
|
+
]
|
|
2124
|
+
}
|
|
2125
|
+
)
|
|
2126
|
+
] }) });
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
// src/react/menus/TableMenu.tsx
|
|
2130
|
+
import { useCurrentEditor as useCurrentEditor5, useEditorState as useEditorState5 } from "@tiptap/react";
|
|
2131
|
+
import {
|
|
2132
|
+
IconRowInsertBottom,
|
|
2133
|
+
IconColumnInsertRight,
|
|
2134
|
+
IconColumnInsertLeft,
|
|
2135
|
+
IconRowRemove,
|
|
2136
|
+
IconColumnRemove,
|
|
2137
|
+
IconTableRow,
|
|
2138
|
+
IconTableColumn,
|
|
2139
|
+
IconArrowMerge,
|
|
2140
|
+
IconArrowsSplit,
|
|
2141
|
+
IconTableOff,
|
|
2142
|
+
IconGripVertical,
|
|
2143
|
+
IconGripHorizontal,
|
|
2144
|
+
IconRowInsertTop
|
|
2145
|
+
} from "@tabler/icons-react";
|
|
2146
|
+
import {
|
|
2147
|
+
useState as useState9,
|
|
2148
|
+
useEffect as useEffect6,
|
|
2149
|
+
useRef as useRef10,
|
|
2150
|
+
useCallback as useCallback11
|
|
2151
|
+
} from "react";
|
|
2152
|
+
import { createPortal } from "react-dom";
|
|
2153
|
+
import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2154
|
+
function TableMenu({ className: _className }) {
|
|
2155
|
+
const { editor } = useCurrentEditor5();
|
|
2156
|
+
const tableInfo = useEditorState5({
|
|
2157
|
+
editor,
|
|
2158
|
+
selector: (ctx) => {
|
|
2159
|
+
if (!ctx.editor) return null;
|
|
2160
|
+
const { $from } = ctx.editor.state.selection;
|
|
2161
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
2162
|
+
if ($from.node(d).type.name === "table") {
|
|
2163
|
+
return { pos: $from.start(d) - 1, depth: d };
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return null;
|
|
2167
|
+
}
|
|
2168
|
+
});
|
|
2169
|
+
const [colGrips, setColGrips] = useState9([]);
|
|
2170
|
+
const [rowGrips, setRowGrips] = useState9([]);
|
|
2171
|
+
const [dropdown, setDropdown] = useState9(null);
|
|
2172
|
+
const [tableRect, setTableRect] = useState9(null);
|
|
2173
|
+
const [isHovering, setIsHovering] = useState9(false);
|
|
2174
|
+
const [drag, setDrag] = useState9(null);
|
|
2175
|
+
const dropdownRef = useRef10(null);
|
|
2176
|
+
const dragRef = useRef10(null);
|
|
2177
|
+
const getTableDom = useCallback11(() => {
|
|
2178
|
+
if (!editor || !tableInfo) return null;
|
|
2179
|
+
return editor.view.nodeDOM(tableInfo.pos);
|
|
2180
|
+
}, [editor, tableInfo]);
|
|
2181
|
+
const measureGrips = useCallback11(() => {
|
|
2182
|
+
const tableDom = getTableDom();
|
|
2183
|
+
if (!tableDom) {
|
|
2184
|
+
setColGrips([]);
|
|
2185
|
+
setRowGrips([]);
|
|
2186
|
+
setTableRect(null);
|
|
2187
|
+
return;
|
|
2188
|
+
}
|
|
2189
|
+
const rect = tableDom.getBoundingClientRect();
|
|
2190
|
+
setTableRect(rect);
|
|
2191
|
+
const firstRow = tableDom.querySelector("tr");
|
|
2192
|
+
if (!firstRow) return;
|
|
2193
|
+
const cells = firstRow.querySelectorAll("th, td");
|
|
2194
|
+
const newColGrips = [];
|
|
2195
|
+
cells.forEach((cell) => {
|
|
2196
|
+
const cellRect = cell.getBoundingClientRect();
|
|
2197
|
+
newColGrips.push({
|
|
2198
|
+
left: cellRect.left,
|
|
2199
|
+
top: rect.top,
|
|
2200
|
+
width: cellRect.width,
|
|
2201
|
+
height: 0
|
|
2202
|
+
});
|
|
2203
|
+
});
|
|
2204
|
+
setColGrips(newColGrips);
|
|
2205
|
+
const rows = tableDom.querySelectorAll("tr");
|
|
2206
|
+
const newRowGrips = [];
|
|
2207
|
+
rows.forEach((row) => {
|
|
2208
|
+
const rowRect = row.getBoundingClientRect();
|
|
2209
|
+
newRowGrips.push({
|
|
2210
|
+
left: rect.left,
|
|
2211
|
+
top: rowRect.top,
|
|
2212
|
+
width: 0,
|
|
2213
|
+
height: rowRect.height
|
|
2214
|
+
});
|
|
2215
|
+
});
|
|
2216
|
+
setRowGrips(newRowGrips);
|
|
2217
|
+
}, [getTableDom]);
|
|
2218
|
+
useEffect6(() => {
|
|
2219
|
+
if (!tableInfo) {
|
|
2220
|
+
setColGrips([]);
|
|
2221
|
+
setRowGrips([]);
|
|
2222
|
+
setTableRect(null);
|
|
2223
|
+
setDropdown(null);
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
measureGrips();
|
|
2227
|
+
const tableDom = getTableDom();
|
|
2228
|
+
if (!tableDom) return;
|
|
2229
|
+
const ro = new ResizeObserver(() => measureGrips());
|
|
2230
|
+
ro.observe(tableDom);
|
|
2231
|
+
const HOVER_PAD = 32;
|
|
2232
|
+
const handleMouseMove = (e) => {
|
|
2233
|
+
const r = tableDom.getBoundingClientRect();
|
|
2234
|
+
const inside = e.clientX >= r.left - HOVER_PAD && e.clientX <= r.right + HOVER_PAD && e.clientY >= r.top - HOVER_PAD && e.clientY <= r.bottom + HOVER_PAD;
|
|
2235
|
+
setIsHovering(inside);
|
|
2236
|
+
};
|
|
2237
|
+
document.addEventListener("mousemove", handleMouseMove, { passive: true });
|
|
2238
|
+
const scrollParent = tableDom.closest(".nph-editor") || window;
|
|
2239
|
+
const handleScroll = () => {
|
|
2240
|
+
measureGrips();
|
|
2241
|
+
setDropdown(null);
|
|
2242
|
+
};
|
|
2243
|
+
scrollParent.addEventListener("scroll", handleScroll, { passive: true });
|
|
2244
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
2245
|
+
return () => {
|
|
2246
|
+
ro.disconnect();
|
|
2247
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
2248
|
+
scrollParent.removeEventListener("scroll", handleScroll);
|
|
2249
|
+
window.removeEventListener("scroll", handleScroll);
|
|
2250
|
+
};
|
|
2251
|
+
}, [tableInfo, getTableDom, measureGrips]);
|
|
2252
|
+
useEffect6(() => {
|
|
2253
|
+
if (!dropdown) return;
|
|
2254
|
+
const handlePointerDown = (e) => {
|
|
2255
|
+
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
2256
|
+
setDropdown(null);
|
|
2257
|
+
}
|
|
2258
|
+
};
|
|
2259
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
2260
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown);
|
|
2261
|
+
}, [dropdown]);
|
|
2262
|
+
useEffect6(() => {
|
|
2263
|
+
if (!editor || !dropdown) return;
|
|
2264
|
+
const handleTransaction = () => setDropdown(null);
|
|
2265
|
+
editor.on("transaction", handleTransaction);
|
|
2266
|
+
return () => {
|
|
2267
|
+
editor.off("transaction", handleTransaction);
|
|
2268
|
+
};
|
|
2269
|
+
}, [editor, dropdown]);
|
|
2270
|
+
const handleColGripClick = useCallback11(
|
|
2271
|
+
(index, e) => {
|
|
2272
|
+
e.preventDefault();
|
|
2273
|
+
e.stopPropagation();
|
|
2274
|
+
if (!editor || !tableInfo) return;
|
|
2275
|
+
const tableDom = getTableDom();
|
|
2276
|
+
if (!tableDom) return;
|
|
2277
|
+
const firstRow = tableDom.querySelector("tr");
|
|
2278
|
+
if (!firstRow) return;
|
|
2279
|
+
const cells = firstRow.querySelectorAll("th, td");
|
|
2280
|
+
const cell = cells[index];
|
|
2281
|
+
if (!cell) return;
|
|
2282
|
+
const pos = editor.view.posAtDOM(cell, 0);
|
|
2283
|
+
editor.chain().focus().setTextSelection(pos).run();
|
|
2284
|
+
const rect = cell.getBoundingClientRect();
|
|
2285
|
+
setDropdown({
|
|
2286
|
+
type: "column",
|
|
2287
|
+
index,
|
|
2288
|
+
x: rect.left,
|
|
2289
|
+
y: rect.top - 4
|
|
2290
|
+
});
|
|
2291
|
+
},
|
|
2292
|
+
[editor, tableInfo, getTableDom]
|
|
2293
|
+
);
|
|
2294
|
+
const handleRowGripClick = useCallback11(
|
|
2295
|
+
(index, e) => {
|
|
2296
|
+
e.preventDefault();
|
|
2297
|
+
e.stopPropagation();
|
|
2298
|
+
if (!editor || !tableInfo) return;
|
|
2299
|
+
const tableDom = getTableDom();
|
|
2300
|
+
if (!tableDom) return;
|
|
2301
|
+
const rows = tableDom.querySelectorAll("tr");
|
|
2302
|
+
const row = rows[index];
|
|
2303
|
+
if (!row) return;
|
|
2304
|
+
const firstCell = row.querySelector("th, td");
|
|
2305
|
+
if (!firstCell) return;
|
|
2306
|
+
const pos = editor.view.posAtDOM(firstCell, 0);
|
|
2307
|
+
editor.chain().focus().setTextSelection(pos).run();
|
|
2308
|
+
const gripEl = e.currentTarget;
|
|
2309
|
+
const gripRect = gripEl.getBoundingClientRect();
|
|
2310
|
+
setDropdown({
|
|
2311
|
+
type: "row",
|
|
2312
|
+
index,
|
|
2313
|
+
x: gripRect.left,
|
|
2314
|
+
y: gripRect.bottom + 4
|
|
2315
|
+
});
|
|
2316
|
+
},
|
|
2317
|
+
[editor, tableInfo, getTableDom]
|
|
2318
|
+
);
|
|
2319
|
+
const moveColumn = useCallback11(
|
|
2320
|
+
(from, to) => {
|
|
2321
|
+
if (!editor || !tableInfo || from === to) return;
|
|
2322
|
+
const { state } = editor;
|
|
2323
|
+
const tableStart = tableInfo.pos;
|
|
2324
|
+
const tableNode = state.doc.nodeAt(tableStart);
|
|
2325
|
+
if (!tableNode) return;
|
|
2326
|
+
const tr = state.tr;
|
|
2327
|
+
tableNode.forEach((row, rowOffset) => {
|
|
2328
|
+
if (row.type.name !== "tableRow") return;
|
|
2329
|
+
const cells = [];
|
|
2330
|
+
row.forEach((cell, cellOffset) => {
|
|
2331
|
+
cells.push({ node: cell, pos: tableStart + 1 + rowOffset + 1 + cellOffset });
|
|
2332
|
+
});
|
|
2333
|
+
if (from >= cells.length || to >= cells.length) return;
|
|
2334
|
+
const reordered = [...cells];
|
|
2335
|
+
const [moved] = reordered.splice(from, 1);
|
|
2336
|
+
reordered.splice(to, 0, moved);
|
|
2337
|
+
const rowPos = tableStart + 1 + rowOffset;
|
|
2338
|
+
const mappedRowPos = tr.mapping.map(rowPos);
|
|
2339
|
+
const newRow = row.type.create(row.attrs, reordered.map((c) => c.node));
|
|
2340
|
+
tr.replaceWith(mappedRowPos, mappedRowPos + row.nodeSize, newRow);
|
|
2341
|
+
});
|
|
2342
|
+
editor.view.dispatch(tr);
|
|
2343
|
+
},
|
|
2344
|
+
[editor, tableInfo]
|
|
2345
|
+
);
|
|
2346
|
+
const moveRow = useCallback11(
|
|
2347
|
+
(from, to) => {
|
|
2348
|
+
if (!editor || !tableInfo || from === to) return;
|
|
2349
|
+
const { state } = editor;
|
|
2350
|
+
const tableStart = tableInfo.pos;
|
|
2351
|
+
const tableNode = state.doc.nodeAt(tableStart);
|
|
2352
|
+
if (!tableNode) return;
|
|
2353
|
+
const rows = [];
|
|
2354
|
+
tableNode.forEach((row) => rows.push(row));
|
|
2355
|
+
if (from >= rows.length || to >= rows.length) return;
|
|
2356
|
+
const reordered = [...rows];
|
|
2357
|
+
const [moved] = reordered.splice(from, 1);
|
|
2358
|
+
reordered.splice(to, 0, moved);
|
|
2359
|
+
const tr = state.tr;
|
|
2360
|
+
tr.replaceWith(
|
|
2361
|
+
tableStart + 1,
|
|
2362
|
+
tableStart + 1 + tableNode.content.size,
|
|
2363
|
+
reordered
|
|
2364
|
+
);
|
|
2365
|
+
editor.view.dispatch(tr);
|
|
2366
|
+
},
|
|
2367
|
+
[editor, tableInfo]
|
|
2368
|
+
);
|
|
2369
|
+
const handleGripDragStart = useCallback11(
|
|
2370
|
+
(type, index, e) => {
|
|
2371
|
+
e.preventDefault();
|
|
2372
|
+
e.stopPropagation();
|
|
2373
|
+
setDropdown(null);
|
|
2374
|
+
const startX = e.clientX;
|
|
2375
|
+
const startY = e.clientY;
|
|
2376
|
+
let hasMoved = false;
|
|
2377
|
+
const dragState = { type, fromIndex: index, toIndex: index };
|
|
2378
|
+
dragRef.current = dragState;
|
|
2379
|
+
setDrag(dragState);
|
|
2380
|
+
const handleMouseMove = (ev) => {
|
|
2381
|
+
const dx = ev.clientX - startX;
|
|
2382
|
+
const dy = ev.clientY - startY;
|
|
2383
|
+
if (!hasMoved && Math.abs(type === "column" ? dx : dy) < 5) return;
|
|
2384
|
+
hasMoved = true;
|
|
2385
|
+
let newIndex = index;
|
|
2386
|
+
if (type === "column") {
|
|
2387
|
+
for (let i = 0; i < colGrips.length; i++) {
|
|
2388
|
+
const mid = colGrips[i].left + colGrips[i].width / 2;
|
|
2389
|
+
if (ev.clientX < mid) {
|
|
2390
|
+
newIndex = i;
|
|
2391
|
+
break;
|
|
2392
|
+
}
|
|
2393
|
+
newIndex = i;
|
|
1933
2394
|
}
|
|
1934
|
-
|
|
2395
|
+
} else {
|
|
2396
|
+
for (let i = 0; i < rowGrips.length; i++) {
|
|
2397
|
+
const mid = rowGrips[i].top + rowGrips[i].height / 2;
|
|
2398
|
+
if (ev.clientY < mid) {
|
|
2399
|
+
newIndex = i;
|
|
2400
|
+
break;
|
|
2401
|
+
}
|
|
2402
|
+
newIndex = i;
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
const updated = { type, fromIndex: index, toIndex: newIndex };
|
|
2406
|
+
dragRef.current = updated;
|
|
2407
|
+
setDrag(updated);
|
|
2408
|
+
};
|
|
2409
|
+
const handleMouseUp = () => {
|
|
2410
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
2411
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
2412
|
+
const finalDrag = dragRef.current;
|
|
2413
|
+
dragRef.current = null;
|
|
2414
|
+
setDrag(null);
|
|
2415
|
+
if (finalDrag && hasMoved && finalDrag.fromIndex !== finalDrag.toIndex) {
|
|
2416
|
+
if (type === "column") {
|
|
2417
|
+
moveColumn(finalDrag.fromIndex, finalDrag.toIndex);
|
|
2418
|
+
} else {
|
|
2419
|
+
moveRow(finalDrag.fromIndex, finalDrag.toIndex);
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
};
|
|
2423
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
2424
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
2425
|
+
},
|
|
2426
|
+
[colGrips, rowGrips, moveColumn, moveRow]
|
|
2427
|
+
);
|
|
2428
|
+
if (!editor || !tableInfo || colGrips.length === 0) return null;
|
|
2429
|
+
const GRIP_SIZE = 20;
|
|
2430
|
+
const GRIP_GAP = 4;
|
|
2431
|
+
const gripsVisible = isHovering || !!dropdown;
|
|
2432
|
+
const columnDropdownItems = [
|
|
2433
|
+
{
|
|
2434
|
+
label: "Toggle header column",
|
|
2435
|
+
icon: /* @__PURE__ */ jsx15(IconTableColumn, { size: 16 }),
|
|
2436
|
+
action: () => {
|
|
2437
|
+
editor.chain().focus().toggleHeaderColumn().run();
|
|
2438
|
+
setDropdown(null);
|
|
1935
2439
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
}
|
|
2440
|
+
},
|
|
2441
|
+
{
|
|
2442
|
+
label: "Insert column before",
|
|
2443
|
+
icon: /* @__PURE__ */ jsx15(IconColumnInsertLeft, { size: 16 }),
|
|
2444
|
+
action: () => {
|
|
2445
|
+
editor.chain().focus().addColumnBefore().run();
|
|
2446
|
+
setDropdown(null);
|
|
2447
|
+
},
|
|
2448
|
+
separator: true
|
|
2449
|
+
},
|
|
2450
|
+
{
|
|
2451
|
+
label: "Insert column after",
|
|
2452
|
+
icon: /* @__PURE__ */ jsx15(IconColumnInsertRight, { size: 16 }),
|
|
2453
|
+
action: () => {
|
|
2454
|
+
editor.chain().focus().addColumnAfter().run();
|
|
2455
|
+
setDropdown(null);
|
|
2456
|
+
}
|
|
2457
|
+
},
|
|
2458
|
+
{
|
|
2459
|
+
label: "Merge cells",
|
|
2460
|
+
icon: /* @__PURE__ */ jsx15(IconArrowMerge, { size: 16 }),
|
|
2461
|
+
action: () => {
|
|
2462
|
+
editor.chain().focus().mergeCells().run();
|
|
2463
|
+
setDropdown(null);
|
|
2464
|
+
},
|
|
2465
|
+
separator: true
|
|
2466
|
+
},
|
|
2467
|
+
{
|
|
2468
|
+
label: "Split cell",
|
|
2469
|
+
icon: /* @__PURE__ */ jsx15(IconArrowsSplit, { size: 16 }),
|
|
2470
|
+
action: () => {
|
|
2471
|
+
editor.chain().focus().splitCell().run();
|
|
2472
|
+
setDropdown(null);
|
|
2473
|
+
}
|
|
2474
|
+
},
|
|
2475
|
+
{
|
|
2476
|
+
label: "Delete column",
|
|
2477
|
+
icon: /* @__PURE__ */ jsx15(IconColumnRemove, { size: 16 }),
|
|
2478
|
+
action: () => {
|
|
2479
|
+
editor.chain().focus().deleteColumn().run();
|
|
2480
|
+
setDropdown(null);
|
|
2481
|
+
},
|
|
2482
|
+
destructive: true,
|
|
2483
|
+
separator: true
|
|
2484
|
+
}
|
|
2485
|
+
];
|
|
2486
|
+
const rowDropdownItems = [
|
|
2487
|
+
{
|
|
2488
|
+
label: "Toggle header row",
|
|
2489
|
+
icon: /* @__PURE__ */ jsx15(IconTableRow, { size: 16 }),
|
|
2490
|
+
action: () => {
|
|
2491
|
+
editor.chain().focus().toggleHeaderRow().run();
|
|
2492
|
+
setDropdown(null);
|
|
2493
|
+
}
|
|
2494
|
+
},
|
|
2495
|
+
{
|
|
2496
|
+
label: "Insert row above",
|
|
2497
|
+
icon: /* @__PURE__ */ jsx15(IconRowInsertTop, { size: 16 }),
|
|
2498
|
+
action: () => {
|
|
2499
|
+
editor.chain().focus().addRowBefore().run();
|
|
2500
|
+
setDropdown(null);
|
|
2501
|
+
},
|
|
2502
|
+
separator: true
|
|
2503
|
+
},
|
|
2504
|
+
{
|
|
2505
|
+
label: "Insert row below",
|
|
2506
|
+
icon: /* @__PURE__ */ jsx15(IconRowInsertBottom, { size: 16 }),
|
|
2507
|
+
action: () => {
|
|
2508
|
+
editor.chain().focus().addRowAfter().run();
|
|
2509
|
+
setDropdown(null);
|
|
2510
|
+
}
|
|
2511
|
+
},
|
|
2512
|
+
{
|
|
2513
|
+
label: "Merge cells",
|
|
2514
|
+
icon: /* @__PURE__ */ jsx15(IconArrowMerge, { size: 16 }),
|
|
2515
|
+
action: () => {
|
|
2516
|
+
editor.chain().focus().mergeCells().run();
|
|
2517
|
+
setDropdown(null);
|
|
2518
|
+
},
|
|
2519
|
+
separator: true
|
|
2520
|
+
},
|
|
2521
|
+
{
|
|
2522
|
+
label: "Split cell",
|
|
2523
|
+
icon: /* @__PURE__ */ jsx15(IconArrowsSplit, { size: 16 }),
|
|
2524
|
+
action: () => {
|
|
2525
|
+
editor.chain().focus().splitCell().run();
|
|
2526
|
+
setDropdown(null);
|
|
2527
|
+
}
|
|
2528
|
+
},
|
|
2529
|
+
{
|
|
2530
|
+
label: "Delete row",
|
|
2531
|
+
icon: /* @__PURE__ */ jsx15(IconRowRemove, { size: 16 }),
|
|
2532
|
+
action: () => {
|
|
2533
|
+
editor.chain().focus().deleteRow().run();
|
|
2534
|
+
setDropdown(null);
|
|
2535
|
+
},
|
|
2536
|
+
destructive: true,
|
|
2537
|
+
separator: true
|
|
2538
|
+
}
|
|
2539
|
+
];
|
|
2540
|
+
const dropdownItems = dropdown?.type === "column" ? columnDropdownItems : rowDropdownItems;
|
|
2541
|
+
return createPortal(
|
|
2542
|
+
/* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
2543
|
+
colGrips.map((grip, i) => /* @__PURE__ */ jsx15(
|
|
2544
|
+
"button",
|
|
2545
|
+
{
|
|
2546
|
+
type: "button",
|
|
2547
|
+
className: `nph-table-grip nph-table-grip--col${gripsVisible ? " nph-table-grip--visible" : ""}${drag?.type === "column" && drag.fromIndex === i ? " nph-table-grip--dragging" : ""}`,
|
|
2548
|
+
style: {
|
|
2549
|
+
position: "fixed",
|
|
2550
|
+
left: grip.left + grip.width / 2 - GRIP_SIZE / 2,
|
|
2551
|
+
top: grip.top - GRIP_SIZE - GRIP_GAP,
|
|
2552
|
+
width: GRIP_SIZE,
|
|
2553
|
+
height: GRIP_SIZE,
|
|
2554
|
+
cursor: "grab"
|
|
2555
|
+
},
|
|
2556
|
+
onMouseDown: (e) => handleGripDragStart("column", i, e),
|
|
2557
|
+
onClick: (e) => handleColGripClick(i, e),
|
|
2558
|
+
"aria-label": `Column ${i + 1} options`,
|
|
2559
|
+
children: /* @__PURE__ */ jsx15(IconGripHorizontal, { size: 14 })
|
|
2560
|
+
},
|
|
2561
|
+
`col-${i}`
|
|
2562
|
+
)),
|
|
2563
|
+
rowGrips.map((grip, i) => /* @__PURE__ */ jsx15(
|
|
2564
|
+
"button",
|
|
2565
|
+
{
|
|
2566
|
+
type: "button",
|
|
2567
|
+
className: `nph-table-grip nph-table-grip--row${gripsVisible ? " nph-table-grip--visible" : ""}${drag?.type === "row" && drag.fromIndex === i ? " nph-table-grip--dragging" : ""}`,
|
|
2568
|
+
style: {
|
|
2569
|
+
position: "fixed",
|
|
2570
|
+
left: grip.left - GRIP_SIZE - GRIP_GAP,
|
|
2571
|
+
top: grip.top + grip.height / 2 - GRIP_SIZE / 2,
|
|
2572
|
+
width: GRIP_SIZE,
|
|
2573
|
+
height: GRIP_SIZE,
|
|
2574
|
+
cursor: "grab"
|
|
2575
|
+
},
|
|
2576
|
+
onMouseDown: (e) => handleGripDragStart("row", i, e),
|
|
2577
|
+
onClick: (e) => handleRowGripClick(i, e),
|
|
2578
|
+
"aria-label": `Row ${i + 1} options`,
|
|
2579
|
+
children: /* @__PURE__ */ jsx15(IconGripVertical, { size: 14 })
|
|
2580
|
+
},
|
|
2581
|
+
`row-${i}`
|
|
2582
|
+
)),
|
|
2583
|
+
tableRect && /* @__PURE__ */ jsxs15(
|
|
2584
|
+
"button",
|
|
2585
|
+
{
|
|
2586
|
+
type: "button",
|
|
2587
|
+
className: `nph-table-grip nph-table-grip--delete${gripsVisible ? " nph-table-grip--visible" : ""}`,
|
|
2588
|
+
style: {
|
|
2589
|
+
position: "fixed",
|
|
2590
|
+
left: tableRect.left + tableRect.width / 2 - 60,
|
|
2591
|
+
top: tableRect.bottom + GRIP_GAP,
|
|
2592
|
+
width: 120,
|
|
2593
|
+
height: 24
|
|
2594
|
+
},
|
|
2595
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2596
|
+
onClick: () => {
|
|
2597
|
+
editor.chain().focus().deleteTable().run();
|
|
2598
|
+
},
|
|
2599
|
+
"aria-label": "Delete table",
|
|
2600
|
+
children: [
|
|
2601
|
+
/* @__PURE__ */ jsx15(IconTableOff, { size: 14 }),
|
|
2602
|
+
/* @__PURE__ */ jsx15("span", { style: { fontSize: 12 }, children: "Delete table" })
|
|
2603
|
+
]
|
|
2604
|
+
}
|
|
2605
|
+
),
|
|
2606
|
+
dropdown && /* @__PURE__ */ jsx15(
|
|
2607
|
+
"div",
|
|
2608
|
+
{
|
|
2609
|
+
ref: dropdownRef,
|
|
2610
|
+
className: "nph-table-dropdown",
|
|
2611
|
+
style: {
|
|
2612
|
+
position: "fixed",
|
|
2613
|
+
left: dropdown.x,
|
|
2614
|
+
top: dropdown.y,
|
|
2615
|
+
transform: dropdown.type === "column" ? "translateY(-100%)" : void 0
|
|
2616
|
+
},
|
|
2617
|
+
children: dropdownItems.map((item, i) => /* @__PURE__ */ jsxs15("div", { children: [
|
|
2618
|
+
item.separator && i > 0 && /* @__PURE__ */ jsx15("div", { className: "nph-table-dropdown__separator" }),
|
|
2619
|
+
/* @__PURE__ */ jsxs15(
|
|
2620
|
+
"button",
|
|
2621
|
+
{
|
|
2622
|
+
type: "button",
|
|
2623
|
+
className: `nph-table-dropdown__item ${item.destructive ? "nph-table-dropdown__item--destructive" : ""}`,
|
|
2624
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2625
|
+
onClick: item.action,
|
|
2626
|
+
children: [
|
|
2627
|
+
item.icon,
|
|
2628
|
+
/* @__PURE__ */ jsx15("span", { children: item.label })
|
|
2629
|
+
]
|
|
2630
|
+
}
|
|
2631
|
+
)
|
|
2632
|
+
] }, i))
|
|
2633
|
+
}
|
|
2634
|
+
),
|
|
2635
|
+
drag && drag.fromIndex !== drag.toIndex && tableRect && (drag.type === "column" ? /* @__PURE__ */ jsx15(
|
|
2636
|
+
"div",
|
|
2637
|
+
{
|
|
2638
|
+
className: "nph-table-drop-indicator nph-table-drop-indicator--col",
|
|
2639
|
+
style: {
|
|
2640
|
+
position: "fixed",
|
|
2641
|
+
left: drag.toIndex < colGrips.length ? colGrips[drag.toIndex].left - 1 : colGrips[colGrips.length - 1].left + colGrips[colGrips.length - 1].width,
|
|
2642
|
+
top: tableRect.top,
|
|
2643
|
+
width: 2,
|
|
2644
|
+
height: tableRect.height
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
) : /* @__PURE__ */ jsx15(
|
|
2648
|
+
"div",
|
|
2649
|
+
{
|
|
2650
|
+
className: "nph-table-drop-indicator nph-table-drop-indicator--row",
|
|
2651
|
+
style: {
|
|
2652
|
+
position: "fixed",
|
|
2653
|
+
left: tableRect.left,
|
|
2654
|
+
top: drag.toIndex < rowGrips.length ? rowGrips[drag.toIndex].top - 1 : rowGrips[rowGrips.length - 1].top + rowGrips[rowGrips.length - 1].height,
|
|
2655
|
+
width: tableRect.width,
|
|
2656
|
+
height: 2
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
))
|
|
2660
|
+
] }),
|
|
2661
|
+
document.body
|
|
2662
|
+
);
|
|
2663
|
+
}
|
|
1940
2664
|
|
|
1941
2665
|
// src/react/Editor.tsx
|
|
1942
|
-
import {
|
|
1943
|
-
|
|
2666
|
+
import { useMemo as useMemo2, useState as useState10, useCallback as useCallback12, useRef as useRef11, useEffect as useEffect7 } from "react";
|
|
2667
|
+
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2668
|
+
function Editor5({
|
|
1944
2669
|
content,
|
|
1945
2670
|
className,
|
|
1946
2671
|
editable = true,
|
|
1947
2672
|
immediatelyRender = false,
|
|
1948
2673
|
showTextMenu = true,
|
|
1949
2674
|
showSlashMenu = true,
|
|
1950
|
-
showImageMenu =
|
|
2675
|
+
showImageMenu = false,
|
|
2676
|
+
showDragHandle = true,
|
|
1951
2677
|
extensions,
|
|
1952
2678
|
bubbleMenuExtras,
|
|
1953
2679
|
onUpdate,
|
|
@@ -1959,6 +2685,41 @@ function Editor3({
|
|
|
1959
2685
|
slashCommand,
|
|
1960
2686
|
placeholder
|
|
1961
2687
|
}) {
|
|
2688
|
+
const [actionMenuAnchor, setActionMenuAnchor] = useState10(null);
|
|
2689
|
+
const [actionMenuEditor, setActionMenuEditor] = useState10(null);
|
|
2690
|
+
const actionMenuRef = useRef11(null);
|
|
2691
|
+
useEffect7(() => {
|
|
2692
|
+
if (!actionMenuAnchor) return;
|
|
2693
|
+
const handlePointerDown = (e) => {
|
|
2694
|
+
if (actionMenuRef.current && !actionMenuRef.current.contains(e.target)) {
|
|
2695
|
+
setActionMenuAnchor(null);
|
|
2696
|
+
}
|
|
2697
|
+
};
|
|
2698
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
2699
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown);
|
|
2700
|
+
}, [actionMenuAnchor]);
|
|
2701
|
+
const dragHandleCallbacks = useMemo2(
|
|
2702
|
+
() => ({
|
|
2703
|
+
onAddBlock: (editor) => {
|
|
2704
|
+
const { state } = editor;
|
|
2705
|
+
const { selection } = state;
|
|
2706
|
+
const { $anchor } = selection;
|
|
2707
|
+
const topDepth = Math.min($anchor.depth, 1);
|
|
2708
|
+
const endOfBlock = $anchor.end(topDepth);
|
|
2709
|
+
const insertPos = endOfBlock + 1;
|
|
2710
|
+
editor.chain().focus().insertContentAt(insertPos, { type: "paragraph" }).focus(insertPos + 1).run();
|
|
2711
|
+
requestAnimationFrame(() => {
|
|
2712
|
+
editor.commands.insertContent("/");
|
|
2713
|
+
});
|
|
2714
|
+
},
|
|
2715
|
+
onGripClick: (editor, _node, element) => {
|
|
2716
|
+
setActionMenuEditor(editor);
|
|
2717
|
+
setActionMenuAnchor((prev) => prev === element ? null : element);
|
|
2718
|
+
}
|
|
2719
|
+
}),
|
|
2720
|
+
[]
|
|
2721
|
+
);
|
|
2722
|
+
const enableDragHandle = showDragHandle && editable;
|
|
1962
2723
|
const normalizeExtras = (extras) => {
|
|
1963
2724
|
const result = {
|
|
1964
2725
|
start: [],
|
|
@@ -1975,58 +2736,184 @@ function Editor3({
|
|
|
1975
2736
|
};
|
|
1976
2737
|
const textExtras = normalizeExtras(bubbleMenuExtras?.text);
|
|
1977
2738
|
const imageExtras = normalizeExtras(bubbleMenuExtras?.image);
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
leadingExtras: textExtras.start,
|
|
2012
|
-
trailingExtras: textExtras.end
|
|
2739
|
+
const handleCloseActionMenu = useCallback12(() => {
|
|
2740
|
+
setActionMenuAnchor(null);
|
|
2741
|
+
}, []);
|
|
2742
|
+
return /* @__PURE__ */ jsxs16("div", { className, children: [
|
|
2743
|
+
/* @__PURE__ */ jsx16(EditorRoot, { children: /* @__PURE__ */ jsxs16(
|
|
2744
|
+
EditorContent,
|
|
2745
|
+
{
|
|
2746
|
+
onUpdate,
|
|
2747
|
+
onCreate,
|
|
2748
|
+
immediatelyRender,
|
|
2749
|
+
editable,
|
|
2750
|
+
content,
|
|
2751
|
+
extensions: [
|
|
2752
|
+
...extension_kit_default({
|
|
2753
|
+
uploadImage,
|
|
2754
|
+
collaboration,
|
|
2755
|
+
imageBlockView: ImageBlockView,
|
|
2756
|
+
videoBlockView: VideoBlockView,
|
|
2757
|
+
mention: mentionOptions,
|
|
2758
|
+
reference: referenceOptions,
|
|
2759
|
+
slashCommand,
|
|
2760
|
+
dragHandle: enableDragHandle,
|
|
2761
|
+
dragHandleCallbacks: enableDragHandle ? dragHandleCallbacks : void 0,
|
|
2762
|
+
placeholder
|
|
2763
|
+
}),
|
|
2764
|
+
...extensions ?? []
|
|
2765
|
+
],
|
|
2766
|
+
editorProps: {
|
|
2767
|
+
attributes: {
|
|
2768
|
+
class: "nph-editor max-w-none outline-none"
|
|
2769
|
+
},
|
|
2770
|
+
handleKeyDown: (view, event) => {
|
|
2771
|
+
return !!handleCommandNavigation?.(event);
|
|
2013
2772
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2773
|
+
},
|
|
2774
|
+
children: [
|
|
2775
|
+
showTextMenu ? /* @__PURE__ */ jsx16(
|
|
2776
|
+
TextMenu,
|
|
2777
|
+
{
|
|
2778
|
+
leadingExtras: textExtras.start,
|
|
2779
|
+
trailingExtras: textExtras.end
|
|
2780
|
+
}
|
|
2781
|
+
) : null,
|
|
2782
|
+
showImageMenu ? /* @__PURE__ */ jsx16(
|
|
2783
|
+
ImageMenu,
|
|
2784
|
+
{
|
|
2785
|
+
leadingExtras: imageExtras.start,
|
|
2786
|
+
trailingExtras: imageExtras.end
|
|
2787
|
+
}
|
|
2788
|
+
) : null,
|
|
2789
|
+
/* @__PURE__ */ jsx16(LinkMenu, {}),
|
|
2790
|
+
/* @__PURE__ */ jsx16(TableMenu, {}),
|
|
2791
|
+
showSlashMenu ? /* @__PURE__ */ jsx16(SlashMenu, {}) : null
|
|
2792
|
+
]
|
|
2793
|
+
}
|
|
2794
|
+
) }),
|
|
2795
|
+
actionMenuAnchor && actionMenuEditor && /* @__PURE__ */ jsx16(
|
|
2796
|
+
"div",
|
|
2797
|
+
{
|
|
2798
|
+
ref: actionMenuRef,
|
|
2799
|
+
style: {
|
|
2800
|
+
position: "fixed",
|
|
2801
|
+
zIndex: 1e4,
|
|
2802
|
+
top: actionMenuAnchor.getBoundingClientRect().bottom + 4,
|
|
2803
|
+
left: actionMenuAnchor.getBoundingClientRect().left
|
|
2804
|
+
},
|
|
2805
|
+
children: /* @__PURE__ */ jsx16(
|
|
2806
|
+
BlockActionMenu,
|
|
2017
2807
|
{
|
|
2018
|
-
|
|
2019
|
-
|
|
2808
|
+
editor: actionMenuEditor,
|
|
2809
|
+
onClose: handleCloseActionMenu
|
|
2020
2810
|
}
|
|
2021
|
-
)
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2811
|
+
)
|
|
2812
|
+
}
|
|
2813
|
+
)
|
|
2814
|
+
] });
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
// src/react/TableOfContents.tsx
|
|
2818
|
+
import { useCurrentEditor as useCurrentEditor6, useEditorState as useEditorState6 } from "@tiptap/react";
|
|
2819
|
+
import { useCallback as useCallback13, useEffect as useEffect8, useRef as useRef12, useState as useState11 } from "react";
|
|
2820
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2821
|
+
function TableOfContents({
|
|
2822
|
+
className,
|
|
2823
|
+
itemClassName,
|
|
2824
|
+
activeClassName
|
|
2825
|
+
}) {
|
|
2826
|
+
const { editor } = useCurrentEditor6();
|
|
2827
|
+
const [activeId, setActiveId] = useState11(null);
|
|
2828
|
+
const observerRef = useRef12(null);
|
|
2829
|
+
const headings = useEditorState6({
|
|
2830
|
+
editor,
|
|
2831
|
+
selector: (ctx) => {
|
|
2832
|
+
if (!ctx.editor) return [];
|
|
2833
|
+
const items = [];
|
|
2834
|
+
ctx.editor.state.doc.descendants((node, pos) => {
|
|
2835
|
+
if (node.type.name === "heading") {
|
|
2836
|
+
const id = `heading-${pos}`;
|
|
2837
|
+
items.push({
|
|
2838
|
+
id,
|
|
2839
|
+
level: node.attrs.level,
|
|
2840
|
+
text: node.textContent,
|
|
2841
|
+
pos
|
|
2842
|
+
});
|
|
2843
|
+
}
|
|
2844
|
+
});
|
|
2845
|
+
return items;
|
|
2025
2846
|
}
|
|
2026
|
-
|
|
2847
|
+
});
|
|
2848
|
+
useEffect8(() => {
|
|
2849
|
+
if (!editor || !headings || headings.length === 0) return;
|
|
2850
|
+
observerRef.current?.disconnect();
|
|
2851
|
+
const callback = (entries) => {
|
|
2852
|
+
const visibleEntries = entries.filter((e) => e.isIntersecting);
|
|
2853
|
+
if (visibleEntries.length > 0) {
|
|
2854
|
+
const firstVisible = visibleEntries[0];
|
|
2855
|
+
const id = firstVisible.target.getAttribute("data-toc-id");
|
|
2856
|
+
if (id) setActiveId(id);
|
|
2857
|
+
}
|
|
2858
|
+
};
|
|
2859
|
+
const observer = new IntersectionObserver(callback, {
|
|
2860
|
+
rootMargin: "-80px 0px -70% 0px",
|
|
2861
|
+
threshold: 0
|
|
2862
|
+
});
|
|
2863
|
+
observerRef.current = observer;
|
|
2864
|
+
const editorEl = editor.view.dom;
|
|
2865
|
+
headings.forEach((heading) => {
|
|
2866
|
+
try {
|
|
2867
|
+
const domNode = editor.view.nodeDOM(heading.pos);
|
|
2868
|
+
if (domNode && domNode instanceof HTMLElement) {
|
|
2869
|
+
domNode.setAttribute("data-toc-id", heading.id);
|
|
2870
|
+
observer.observe(domNode);
|
|
2871
|
+
}
|
|
2872
|
+
} catch {
|
|
2873
|
+
}
|
|
2874
|
+
});
|
|
2875
|
+
return () => {
|
|
2876
|
+
observer.disconnect();
|
|
2877
|
+
};
|
|
2878
|
+
}, [editor, headings]);
|
|
2879
|
+
const handleClick = useCallback13(
|
|
2880
|
+
(pos) => {
|
|
2881
|
+
if (!editor) return;
|
|
2882
|
+
editor.chain().focus().setTextSelection(pos + 1).run();
|
|
2883
|
+
try {
|
|
2884
|
+
const domNode = editor.view.nodeDOM(pos);
|
|
2885
|
+
if (domNode && domNode instanceof HTMLElement) {
|
|
2886
|
+
domNode.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
2887
|
+
}
|
|
2888
|
+
} catch {
|
|
2889
|
+
}
|
|
2890
|
+
},
|
|
2891
|
+
[editor]
|
|
2892
|
+
);
|
|
2893
|
+
if (!editor || !headings || headings.length === 0) return null;
|
|
2894
|
+
return /* @__PURE__ */ jsx17("nav", { className: className ?? "nph-toc", children: headings.map((heading) => {
|
|
2895
|
+
const isActive = activeId === heading.id;
|
|
2896
|
+
const itemClass = [
|
|
2897
|
+
itemClassName ?? "nph-toc__item",
|
|
2898
|
+
isActive ? activeClassName ?? "nph-toc__item--active" : ""
|
|
2899
|
+
].filter(Boolean).join(" ");
|
|
2900
|
+
return /* @__PURE__ */ jsx17(
|
|
2901
|
+
"button",
|
|
2902
|
+
{
|
|
2903
|
+
type: "button",
|
|
2904
|
+
className: itemClass,
|
|
2905
|
+
style: { paddingLeft: `${(heading.level - 1) * 12 + 8}px` },
|
|
2906
|
+
onClick: () => handleClick(heading.pos),
|
|
2907
|
+
title: heading.text,
|
|
2908
|
+
children: heading.text || `Heading ${heading.level}`
|
|
2909
|
+
},
|
|
2910
|
+
heading.id
|
|
2911
|
+
);
|
|
2912
|
+
}) });
|
|
2027
2913
|
}
|
|
2028
2914
|
export {
|
|
2029
|
-
|
|
2915
|
+
Editor5 as Editor,
|
|
2916
|
+
TableOfContents,
|
|
2030
2917
|
TextMenu
|
|
2031
2918
|
};
|
|
2032
2919
|
//# sourceMappingURL=index.js.map
|