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.cjs
CHANGED
|
@@ -31,13 +31,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
// src/react/index.ts
|
|
32
32
|
var react_exports = {};
|
|
33
33
|
__export(react_exports, {
|
|
34
|
-
Editor: () =>
|
|
34
|
+
Editor: () => Editor5,
|
|
35
|
+
TableOfContents: () => TableOfContents,
|
|
35
36
|
TextMenu: () => TextMenu
|
|
36
37
|
});
|
|
37
38
|
module.exports = __toCommonJS(react_exports);
|
|
38
39
|
|
|
39
40
|
// src/headless/index.ts
|
|
40
|
-
var
|
|
41
|
+
var import_react10 = require("@tiptap/react");
|
|
41
42
|
|
|
42
43
|
// src/headless/components/editor.tsx
|
|
43
44
|
var import_react = require("@tiptap/react");
|
|
@@ -69,109 +70,177 @@ EditorContent.displayName = "EditorContent";
|
|
|
69
70
|
// src/headless/components/editor-command.tsx
|
|
70
71
|
var import_jotai4 = require("jotai");
|
|
71
72
|
var import_react3 = require("react");
|
|
72
|
-
var
|
|
73
|
+
var import_react_dom = require("react-dom");
|
|
73
74
|
|
|
74
75
|
// src/headless/utils/atoms.ts
|
|
75
76
|
var import_jotai3 = require("jotai");
|
|
76
77
|
var queryAtom = (0, import_jotai3.atom)("");
|
|
77
78
|
var rangeAtom = (0, import_jotai3.atom)(null);
|
|
79
|
+
var slashMenuOpenAtom = (0, import_jotai3.atom)(false);
|
|
80
|
+
var slashMenuRectAtom = (0, import_jotai3.atom)(null);
|
|
78
81
|
|
|
79
82
|
// src/headless/components/editor-command.tsx
|
|
80
|
-
var import_tunnel_rat = __toESM(require("tunnel-rat"), 1);
|
|
81
83
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
82
|
-
var commandTunnel = (0, import_tunnel_rat.default)();
|
|
83
|
-
var EditorCommandOut = ({
|
|
84
|
-
query,
|
|
85
|
-
range
|
|
86
|
-
}) => {
|
|
87
|
-
const setQuery = (0, import_jotai4.useSetAtom)(queryAtom, { store: novelStore });
|
|
88
|
-
const setRange = (0, import_jotai4.useSetAtom)(rangeAtom, { store: novelStore });
|
|
89
|
-
(0, import_react3.useEffect)(() => {
|
|
90
|
-
setQuery(query);
|
|
91
|
-
}, [query, setQuery]);
|
|
92
|
-
(0, import_react3.useEffect)(() => {
|
|
93
|
-
setRange(range);
|
|
94
|
-
}, [range, setRange]);
|
|
95
|
-
(0, import_react3.useEffect)(() => {
|
|
96
|
-
const navigationKeys = ["ArrowUp", "ArrowDown", "Enter"];
|
|
97
|
-
const onKeyDown = (e) => {
|
|
98
|
-
if (navigationKeys.includes(e.key)) {
|
|
99
|
-
e.preventDefault();
|
|
100
|
-
const commandRef = document.querySelector("#slash-command");
|
|
101
|
-
if (commandRef)
|
|
102
|
-
commandRef.dispatchEvent(
|
|
103
|
-
new KeyboardEvent("keydown", {
|
|
104
|
-
key: e.key,
|
|
105
|
-
cancelable: true,
|
|
106
|
-
bubbles: true
|
|
107
|
-
})
|
|
108
|
-
);
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
document.addEventListener("keydown", onKeyDown);
|
|
113
|
-
return () => {
|
|
114
|
-
document.removeEventListener("keydown", onKeyDown);
|
|
115
|
-
};
|
|
116
|
-
}, []);
|
|
117
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(commandTunnel.Out, {});
|
|
118
|
-
};
|
|
119
|
-
var CommandAny = import_cmdk.Command;
|
|
120
84
|
var EditorCommand = (0, import_react3.forwardRef)(
|
|
121
85
|
({ children, className, ...rest }, ref) => {
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
86
|
+
const isOpen = (0, import_jotai4.useAtomValue)(slashMenuOpenAtom, { store: novelStore });
|
|
87
|
+
const rect = (0, import_jotai4.useAtomValue)(slashMenuRectAtom, { store: novelStore });
|
|
88
|
+
const containerRef = (0, import_react3.useRef)(null);
|
|
89
|
+
const [activeIndex, setActiveIndex] = (0, import_react3.useState)(0);
|
|
90
|
+
const contentRef = (0, import_react3.useRef)(null);
|
|
91
|
+
const query = (0, import_jotai4.useAtomValue)(queryAtom, { store: novelStore });
|
|
92
|
+
(0, import_react3.useEffect)(() => {
|
|
93
|
+
setActiveIndex(0);
|
|
94
|
+
}, [query]);
|
|
95
|
+
if (typeof document !== "undefined" && !containerRef.current) {
|
|
96
|
+
const el = document.createElement("div");
|
|
97
|
+
el.style.position = "fixed";
|
|
98
|
+
el.style.zIndex = "9999";
|
|
99
|
+
el.style.minWidth = "240px";
|
|
100
|
+
el.style.display = "none";
|
|
101
|
+
containerRef.current = el;
|
|
102
|
+
}
|
|
103
|
+
(0, import_react3.useEffect)(() => {
|
|
104
|
+
const el = containerRef.current;
|
|
105
|
+
if (!el) return;
|
|
106
|
+
document.body.appendChild(el);
|
|
107
|
+
return () => {
|
|
108
|
+
el.remove();
|
|
109
|
+
};
|
|
110
|
+
}, []);
|
|
111
|
+
(0, import_react3.useLayoutEffect)(() => {
|
|
112
|
+
const container = containerRef.current;
|
|
113
|
+
if (!container) return;
|
|
114
|
+
if (!isOpen || !rect) {
|
|
115
|
+
container.style.display = "none";
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
container.style.display = "";
|
|
119
|
+
const menuHeight = container.offsetHeight || 360;
|
|
120
|
+
const viewportHeight = window.innerHeight;
|
|
121
|
+
const spaceBelow = viewportHeight - rect.bottom;
|
|
122
|
+
const spaceAbove = rect.top;
|
|
123
|
+
let top;
|
|
124
|
+
if (spaceBelow < menuHeight + 16 && spaceAbove > spaceBelow) {
|
|
125
|
+
top = Math.round(rect.top - menuHeight - 8);
|
|
126
|
+
if (top < 8) top = 8;
|
|
127
|
+
} else {
|
|
128
|
+
top = Math.round(rect.bottom + 8);
|
|
144
129
|
}
|
|
145
|
-
|
|
130
|
+
let left = Math.round(rect.left);
|
|
131
|
+
const menuWidth = container.offsetWidth || 280;
|
|
132
|
+
if (left + menuWidth > window.innerWidth - 8) {
|
|
133
|
+
left = window.innerWidth - menuWidth - 8;
|
|
134
|
+
}
|
|
135
|
+
if (left < 8) left = 8;
|
|
136
|
+
container.style.top = `${top}px`;
|
|
137
|
+
container.style.left = `${left}px`;
|
|
138
|
+
}, [isOpen, rect]);
|
|
139
|
+
const handleKeyDown = (0, import_react3.useCallback)(
|
|
140
|
+
(e) => {
|
|
141
|
+
if (!isOpen || !contentRef.current) return;
|
|
142
|
+
const items = contentRef.current.querySelectorAll("[role='option']");
|
|
143
|
+
if (items.length === 0) return;
|
|
144
|
+
if (e.key === "ArrowDown") {
|
|
145
|
+
e.preventDefault();
|
|
146
|
+
setActiveIndex((prev) => {
|
|
147
|
+
const next = Math.min(prev + 1, items.length - 1);
|
|
148
|
+
items[next]?.scrollIntoView({ block: "nearest" });
|
|
149
|
+
return next;
|
|
150
|
+
});
|
|
151
|
+
} else if (e.key === "ArrowUp") {
|
|
152
|
+
e.preventDefault();
|
|
153
|
+
setActiveIndex((prev) => {
|
|
154
|
+
const next = Math.max(prev - 1, 0);
|
|
155
|
+
items[next]?.scrollIntoView({ block: "nearest" });
|
|
156
|
+
return next;
|
|
157
|
+
});
|
|
158
|
+
} else if (e.key === "Enter") {
|
|
159
|
+
e.preventDefault();
|
|
160
|
+
const activeItem = items[activeIndex];
|
|
161
|
+
activeItem?.click();
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
[isOpen, activeIndex]
|
|
165
|
+
);
|
|
166
|
+
(0, import_react3.useEffect)(() => {
|
|
167
|
+
if (!isOpen) return;
|
|
168
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
169
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
170
|
+
}, [isOpen, handleKeyDown]);
|
|
171
|
+
(0, import_react3.useEffect)(() => {
|
|
172
|
+
if (!contentRef.current) return;
|
|
173
|
+
const items = contentRef.current.querySelectorAll("[role='option']");
|
|
174
|
+
items.forEach((item, i) => {
|
|
175
|
+
if (i === activeIndex) {
|
|
176
|
+
item.setAttribute("aria-selected", "true");
|
|
177
|
+
} else {
|
|
178
|
+
item.removeAttribute("aria-selected");
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
if (!isOpen || !containerRef.current) return null;
|
|
183
|
+
return (0, import_react_dom.createPortal)(
|
|
184
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
185
|
+
"div",
|
|
186
|
+
{
|
|
187
|
+
ref: (el) => {
|
|
188
|
+
contentRef.current = el;
|
|
189
|
+
if (typeof ref === "function") ref(el);
|
|
190
|
+
else if (ref) ref.current = el;
|
|
191
|
+
},
|
|
192
|
+
id: "slash-command",
|
|
193
|
+
className,
|
|
194
|
+
...rest,
|
|
195
|
+
children
|
|
196
|
+
}
|
|
197
|
+
),
|
|
198
|
+
containerRef.current
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
var EditorCommandList = (0, import_react3.forwardRef)(
|
|
203
|
+
({ children, ...rest }, ref) => {
|
|
204
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref, role: "listbox", ...rest, children });
|
|
146
205
|
}
|
|
147
206
|
);
|
|
148
|
-
var EditorCommandList = import_cmdk.Command.List;
|
|
149
207
|
EditorCommand.displayName = "EditorCommand";
|
|
208
|
+
EditorCommandList.displayName = "EditorCommandList";
|
|
150
209
|
|
|
151
210
|
// src/headless/components/editor-command-item.tsx
|
|
152
211
|
var import_react4 = require("react");
|
|
153
|
-
var import_cmdk2 = require("cmdk");
|
|
154
212
|
var import_react5 = require("@tiptap/react");
|
|
155
213
|
var import_jotai5 = require("jotai");
|
|
156
214
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
157
|
-
var
|
|
158
|
-
var CommandEmptyAny = import_cmdk2.CommandEmpty;
|
|
159
|
-
var EditorCommandItem = (0, import_react4.forwardRef)(({ children, onCommand, ...rest }, ref) => {
|
|
215
|
+
var EditorCommandItem = (0, import_react4.forwardRef)(({ children, onCommand, value, className, ...rest }, ref) => {
|
|
160
216
|
const { editor } = (0, import_react5.useCurrentEditor)();
|
|
161
|
-
const range = (0, import_jotai5.useAtomValue)(rangeAtom);
|
|
217
|
+
const range = (0, import_jotai5.useAtomValue)(rangeAtom, { store: novelStore });
|
|
218
|
+
const query = (0, import_jotai5.useAtomValue)(queryAtom, { store: novelStore });
|
|
162
219
|
if (!editor || !range) return null;
|
|
220
|
+
if (query && value) {
|
|
221
|
+
const searchText = value.toLowerCase();
|
|
222
|
+
const q = query.toLowerCase();
|
|
223
|
+
if (!searchText.includes(q)) return null;
|
|
224
|
+
}
|
|
163
225
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
164
|
-
|
|
226
|
+
"div",
|
|
165
227
|
{
|
|
166
228
|
ref,
|
|
229
|
+
role: "option",
|
|
230
|
+
className,
|
|
231
|
+
onClick: () => onCommand({ editor, range }),
|
|
167
232
|
...rest,
|
|
168
|
-
onSelect: () => onCommand({ editor, range }),
|
|
169
233
|
children
|
|
170
234
|
}
|
|
171
235
|
);
|
|
172
236
|
});
|
|
173
237
|
EditorCommandItem.displayName = "EditorCommandItem";
|
|
174
|
-
var EditorCommandEmpty =
|
|
238
|
+
var EditorCommandEmpty = (0, import_react4.forwardRef)(
|
|
239
|
+
({ children, ...rest }, ref) => {
|
|
240
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref, ...rest, children });
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
EditorCommandEmpty.displayName = "EditorCommandEmpty";
|
|
175
244
|
|
|
176
245
|
// src/headless/extensions/index.ts
|
|
177
246
|
var import_starter_kit = require("@tiptap/starter-kit");
|
|
@@ -919,11 +988,218 @@ var renderMentionSuggestion = () => {
|
|
|
919
988
|
};
|
|
920
989
|
};
|
|
921
990
|
|
|
991
|
+
// src/headless/extensions/DragHandle/DragHandle.ts
|
|
992
|
+
var import_extension_drag_handle = __toESM(require("@tiptap/extension-drag-handle"), 1);
|
|
993
|
+
var currentCallbacks = {};
|
|
994
|
+
var currentNode = null;
|
|
995
|
+
var currentEditor = null;
|
|
996
|
+
function setDragHandleCallbacks(callbacks) {
|
|
997
|
+
currentCallbacks = callbacks;
|
|
998
|
+
}
|
|
999
|
+
function createDragHandleElement() {
|
|
1000
|
+
const container = document.createElement("div");
|
|
1001
|
+
container.className = "nph-drag-handle";
|
|
1002
|
+
const plusBtn = document.createElement("button");
|
|
1003
|
+
plusBtn.className = "nph-drag-handle__btn";
|
|
1004
|
+
plusBtn.type = "button";
|
|
1005
|
+
plusBtn.setAttribute("aria-label", "Add block");
|
|
1006
|
+
plusBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14"/><path d="M5 12h14"/></svg>`;
|
|
1007
|
+
plusBtn.addEventListener("click", (e) => {
|
|
1008
|
+
e.preventDefault();
|
|
1009
|
+
e.stopPropagation();
|
|
1010
|
+
if (currentEditor) {
|
|
1011
|
+
currentCallbacks.onAddBlock?.(currentEditor, currentNode);
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
const gripBtn = document.createElement("button");
|
|
1015
|
+
gripBtn.className = "nph-drag-handle__btn nph-drag-handle__grip";
|
|
1016
|
+
gripBtn.type = "button";
|
|
1017
|
+
gripBtn.setAttribute("aria-label", "Drag to reorder");
|
|
1018
|
+
gripBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="5" r="1"/><circle cx="9" cy="12" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="15" cy="19" r="1"/></svg>`;
|
|
1019
|
+
gripBtn.addEventListener("click", (e) => {
|
|
1020
|
+
e.preventDefault();
|
|
1021
|
+
e.stopPropagation();
|
|
1022
|
+
if (currentEditor) {
|
|
1023
|
+
currentCallbacks.onGripClick?.(currentEditor, currentNode, container);
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
container.appendChild(plusBtn);
|
|
1027
|
+
container.appendChild(gripBtn);
|
|
1028
|
+
return container;
|
|
1029
|
+
}
|
|
1030
|
+
var DragHandle = import_extension_drag_handle.default.configure({
|
|
1031
|
+
render: createDragHandleElement,
|
|
1032
|
+
nested: true,
|
|
1033
|
+
onNodeChange: ({ node, editor }) => {
|
|
1034
|
+
currentNode = node;
|
|
1035
|
+
currentEditor = editor;
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
// src/headless/extensions/Table/index.ts
|
|
1040
|
+
var import_extension_table = require("@tiptap/extension-table");
|
|
1041
|
+
var import_extension_table2 = require("@tiptap/extension-table");
|
|
1042
|
+
|
|
1043
|
+
// src/headless/extensions/MarkdownPaste.ts
|
|
1044
|
+
var import_core3 = require("@tiptap/core");
|
|
1045
|
+
var import_state3 = require("@tiptap/pm/state");
|
|
1046
|
+
var import_markdown = require("@tiptap/pm/markdown");
|
|
1047
|
+
var markdownPastePluginKey = new import_state3.PluginKey("markdownPaste");
|
|
1048
|
+
function looksLikeMarkdown(text) {
|
|
1049
|
+
const patterns = [
|
|
1050
|
+
/^#{1,6}\s/m,
|
|
1051
|
+
// headings
|
|
1052
|
+
/^\s*[-*+]\s/m,
|
|
1053
|
+
// unordered list
|
|
1054
|
+
/^\s*\d+\.\s/m,
|
|
1055
|
+
// ordered list
|
|
1056
|
+
/^\s*>\s/m,
|
|
1057
|
+
// blockquote
|
|
1058
|
+
/\|.+\|/m,
|
|
1059
|
+
// table
|
|
1060
|
+
/^```/m,
|
|
1061
|
+
// fenced code block
|
|
1062
|
+
/\*\*.+\*\*/,
|
|
1063
|
+
// bold
|
|
1064
|
+
/\*.+\*/,
|
|
1065
|
+
// italic
|
|
1066
|
+
/~~.+~~/,
|
|
1067
|
+
// strikethrough
|
|
1068
|
+
/`[^`]+`/,
|
|
1069
|
+
// inline code
|
|
1070
|
+
/^\s*---\s*$/m,
|
|
1071
|
+
// horizontal rule
|
|
1072
|
+
/^\s*\*\*\*\s*$/m,
|
|
1073
|
+
// horizontal rule alt
|
|
1074
|
+
/\[.+\]\(.+\)/,
|
|
1075
|
+
// links
|
|
1076
|
+
/!\[.*\]\(.+\)/
|
|
1077
|
+
// images
|
|
1078
|
+
];
|
|
1079
|
+
const hasMarkdown = patterns.some((p) => p.test(text));
|
|
1080
|
+
const isHtml = /^<[a-z][\s\S]*>/i.test(text.trim());
|
|
1081
|
+
return hasMarkdown && !isHtml;
|
|
1082
|
+
}
|
|
1083
|
+
function buildParser(schema) {
|
|
1084
|
+
const md = import_markdown.defaultMarkdownParser.tokenizer;
|
|
1085
|
+
const tokens = {};
|
|
1086
|
+
if (schema.nodes.paragraph) tokens.paragraph = { block: "paragraph" };
|
|
1087
|
+
if (schema.nodes.heading) {
|
|
1088
|
+
tokens.heading = {
|
|
1089
|
+
block: "heading",
|
|
1090
|
+
getAttrs: (tok) => ({ level: Number(tok.tag.slice(1)) })
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
if (schema.nodes.blockquote) tokens.blockquote = { block: "blockquote" };
|
|
1094
|
+
if (schema.nodes.bulletList) tokens.bullet_list = { block: "bulletList" };
|
|
1095
|
+
if (schema.nodes.orderedList) {
|
|
1096
|
+
tokens.ordered_list = {
|
|
1097
|
+
block: "orderedList",
|
|
1098
|
+
getAttrs: (tok) => ({ start: Number(tok.attrGet("start") || 1) })
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
if (schema.nodes.listItem) tokens.list_item = { block: "listItem" };
|
|
1102
|
+
if (schema.nodes.codeBlock) {
|
|
1103
|
+
tokens.code_block = { block: "codeBlock", noCloseToken: true };
|
|
1104
|
+
tokens.fence = {
|
|
1105
|
+
block: "codeBlock",
|
|
1106
|
+
getAttrs: (tok) => ({ language: tok.info || "" }),
|
|
1107
|
+
noCloseToken: true
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
if (schema.nodes.horizontalRule) {
|
|
1111
|
+
tokens.hr = { node: "horizontalRule" };
|
|
1112
|
+
}
|
|
1113
|
+
if (schema.nodes.hardBreak) {
|
|
1114
|
+
tokens.hardbreak = { node: "hardBreak" };
|
|
1115
|
+
}
|
|
1116
|
+
if (schema.nodes.image) {
|
|
1117
|
+
tokens.image = {
|
|
1118
|
+
node: "image",
|
|
1119
|
+
getAttrs: (tok) => ({
|
|
1120
|
+
src: tok.attrGet("src"),
|
|
1121
|
+
title: tok.attrGet("title") || null,
|
|
1122
|
+
alt: tok.children?.[0]?.content || null
|
|
1123
|
+
})
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
if (schema.nodes.table) {
|
|
1127
|
+
tokens.table = { block: "table" };
|
|
1128
|
+
tokens.thead = { ignore: true };
|
|
1129
|
+
tokens.tbody = { ignore: true };
|
|
1130
|
+
tokens.tr = { block: "tableRow" };
|
|
1131
|
+
tokens.th = { block: "tableHeader" };
|
|
1132
|
+
tokens.td = { block: "tableCell" };
|
|
1133
|
+
}
|
|
1134
|
+
if (schema.marks.bold || schema.marks.strong) {
|
|
1135
|
+
tokens.strong = { mark: schema.marks.bold ? "bold" : "strong" };
|
|
1136
|
+
}
|
|
1137
|
+
if (schema.marks.italic || schema.marks.em) {
|
|
1138
|
+
tokens.em = { mark: schema.marks.italic ? "italic" : "em" };
|
|
1139
|
+
}
|
|
1140
|
+
if (schema.marks.code) {
|
|
1141
|
+
tokens.code_inline = { mark: "code", noCloseToken: true };
|
|
1142
|
+
}
|
|
1143
|
+
if (schema.marks.link) {
|
|
1144
|
+
tokens.link = {
|
|
1145
|
+
mark: "link",
|
|
1146
|
+
getAttrs: (tok) => ({
|
|
1147
|
+
href: tok.attrGet("href"),
|
|
1148
|
+
title: tok.attrGet("title") || null
|
|
1149
|
+
})
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
if (schema.marks.strike || schema.marks.strikethrough) {
|
|
1153
|
+
tokens.s = { mark: schema.marks.strike ? "strike" : "strikethrough" };
|
|
1154
|
+
}
|
|
1155
|
+
try {
|
|
1156
|
+
return new import_markdown.MarkdownParser(schema, md, tokens);
|
|
1157
|
+
} catch {
|
|
1158
|
+
return null;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
var MarkdownPaste = import_core3.Extension.create({
|
|
1162
|
+
name: "markdownPaste",
|
|
1163
|
+
addProseMirrorPlugins() {
|
|
1164
|
+
const schema = this.editor.schema;
|
|
1165
|
+
let parser = null;
|
|
1166
|
+
return [
|
|
1167
|
+
new import_state3.Plugin({
|
|
1168
|
+
key: markdownPastePluginKey,
|
|
1169
|
+
props: {
|
|
1170
|
+
handlePaste(view, event) {
|
|
1171
|
+
const clipboardData = event.clipboardData;
|
|
1172
|
+
if (!clipboardData) return false;
|
|
1173
|
+
const html = clipboardData.getData("text/html");
|
|
1174
|
+
if (html && html.trim().length > 0) return false;
|
|
1175
|
+
const text = clipboardData.getData("text/plain");
|
|
1176
|
+
if (!text || !looksLikeMarkdown(text)) return false;
|
|
1177
|
+
if (!parser) {
|
|
1178
|
+
parser = buildParser(schema);
|
|
1179
|
+
}
|
|
1180
|
+
if (!parser) return false;
|
|
1181
|
+
try {
|
|
1182
|
+
const doc = parser.parse(text);
|
|
1183
|
+
if (!doc || doc.content.size === 0) return false;
|
|
1184
|
+
const { tr } = view.state;
|
|
1185
|
+
const slice = doc.slice(0, doc.content.size);
|
|
1186
|
+
tr.replaceSelection(slice);
|
|
1187
|
+
view.dispatch(tr);
|
|
1188
|
+
return true;
|
|
1189
|
+
} catch {
|
|
1190
|
+
return false;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
})
|
|
1195
|
+
];
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
|
|
922
1199
|
// src/headless/extensions/slash-command.tsx
|
|
923
|
-
var import_react10 = require("@tiptap/react");
|
|
924
1200
|
var import_suggestion = __toESM(require("@tiptap/suggestion"), 1);
|
|
925
|
-
var
|
|
926
|
-
var
|
|
1201
|
+
var import_core4 = require("@tiptap/core");
|
|
1202
|
+
var Command = import_core4.Extension.create({
|
|
927
1203
|
name: "slash-command",
|
|
928
1204
|
addOptions() {
|
|
929
1205
|
return {
|
|
@@ -941,7 +1217,6 @@ var Command2 = import_core3.Extension.create({
|
|
|
941
1217
|
(0, import_suggestion.default)({
|
|
942
1218
|
editor: this.editor,
|
|
943
1219
|
char: base.char ?? "/",
|
|
944
|
-
// Only trigger slash command at start of line or after whitespace
|
|
945
1220
|
startOfLine: base.startOfLine ?? true,
|
|
946
1221
|
items: base.items ?? (() => ["/"]),
|
|
947
1222
|
command: (ctx) => {
|
|
@@ -949,76 +1224,63 @@ var Command2 = import_core3.Extension.create({
|
|
|
949
1224
|
ctx.props.command({ editor: ctx.editor, range: ctx.range });
|
|
950
1225
|
}
|
|
951
1226
|
},
|
|
952
|
-
...base
|
|
1227
|
+
...base,
|
|
1228
|
+
render: () => {
|
|
1229
|
+
return {
|
|
1230
|
+
onStart: (props) => {
|
|
1231
|
+
const { selection } = props.editor.state;
|
|
1232
|
+
const parentNode = selection.$from.node(selection.$from.depth);
|
|
1233
|
+
const blockType = parentNode.type.name;
|
|
1234
|
+
if (blockType === "codeBlock") return false;
|
|
1235
|
+
const { $from } = selection;
|
|
1236
|
+
const marks = $from.marks();
|
|
1237
|
+
if (marks.some((mark) => mark.type.name === "code" || mark.type.name === "link")) {
|
|
1238
|
+
return false;
|
|
1239
|
+
}
|
|
1240
|
+
novelStore.set(queryAtom, props.query ?? "");
|
|
1241
|
+
novelStore.set(rangeAtom, props.range ?? null);
|
|
1242
|
+
novelStore.set(slashMenuOpenAtom, true);
|
|
1243
|
+
const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
|
|
1244
|
+
novelStore.set(slashMenuRectAtom, rect);
|
|
1245
|
+
},
|
|
1246
|
+
onUpdate: (props) => {
|
|
1247
|
+
novelStore.set(queryAtom, props.query ?? "");
|
|
1248
|
+
novelStore.set(rangeAtom, props.range ?? null);
|
|
1249
|
+
const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
|
|
1250
|
+
novelStore.set(slashMenuRectAtom, rect);
|
|
1251
|
+
},
|
|
1252
|
+
onKeyDown: ({ event }) => {
|
|
1253
|
+
if (event.key === "Escape") {
|
|
1254
|
+
novelStore.set(slashMenuOpenAtom, false);
|
|
1255
|
+
return true;
|
|
1256
|
+
}
|
|
1257
|
+
if (["ArrowUp", "ArrowDown", "Enter"].includes(event.key)) {
|
|
1258
|
+
const slashCommand = document.querySelector("#slash-command");
|
|
1259
|
+
if (slashCommand) {
|
|
1260
|
+
slashCommand.dispatchEvent(
|
|
1261
|
+
new KeyboardEvent("keydown", {
|
|
1262
|
+
key: event.key,
|
|
1263
|
+
cancelable: true,
|
|
1264
|
+
bubbles: true
|
|
1265
|
+
})
|
|
1266
|
+
);
|
|
1267
|
+
return true;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
return false;
|
|
1271
|
+
},
|
|
1272
|
+
onExit: () => {
|
|
1273
|
+
novelStore.set(slashMenuOpenAtom, false);
|
|
1274
|
+
novelStore.set(queryAtom, "");
|
|
1275
|
+
novelStore.set(rangeAtom, null);
|
|
1276
|
+
novelStore.set(slashMenuRectAtom, null);
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
953
1280
|
})
|
|
954
1281
|
];
|
|
955
1282
|
}
|
|
956
1283
|
});
|
|
957
|
-
var renderItems = (elementRef) => {
|
|
958
|
-
let component = null;
|
|
959
|
-
let container = null;
|
|
960
|
-
const destroy = () => {
|
|
961
|
-
component?.destroy();
|
|
962
|
-
component = null;
|
|
963
|
-
if (container) {
|
|
964
|
-
container.remove();
|
|
965
|
-
container = null;
|
|
966
|
-
}
|
|
967
|
-
};
|
|
968
|
-
const updatePosition = (clientRect) => {
|
|
969
|
-
if (!container || !clientRect) return;
|
|
970
|
-
const top = Math.round(clientRect.bottom + 8);
|
|
971
|
-
const left = Math.round(clientRect.left);
|
|
972
|
-
container.style.top = `${top}px`;
|
|
973
|
-
container.style.left = `${left}px`;
|
|
974
|
-
};
|
|
975
|
-
return {
|
|
976
|
-
onStart: (props) => {
|
|
977
|
-
const { selection } = props.editor.state;
|
|
978
|
-
const parentNode = selection.$from.node(selection.$from.depth);
|
|
979
|
-
const blockType = parentNode.type.name;
|
|
980
|
-
if (blockType === "codeBlock") return false;
|
|
981
|
-
const { $from } = selection;
|
|
982
|
-
const marks = $from.marks();
|
|
983
|
-
if (marks.some((mark) => mark.type.name === "code" || mark.type.name === "link")) {
|
|
984
|
-
return false;
|
|
985
|
-
}
|
|
986
|
-
component = new import_react10.ReactRenderer(EditorCommandOut, {
|
|
987
|
-
props: {
|
|
988
|
-
query: props.query ?? "",
|
|
989
|
-
range: props.range
|
|
990
|
-
},
|
|
991
|
-
editor: props.editor
|
|
992
|
-
});
|
|
993
|
-
container = document.createElement("div");
|
|
994
|
-
container.style.position = "fixed";
|
|
995
|
-
container.style.zIndex = "9999";
|
|
996
|
-
container.style.minWidth = "240px";
|
|
997
|
-
(elementRef?.current ?? document.body).appendChild(container);
|
|
998
|
-
container.appendChild(component.element);
|
|
999
|
-
const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
|
|
1000
|
-
if (rect) updatePosition(rect);
|
|
1001
|
-
},
|
|
1002
|
-
onUpdate: (props) => {
|
|
1003
|
-
component?.updateProps({
|
|
1004
|
-
query: props.query ?? "",
|
|
1005
|
-
range: props.range
|
|
1006
|
-
});
|
|
1007
|
-
const rect = typeof props.clientRect === "function" ? props.clientRect() : null;
|
|
1008
|
-
if (rect) updatePosition(rect);
|
|
1009
|
-
},
|
|
1010
|
-
onKeyDown: ({ event }) => {
|
|
1011
|
-
if (event.key === "Escape") {
|
|
1012
|
-
destroy();
|
|
1013
|
-
return true;
|
|
1014
|
-
}
|
|
1015
|
-
return false;
|
|
1016
|
-
},
|
|
1017
|
-
onExit: () => {
|
|
1018
|
-
destroy();
|
|
1019
|
-
}
|
|
1020
|
-
};
|
|
1021
|
-
};
|
|
1022
1284
|
var handleCommandNavigation = (event) => {
|
|
1023
1285
|
if (["ArrowUp", "ArrowDown", "Enter"].includes(event.key)) {
|
|
1024
1286
|
const slashCommand = document.querySelector("#slash-command");
|
|
@@ -1028,11 +1290,13 @@ var handleCommandNavigation = (event) => {
|
|
|
1028
1290
|
|
|
1029
1291
|
// src/headless/extensions/extension-kit.ts
|
|
1030
1292
|
var import_extension_collaboration = __toESM(require("@tiptap/extension-collaboration"), 1);
|
|
1293
|
+
var import_extension_collaboration_caret = __toESM(require("@tiptap/extension-collaboration-caret"), 1);
|
|
1294
|
+
var import_extension_underline = __toESM(require("@tiptap/extension-underline"), 1);
|
|
1031
1295
|
|
|
1032
1296
|
// src/headless/extensions/VideoBlock/VideoBlock.ts
|
|
1033
|
-
var
|
|
1034
|
-
var
|
|
1035
|
-
var VideoBlock =
|
|
1297
|
+
var import_core5 = require("@tiptap/core");
|
|
1298
|
+
var import_react11 = require("@tiptap/react");
|
|
1299
|
+
var VideoBlock = import_core5.Node.create({
|
|
1036
1300
|
name: "videoBlock",
|
|
1037
1301
|
group: "block",
|
|
1038
1302
|
defining: true,
|
|
@@ -1078,7 +1342,7 @@ var VideoBlock = import_core4.Node.create({
|
|
|
1078
1342
|
renderHTML({ HTMLAttributes }) {
|
|
1079
1343
|
return [
|
|
1080
1344
|
"div",
|
|
1081
|
-
(0,
|
|
1345
|
+
(0, import_core5.mergeAttributes)(HTMLAttributes, { "data-type": "video-block" })
|
|
1082
1346
|
];
|
|
1083
1347
|
},
|
|
1084
1348
|
addCommands() {
|
|
@@ -1097,7 +1361,7 @@ var VideoBlock = import_core4.Node.create({
|
|
|
1097
1361
|
},
|
|
1098
1362
|
addNodeView() {
|
|
1099
1363
|
if (this.options.nodeView) {
|
|
1100
|
-
return (0,
|
|
1364
|
+
return (0, import_react11.ReactNodeViewRenderer)(this.options.nodeView);
|
|
1101
1365
|
}
|
|
1102
1366
|
return null;
|
|
1103
1367
|
}
|
|
@@ -1110,6 +1374,7 @@ var ExtensionKit = (options) => {
|
|
|
1110
1374
|
import_starter_kit.StarterKit.configure({ codeBlock: false, link: false }),
|
|
1111
1375
|
CodeBlock,
|
|
1112
1376
|
Link,
|
|
1377
|
+
import_extension_underline.default,
|
|
1113
1378
|
ImageBlock.configure({
|
|
1114
1379
|
uploadImage: options?.uploadImage,
|
|
1115
1380
|
nodeView: options?.imageBlockView
|
|
@@ -1128,14 +1393,29 @@ var ExtensionKit = (options) => {
|
|
|
1128
1393
|
return enableSlashCommand ? "Press '/' for commands" : "";
|
|
1129
1394
|
},
|
|
1130
1395
|
includeChildren: true
|
|
1131
|
-
})
|
|
1396
|
+
}),
|
|
1397
|
+
MarkdownPaste
|
|
1132
1398
|
];
|
|
1399
|
+
if (options?.table !== false) {
|
|
1400
|
+
extensions.push(
|
|
1401
|
+
import_extension_table.TableKit.configure({
|
|
1402
|
+
resizable: true,
|
|
1403
|
+
lastColumnResizable: true,
|
|
1404
|
+
allowTableNodeSelection: true
|
|
1405
|
+
})
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
if (options?.dragHandle !== false) {
|
|
1409
|
+
extensions.push(DragHandle);
|
|
1410
|
+
if (options?.dragHandleCallbacks) {
|
|
1411
|
+
setDragHandleCallbacks(options.dragHandleCallbacks);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1133
1414
|
if (enableSlashCommand) {
|
|
1134
1415
|
extensions.push(
|
|
1135
|
-
|
|
1416
|
+
Command.configure({
|
|
1136
1417
|
suggestion: {
|
|
1137
1418
|
char: "/",
|
|
1138
|
-
render: renderItems,
|
|
1139
1419
|
allowSpaces: true,
|
|
1140
1420
|
allowedPrefixes: null
|
|
1141
1421
|
}
|
|
@@ -1161,19 +1441,44 @@ var ExtensionKit = (options) => {
|
|
|
1161
1441
|
field: options.collaboration.field
|
|
1162
1442
|
})
|
|
1163
1443
|
);
|
|
1444
|
+
if (options.collaboration.provider && options.collaboration.user) {
|
|
1445
|
+
extensions.push(
|
|
1446
|
+
import_extension_collaboration_caret.default.configure({
|
|
1447
|
+
provider: options.collaboration.provider,
|
|
1448
|
+
user: options.collaboration.user,
|
|
1449
|
+
render: (user) => {
|
|
1450
|
+
const cursor = document.createElement("span");
|
|
1451
|
+
cursor.classList.add("nph-collab-caret");
|
|
1452
|
+
cursor.setAttribute("style", `border-color: ${user.color || "#3b82f6"}`);
|
|
1453
|
+
const label = document.createElement("div");
|
|
1454
|
+
label.classList.add("nph-collab-caret__label");
|
|
1455
|
+
label.setAttribute("style", `background-color: ${user.color || "#3b82f6"}`);
|
|
1456
|
+
label.insertBefore(document.createTextNode(user.name || "Anonymous"), null);
|
|
1457
|
+
cursor.insertBefore(label, null);
|
|
1458
|
+
return cursor;
|
|
1459
|
+
},
|
|
1460
|
+
selectionRender: (user) => ({
|
|
1461
|
+
nodeName: "span",
|
|
1462
|
+
class: "nph-collab-selection",
|
|
1463
|
+
style: `background-color: ${user.color || "#3b82f6"}20`
|
|
1464
|
+
})
|
|
1465
|
+
})
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1164
1468
|
}
|
|
1165
1469
|
return extensions;
|
|
1166
1470
|
};
|
|
1167
1471
|
var extension_kit_default = ExtensionKit;
|
|
1168
1472
|
|
|
1169
1473
|
// src/react/menus/TextMenu.tsx
|
|
1170
|
-
var
|
|
1474
|
+
var import_state4 = require("@tiptap/pm/state");
|
|
1475
|
+
var import_react13 = require("@tiptap/react");
|
|
1171
1476
|
var import_menus = require("@tiptap/react/menus");
|
|
1172
1477
|
var import_icons_react2 = require("@tabler/icons-react");
|
|
1173
|
-
var
|
|
1478
|
+
var import_react14 = require("react");
|
|
1174
1479
|
|
|
1175
1480
|
// src/react/menus/MenuList.tsx
|
|
1176
|
-
var
|
|
1481
|
+
var import_react12 = require("react");
|
|
1177
1482
|
var import_icons_react = require("@tabler/icons-react");
|
|
1178
1483
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1179
1484
|
function MenuList({
|
|
@@ -1183,10 +1488,10 @@ function MenuList({
|
|
|
1183
1488
|
onSelect,
|
|
1184
1489
|
buttonClassName
|
|
1185
1490
|
}) {
|
|
1186
|
-
const [open, setOpen] = (0,
|
|
1187
|
-
const [label, setLabel] = (0,
|
|
1188
|
-
const rootRef = (0,
|
|
1189
|
-
const computeLabel = (0,
|
|
1491
|
+
const [open, setOpen] = (0, import_react12.useState)(false);
|
|
1492
|
+
const [label, setLabel] = (0, import_react12.useState)("Paragraph");
|
|
1493
|
+
const rootRef = (0, import_react12.useRef)(null);
|
|
1494
|
+
const computeLabel = (0, import_react12.useCallback)(() => {
|
|
1190
1495
|
if (!editor) return "Paragraph";
|
|
1191
1496
|
if (editor.isActive("heading", { level: 1 })) return "Heading 1";
|
|
1192
1497
|
if (editor.isActive("heading", { level: 2 })) return "Heading 2";
|
|
@@ -1199,7 +1504,7 @@ function MenuList({
|
|
|
1199
1504
|
if (editor.isActive("codeBlock")) return "Code Block";
|
|
1200
1505
|
return "Paragraph";
|
|
1201
1506
|
}, [editor]);
|
|
1202
|
-
(0,
|
|
1507
|
+
(0, import_react12.useEffect)(() => {
|
|
1203
1508
|
setLabel(computeLabel());
|
|
1204
1509
|
if (!editor) return;
|
|
1205
1510
|
const update = () => setLabel(computeLabel());
|
|
@@ -1212,7 +1517,7 @@ function MenuList({
|
|
|
1212
1517
|
editor.off("update", update);
|
|
1213
1518
|
};
|
|
1214
1519
|
}, [editor, computeLabel]);
|
|
1215
|
-
(0,
|
|
1520
|
+
(0, import_react12.useEffect)(() => {
|
|
1216
1521
|
if (!editor) return;
|
|
1217
1522
|
const close = () => setOpen(false);
|
|
1218
1523
|
editor.on("selectionUpdate", close);
|
|
@@ -1222,7 +1527,7 @@ function MenuList({
|
|
|
1222
1527
|
editor.off("blur", close);
|
|
1223
1528
|
};
|
|
1224
1529
|
}, [editor]);
|
|
1225
|
-
(0,
|
|
1530
|
+
(0, import_react12.useEffect)(() => {
|
|
1226
1531
|
const handlePointerDown = (e) => {
|
|
1227
1532
|
if (!open) return;
|
|
1228
1533
|
const el = rootRef.current;
|
|
@@ -1237,7 +1542,7 @@ function MenuList({
|
|
|
1237
1542
|
onSelect?.();
|
|
1238
1543
|
setOpen(false);
|
|
1239
1544
|
};
|
|
1240
|
-
const isActive = (0,
|
|
1545
|
+
const isActive = (0, import_react12.useCallback)((name) => label === name, [label]);
|
|
1241
1546
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: rootRef, className: "nph-dropdown", children: [
|
|
1242
1547
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1243
1548
|
"button",
|
|
@@ -1425,22 +1730,26 @@ function MenuList({
|
|
|
1425
1730
|
|
|
1426
1731
|
// src/react/menus/TextMenu.tsx
|
|
1427
1732
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1733
|
+
function Separator() {
|
|
1734
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "nph-bubble-separator" });
|
|
1735
|
+
}
|
|
1428
1736
|
function TextMenu({
|
|
1429
1737
|
className,
|
|
1430
1738
|
leadingExtras,
|
|
1431
1739
|
trailingExtras
|
|
1432
1740
|
}) {
|
|
1433
|
-
const { editor } = (0,
|
|
1434
|
-
const [isAddingLink, setIsAddingLink] = (0,
|
|
1435
|
-
const [linkUrl, setLinkUrl] = (0,
|
|
1436
|
-
const linkInputRef = (0,
|
|
1437
|
-
const editorState = (0,
|
|
1741
|
+
const { editor } = (0, import_react13.useCurrentEditor)();
|
|
1742
|
+
const [isAddingLink, setIsAddingLink] = (0, import_react14.useState)(false);
|
|
1743
|
+
const [linkUrl, setLinkUrl] = (0, import_react14.useState)("");
|
|
1744
|
+
const linkInputRef = (0, import_react14.useRef)(null);
|
|
1745
|
+
const editorState = (0, import_react13.useEditorState)({
|
|
1438
1746
|
editor,
|
|
1439
1747
|
selector: (ctx) => {
|
|
1440
1748
|
if (!ctx.editor) return null;
|
|
1441
1749
|
return {
|
|
1442
1750
|
isBold: ctx.editor.isActive("bold"),
|
|
1443
1751
|
isItalic: ctx.editor.isActive("italic"),
|
|
1752
|
+
isUnderline: ctx.editor.isActive("underline"),
|
|
1444
1753
|
isStrike: ctx.editor.isActive("strike"),
|
|
1445
1754
|
isCode: ctx.editor.isActive("code"),
|
|
1446
1755
|
isCodeBlock: ctx.editor.isActive("codeBlock"),
|
|
@@ -1451,7 +1760,7 @@ function TextMenu({
|
|
|
1451
1760
|
};
|
|
1452
1761
|
}
|
|
1453
1762
|
});
|
|
1454
|
-
(0,
|
|
1763
|
+
(0, import_react14.useEffect)(() => {
|
|
1455
1764
|
if (isAddingLink && linkInputRef.current) {
|
|
1456
1765
|
linkInputRef.current.focus();
|
|
1457
1766
|
}
|
|
@@ -1486,6 +1795,7 @@ function TextMenu({
|
|
|
1486
1795
|
}
|
|
1487
1796
|
return has;
|
|
1488
1797
|
};
|
|
1798
|
+
const isInCode = editorState.isCode || editorState.isCodeBlock;
|
|
1489
1799
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1490
1800
|
import_menus.BubbleMenu,
|
|
1491
1801
|
{
|
|
@@ -1494,7 +1804,7 @@ function TextMenu({
|
|
|
1494
1804
|
if (e.isActive("imageBlock")) return false;
|
|
1495
1805
|
if (e.isActive("videoBlock")) return false;
|
|
1496
1806
|
const { selection } = state;
|
|
1497
|
-
if (selection
|
|
1807
|
+
if (selection instanceof import_state4.NodeSelection) return false;
|
|
1498
1808
|
if (from === to) return false;
|
|
1499
1809
|
if (e.isActive("link")) return false;
|
|
1500
1810
|
let hasImage = false;
|
|
@@ -1508,8 +1818,9 @@ function TextMenu({
|
|
|
1508
1818
|
return true;
|
|
1509
1819
|
},
|
|
1510
1820
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: className ? `bubble-menu ${className}` : "bubble-menu", children: [
|
|
1511
|
-
leadingExtras && editor ? leadingExtras.map((renderExtra, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1821
|
+
leadingExtras && editor ? leadingExtras.map((renderExtra, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react14.Fragment, { children: renderExtra(editor) }, `leading-extra-${index}`)) : null,
|
|
1512
1822
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MenuList, { editor }),
|
|
1823
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Separator, {}),
|
|
1513
1824
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1514
1825
|
"button",
|
|
1515
1826
|
{
|
|
@@ -1517,8 +1828,8 @@ function TextMenu({
|
|
|
1517
1828
|
onMouseDown: (e) => e.preventDefault(),
|
|
1518
1829
|
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
1519
1830
|
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editorState.isBold ? " is-active" : ""}`,
|
|
1520
|
-
disabled:
|
|
1521
|
-
"aria-disabled":
|
|
1831
|
+
disabled: isInCode,
|
|
1832
|
+
"aria-disabled": isInCode,
|
|
1522
1833
|
"aria-pressed": editorState.isBold,
|
|
1523
1834
|
"aria-label": "Toggle bold",
|
|
1524
1835
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_icons_react2.IconBold, { size: 16 })
|
|
@@ -1531,13 +1842,27 @@ function TextMenu({
|
|
|
1531
1842
|
onMouseDown: (e) => e.preventDefault(),
|
|
1532
1843
|
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
1533
1844
|
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editorState.isItalic ? " is-active" : ""}`,
|
|
1534
|
-
disabled:
|
|
1535
|
-
"aria-disabled":
|
|
1845
|
+
disabled: isInCode,
|
|
1846
|
+
"aria-disabled": isInCode,
|
|
1536
1847
|
"aria-pressed": editorState.isItalic,
|
|
1537
1848
|
"aria-label": "Toggle italic",
|
|
1538
1849
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_icons_react2.IconItalic, { size: 16 })
|
|
1539
1850
|
}
|
|
1540
1851
|
),
|
|
1852
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1853
|
+
"button",
|
|
1854
|
+
{
|
|
1855
|
+
type: "button",
|
|
1856
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1857
|
+
onClick: () => editor.chain().focus().toggleUnderline().run(),
|
|
1858
|
+
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editorState.isUnderline ? " is-active" : ""}`,
|
|
1859
|
+
disabled: isInCode,
|
|
1860
|
+
"aria-disabled": isInCode,
|
|
1861
|
+
"aria-pressed": editorState.isUnderline,
|
|
1862
|
+
"aria-label": "Toggle underline",
|
|
1863
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_icons_react2.IconUnderline, { size: 16 })
|
|
1864
|
+
}
|
|
1865
|
+
),
|
|
1541
1866
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1542
1867
|
"button",
|
|
1543
1868
|
{
|
|
@@ -1545,13 +1870,28 @@ function TextMenu({
|
|
|
1545
1870
|
onMouseDown: (e) => e.preventDefault(),
|
|
1546
1871
|
onClick: () => editor.chain().focus().toggleStrike().run(),
|
|
1547
1872
|
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editorState.isStrike ? " is-active" : ""}`,
|
|
1548
|
-
disabled:
|
|
1549
|
-
"aria-disabled":
|
|
1873
|
+
disabled: isInCode,
|
|
1874
|
+
"aria-disabled": isInCode,
|
|
1550
1875
|
"aria-pressed": editorState.isStrike,
|
|
1551
1876
|
"aria-label": "Toggle strike",
|
|
1552
1877
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_icons_react2.IconStrikethrough, { size: 16 })
|
|
1553
1878
|
}
|
|
1554
1879
|
),
|
|
1880
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1881
|
+
"button",
|
|
1882
|
+
{
|
|
1883
|
+
type: "button",
|
|
1884
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1885
|
+
onClick: () => editor.chain().focus().toggleCode().run(),
|
|
1886
|
+
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editorState.isCode ? " is-active" : ""}`,
|
|
1887
|
+
disabled: editorState.isCodeBlock,
|
|
1888
|
+
"aria-disabled": editorState.isCodeBlock,
|
|
1889
|
+
"aria-pressed": editorState.isCode,
|
|
1890
|
+
"aria-label": "Toggle inline code",
|
|
1891
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_icons_react2.IconCode, { size: 16 })
|
|
1892
|
+
}
|
|
1893
|
+
),
|
|
1894
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Separator, {}),
|
|
1555
1895
|
!isAddingLink ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1556
1896
|
"button",
|
|
1557
1897
|
{
|
|
@@ -1559,8 +1899,8 @@ function TextMenu({
|
|
|
1559
1899
|
onMouseDown: (e) => e.preventDefault(),
|
|
1560
1900
|
onClick: () => setIsAddingLink(true),
|
|
1561
1901
|
className: `nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon${editor.isActive("link") ? " is-active" : ""}`,
|
|
1562
|
-
disabled:
|
|
1563
|
-
"aria-disabled":
|
|
1902
|
+
disabled: isInCode,
|
|
1903
|
+
"aria-disabled": isInCode,
|
|
1564
1904
|
"aria-pressed": editor.isActive("link"),
|
|
1565
1905
|
"aria-label": "Add link",
|
|
1566
1906
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_icons_react2.IconLink, { size: 16 })
|
|
@@ -1622,342 +1962,202 @@ function TextMenu({
|
|
|
1622
1962
|
(() => {
|
|
1623
1963
|
const hasInlineMarks = hasAnyMarksInSelection();
|
|
1624
1964
|
const isPlainParagraph = editorState.isParagraph && !editorState.isHeading && !editorState.isList && !editorState.isBlockquote && !editorState.isCodeBlock && !hasInlineMarks;
|
|
1625
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1965
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1966
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Separator, {}),
|
|
1967
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1968
|
+
"button",
|
|
1969
|
+
{
|
|
1970
|
+
type: "button",
|
|
1971
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1972
|
+
onClick: () => editor.chain().focus().clearNodes().setParagraph().unsetAllMarks().run(),
|
|
1973
|
+
className: "nph-btn nph-btn-ghost nph-btn-xs nph-btn-icon",
|
|
1974
|
+
"aria-label": "Revert to paragraph",
|
|
1975
|
+
title: "Revert to paragraph",
|
|
1976
|
+
disabled: isPlainParagraph,
|
|
1977
|
+
"aria-disabled": isPlainParagraph,
|
|
1978
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_icons_react2.IconArrowBackUp, { size: 16 })
|
|
1979
|
+
}
|
|
1980
|
+
)
|
|
1981
|
+
] });
|
|
1639
1982
|
})(),
|
|
1640
|
-
trailingExtras && editor ? trailingExtras.map((renderExtra, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1983
|
+
trailingExtras && editor ? trailingExtras.map((renderExtra, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react14.Fragment, { children: renderExtra(editor) }, `trailing-extra-${index}`)) : null
|
|
1641
1984
|
] })
|
|
1642
1985
|
}
|
|
1643
1986
|
);
|
|
1644
1987
|
}
|
|
1645
1988
|
|
|
1646
1989
|
// src/react/menus/SlashMenu.tsx
|
|
1647
|
-
var
|
|
1990
|
+
var import_react15 = require("@tiptap/react");
|
|
1991
|
+
var import_jotai6 = require("jotai");
|
|
1648
1992
|
var import_icons_react3 = require("@tabler/icons-react");
|
|
1993
|
+
var import_react16 = require("react");
|
|
1649
1994
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1995
|
+
var SLASH_COMMANDS = [
|
|
1996
|
+
{
|
|
1997
|
+
group: "Format",
|
|
1998
|
+
items: [
|
|
1999
|
+
{
|
|
2000
|
+
value: "paragraph text",
|
|
2001
|
+
label: "Paragraph",
|
|
2002
|
+
description: "Plain text block",
|
|
2003
|
+
icon: import_icons_react3.IconTypography,
|
|
2004
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).setParagraph().run()
|
|
2005
|
+
},
|
|
2006
|
+
{
|
|
2007
|
+
value: "heading1 h1",
|
|
2008
|
+
label: "Heading 1",
|
|
2009
|
+
description: "Large section heading",
|
|
2010
|
+
icon: import_icons_react3.IconH1,
|
|
2011
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleHeading({ level: 1 }).run()
|
|
2012
|
+
},
|
|
2013
|
+
{
|
|
2014
|
+
value: "heading2 h2",
|
|
2015
|
+
label: "Heading 2",
|
|
2016
|
+
description: "Medium section heading",
|
|
2017
|
+
icon: import_icons_react3.IconH2,
|
|
2018
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleHeading({ level: 2 }).run()
|
|
2019
|
+
},
|
|
2020
|
+
{
|
|
2021
|
+
value: "heading3 h3",
|
|
2022
|
+
label: "Heading 3",
|
|
2023
|
+
description: "Small section heading",
|
|
2024
|
+
icon: import_icons_react3.IconH3,
|
|
2025
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleHeading({ level: 3 }).run()
|
|
2026
|
+
},
|
|
2027
|
+
{
|
|
2028
|
+
value: "heading4 h4",
|
|
2029
|
+
label: "Heading 4",
|
|
2030
|
+
description: "Subsection heading",
|
|
2031
|
+
icon: import_icons_react3.IconH4,
|
|
2032
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleHeading({ level: 4 }).run()
|
|
2033
|
+
}
|
|
2034
|
+
]
|
|
2035
|
+
},
|
|
2036
|
+
{
|
|
2037
|
+
group: "Lists",
|
|
2038
|
+
items: [
|
|
2039
|
+
{
|
|
2040
|
+
value: "bullet list ul",
|
|
2041
|
+
label: "Bullet List",
|
|
2042
|
+
description: "Unordered list of items",
|
|
2043
|
+
icon: import_icons_react3.IconList,
|
|
2044
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleBulletList().run()
|
|
2045
|
+
},
|
|
2046
|
+
{
|
|
2047
|
+
value: "ordered list ol numbered",
|
|
2048
|
+
label: "Numbered List",
|
|
2049
|
+
description: "Ordered list of items",
|
|
2050
|
+
icon: import_icons_react3.IconListNumbers,
|
|
2051
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleOrderedList().run()
|
|
2052
|
+
},
|
|
2053
|
+
{
|
|
2054
|
+
value: "task list todo checklist",
|
|
2055
|
+
label: "Task List",
|
|
2056
|
+
description: "Checklist of to-do items",
|
|
2057
|
+
icon: import_icons_react3.IconListCheck,
|
|
2058
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleTaskList().run()
|
|
2059
|
+
},
|
|
2060
|
+
{
|
|
2061
|
+
value: "quote blockquote",
|
|
2062
|
+
label: "Blockquote",
|
|
2063
|
+
description: "Highlight a quote or excerpt",
|
|
2064
|
+
icon: import_icons_react3.IconBlockquote,
|
|
2065
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleBlockquote().run()
|
|
2066
|
+
}
|
|
2067
|
+
]
|
|
2068
|
+
},
|
|
2069
|
+
{
|
|
2070
|
+
group: "Insert",
|
|
2071
|
+
items: [
|
|
2072
|
+
{
|
|
2073
|
+
value: "image photo picture",
|
|
2074
|
+
label: "Image",
|
|
2075
|
+
description: "Upload or embed an image",
|
|
2076
|
+
icon: import_icons_react3.IconPhoto,
|
|
2077
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).setImageBlock({ src: "" }).run()
|
|
2078
|
+
},
|
|
2079
|
+
{
|
|
2080
|
+
value: "video embed youtube vimeo",
|
|
2081
|
+
label: "Video",
|
|
2082
|
+
description: "Embed a YouTube or Vimeo video",
|
|
2083
|
+
icon: import_icons_react3.IconVideo,
|
|
2084
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).setVideoBlock({ src: "" }).run()
|
|
2085
|
+
},
|
|
2086
|
+
{
|
|
2087
|
+
value: "table grid",
|
|
2088
|
+
label: "Table",
|
|
2089
|
+
description: "Insert a table with rows and columns",
|
|
2090
|
+
icon: import_icons_react3.IconTable,
|
|
2091
|
+
onCommand: ({ editor, range }) => {
|
|
2092
|
+
editor.chain().focus().deleteRange(range).run();
|
|
2093
|
+
if (editor.commands.insertTable) {
|
|
2094
|
+
editor.commands.insertTable({ rows: 3, cols: 3, withHeaderRow: true });
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
},
|
|
2098
|
+
{
|
|
2099
|
+
value: "code block codeblock",
|
|
2100
|
+
label: "Code Block",
|
|
2101
|
+
description: "Syntax-highlighted code block",
|
|
2102
|
+
icon: import_icons_react3.IconSourceCode,
|
|
2103
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleCodeBlock().run()
|
|
2104
|
+
},
|
|
2105
|
+
{
|
|
2106
|
+
value: "code inline",
|
|
2107
|
+
label: "Inline Code",
|
|
2108
|
+
description: "Mark text as inline code",
|
|
2109
|
+
icon: import_icons_react3.IconCode,
|
|
2110
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleCode().run()
|
|
2111
|
+
},
|
|
2112
|
+
{
|
|
2113
|
+
value: "divider horizontal rule separator",
|
|
2114
|
+
label: "Divider",
|
|
2115
|
+
description: "Horizontal separator line",
|
|
2116
|
+
icon: import_icons_react3.IconMinus,
|
|
2117
|
+
onCommand: ({ editor, range }) => editor.chain().focus().deleteRange(range).setHorizontalRule().run()
|
|
2118
|
+
}
|
|
2119
|
+
]
|
|
2120
|
+
}
|
|
2121
|
+
];
|
|
1650
2122
|
function SlashMenu({ className }) {
|
|
1651
|
-
const { editor } = (0,
|
|
2123
|
+
const { editor } = (0, import_react15.useCurrentEditor)();
|
|
2124
|
+
const query = (0, import_jotai6.useAtomValue)(queryAtom, { store: novelStore });
|
|
2125
|
+
const filteredGroups = (0, import_react16.useMemo)(() => {
|
|
2126
|
+
if (!query) return SLASH_COMMANDS;
|
|
2127
|
+
const q = query.toLowerCase();
|
|
2128
|
+
return SLASH_COMMANDS.map((group) => ({
|
|
2129
|
+
...group,
|
|
2130
|
+
items: group.items.filter((item) => item.value.toLowerCase().includes(q))
|
|
2131
|
+
})).filter((group) => group.items.length > 0);
|
|
2132
|
+
}, [query]);
|
|
1652
2133
|
if (!editor) return null;
|
|
1653
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EditorCommand, { className: className ?? "nph-command", children: /* @__PURE__ */ (0, import_jsx_runtime8.
|
|
2134
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EditorCommand, { className: className ?? "nph-command", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1654
2135
|
EditorCommandList,
|
|
1655
2136
|
{
|
|
1656
2137
|
className: "nph-command__list",
|
|
1657
2138
|
style: { display: "flex", flexDirection: "column", gap: 2 },
|
|
1658
|
-
children: [
|
|
1659
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Bold" })
|
|
1676
|
-
]
|
|
1677
|
-
}
|
|
1678
|
-
)
|
|
1679
|
-
}
|
|
1680
|
-
),
|
|
1681
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1682
|
-
EditorCommandItem,
|
|
1683
|
-
{
|
|
1684
|
-
value: "italic",
|
|
1685
|
-
className: "nph-command__item",
|
|
1686
|
-
onCommand: ({
|
|
1687
|
-
editor: editor2,
|
|
1688
|
-
range
|
|
1689
|
-
}) => editor2.chain().focus().deleteRange(range).toggleItalic().run(),
|
|
1690
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1691
|
-
"span",
|
|
1692
|
-
{
|
|
1693
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1694
|
-
children: [
|
|
1695
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconItalic, { size: 16 }),
|
|
1696
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Italic" })
|
|
1697
|
-
]
|
|
1698
|
-
}
|
|
1699
|
-
)
|
|
1700
|
-
}
|
|
1701
|
-
),
|
|
1702
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1703
|
-
EditorCommandItem,
|
|
1704
|
-
{
|
|
1705
|
-
value: "strike",
|
|
1706
|
-
className: "nph-command__item",
|
|
1707
|
-
onCommand: ({
|
|
1708
|
-
editor: editor2,
|
|
1709
|
-
range
|
|
1710
|
-
}) => editor2.chain().focus().deleteRange(range).toggleStrike().run(),
|
|
1711
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1712
|
-
"span",
|
|
1713
|
-
{
|
|
1714
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1715
|
-
children: [
|
|
1716
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconStrikethrough, { size: 16 }),
|
|
1717
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Strike" })
|
|
1718
|
-
]
|
|
1719
|
-
}
|
|
1720
|
-
)
|
|
1721
|
-
}
|
|
1722
|
-
),
|
|
1723
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1724
|
-
EditorCommandItem,
|
|
1725
|
-
{
|
|
1726
|
-
value: "heading1 h1",
|
|
1727
|
-
className: "nph-command__item",
|
|
1728
|
-
onCommand: ({
|
|
1729
|
-
editor: editor2,
|
|
1730
|
-
range
|
|
1731
|
-
}) => editor2.chain().focus().deleteRange(range).toggleHeading({ level: 1 }).run(),
|
|
1732
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1733
|
-
"span",
|
|
1734
|
-
{
|
|
1735
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1736
|
-
children: [
|
|
1737
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconH1, { size: 16 }),
|
|
1738
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Heading 1" })
|
|
1739
|
-
]
|
|
1740
|
-
}
|
|
1741
|
-
)
|
|
1742
|
-
}
|
|
1743
|
-
),
|
|
1744
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1745
|
-
EditorCommandItem,
|
|
1746
|
-
{
|
|
1747
|
-
value: "heading2 h2",
|
|
1748
|
-
className: "nph-command__item",
|
|
1749
|
-
onCommand: ({
|
|
1750
|
-
editor: editor2,
|
|
1751
|
-
range
|
|
1752
|
-
}) => editor2.chain().focus().deleteRange(range).toggleHeading({ level: 2 }).run(),
|
|
1753
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1754
|
-
"span",
|
|
1755
|
-
{
|
|
1756
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1757
|
-
children: [
|
|
1758
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconH2, { size: 16 }),
|
|
1759
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Heading 2" })
|
|
1760
|
-
]
|
|
1761
|
-
}
|
|
1762
|
-
)
|
|
1763
|
-
}
|
|
1764
|
-
),
|
|
1765
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1766
|
-
EditorCommandItem,
|
|
1767
|
-
{
|
|
1768
|
-
value: "heading3 h3",
|
|
1769
|
-
className: "nph-command__item",
|
|
1770
|
-
onCommand: ({
|
|
1771
|
-
editor: editor2,
|
|
1772
|
-
range
|
|
1773
|
-
}) => editor2.chain().focus().deleteRange(range).toggleHeading({ level: 3 }).run(),
|
|
1774
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1775
|
-
"span",
|
|
1776
|
-
{
|
|
1777
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1778
|
-
children: [
|
|
1779
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconH3, { size: 16 }),
|
|
1780
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Heading 3" })
|
|
1781
|
-
]
|
|
1782
|
-
}
|
|
1783
|
-
)
|
|
1784
|
-
}
|
|
1785
|
-
),
|
|
1786
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1787
|
-
EditorCommandItem,
|
|
1788
|
-
{
|
|
1789
|
-
value: "heading4 h4",
|
|
1790
|
-
className: "nph-command__item",
|
|
1791
|
-
onCommand: ({
|
|
1792
|
-
editor: editor2,
|
|
1793
|
-
range
|
|
1794
|
-
}) => editor2.chain().focus().deleteRange(range).toggleHeading({ level: 4 }).run(),
|
|
1795
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1796
|
-
"span",
|
|
1797
|
-
{
|
|
1798
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1799
|
-
children: [
|
|
1800
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconH4, { size: 16 }),
|
|
1801
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Heading 4" })
|
|
1802
|
-
]
|
|
1803
|
-
}
|
|
1804
|
-
)
|
|
1805
|
-
}
|
|
1806
|
-
),
|
|
1807
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1808
|
-
EditorCommandItem,
|
|
1809
|
-
{
|
|
1810
|
-
value: "bullet list ul",
|
|
1811
|
-
className: "nph-command__item",
|
|
1812
|
-
onCommand: ({
|
|
1813
|
-
editor: editor2,
|
|
1814
|
-
range
|
|
1815
|
-
}) => editor2.chain().focus().deleteRange(range).toggleBulletList().run(),
|
|
1816
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1817
|
-
"span",
|
|
1818
|
-
{
|
|
1819
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1820
|
-
children: [
|
|
1821
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconList, { size: 16 }),
|
|
1822
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Bullet list" })
|
|
1823
|
-
]
|
|
1824
|
-
}
|
|
1825
|
-
)
|
|
1826
|
-
}
|
|
1827
|
-
),
|
|
1828
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1829
|
-
EditorCommandItem,
|
|
1830
|
-
{
|
|
1831
|
-
value: "ordered list ol",
|
|
1832
|
-
className: "nph-command__item",
|
|
1833
|
-
onCommand: ({
|
|
1834
|
-
editor: editor2,
|
|
1835
|
-
range
|
|
1836
|
-
}) => editor2.chain().focus().deleteRange(range).toggleOrderedList().run(),
|
|
1837
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1838
|
-
"span",
|
|
1839
|
-
{
|
|
1840
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1841
|
-
children: [
|
|
1842
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconListNumbers, { size: 16 }),
|
|
1843
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Ordered list" })
|
|
1844
|
-
]
|
|
1845
|
-
}
|
|
1846
|
-
)
|
|
1847
|
-
}
|
|
1848
|
-
),
|
|
1849
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1850
|
-
EditorCommandItem,
|
|
1851
|
-
{
|
|
1852
|
-
value: "quote blockquote",
|
|
1853
|
-
className: "nph-command__item",
|
|
1854
|
-
onCommand: ({
|
|
1855
|
-
editor: editor2,
|
|
1856
|
-
range
|
|
1857
|
-
}) => editor2.chain().focus().deleteRange(range).toggleBlockquote().run(),
|
|
1858
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1859
|
-
"span",
|
|
1860
|
-
{
|
|
1861
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1862
|
-
children: [
|
|
1863
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconBlockquote, { size: 16 }),
|
|
1864
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Quote" })
|
|
1865
|
-
]
|
|
1866
|
-
}
|
|
1867
|
-
)
|
|
1868
|
-
}
|
|
1869
|
-
),
|
|
1870
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1871
|
-
EditorCommandItem,
|
|
1872
|
-
{
|
|
1873
|
-
value: "code inline",
|
|
1874
|
-
className: "nph-command__item",
|
|
1875
|
-
onCommand: ({
|
|
1876
|
-
editor: editor2,
|
|
1877
|
-
range
|
|
1878
|
-
}) => editor2.chain().focus().deleteRange(range).toggleCode().run(),
|
|
1879
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1880
|
-
"span",
|
|
1881
|
-
{
|
|
1882
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1883
|
-
children: [
|
|
1884
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconCode, { size: 16 }),
|
|
1885
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Code" })
|
|
1886
|
-
]
|
|
1887
|
-
}
|
|
1888
|
-
)
|
|
1889
|
-
}
|
|
1890
|
-
),
|
|
1891
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1892
|
-
EditorCommandItem,
|
|
1893
|
-
{
|
|
1894
|
-
value: "code block codeblock",
|
|
1895
|
-
className: "nph-command__item",
|
|
1896
|
-
onCommand: ({
|
|
1897
|
-
editor: editor2,
|
|
1898
|
-
range
|
|
1899
|
-
}) => editor2.chain().focus().deleteRange(range).toggleCodeBlock().run(),
|
|
1900
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1901
|
-
"span",
|
|
1902
|
-
{
|
|
1903
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1904
|
-
children: [
|
|
1905
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconSourceCode, { size: 16 }),
|
|
1906
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Code Block" })
|
|
1907
|
-
]
|
|
1908
|
-
}
|
|
1909
|
-
)
|
|
1910
|
-
}
|
|
1911
|
-
),
|
|
1912
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1913
|
-
EditorCommandItem,
|
|
1914
|
-
{
|
|
1915
|
-
value: "image photo picture block",
|
|
1916
|
-
className: "nph-command__item",
|
|
1917
|
-
onCommand: ({
|
|
1918
|
-
editor: editor2,
|
|
1919
|
-
range
|
|
1920
|
-
}) => {
|
|
1921
|
-
;
|
|
1922
|
-
editor2.chain().focus().deleteRange(range).setImageBlock({ src: "" }).run();
|
|
1923
|
-
},
|
|
1924
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1925
|
-
"span",
|
|
1926
|
-
{
|
|
1927
|
-
style: { display: "inline-flex", alignItems: "center", gap: 8 },
|
|
1928
|
-
children: [
|
|
1929
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconPhoto, { size: 16 }),
|
|
1930
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Image" })
|
|
1931
|
-
]
|
|
1932
|
-
}
|
|
1933
|
-
)
|
|
1934
|
-
}
|
|
1935
|
-
),
|
|
1936
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1937
|
-
EditorCommandItem,
|
|
1938
|
-
{
|
|
1939
|
-
value: "video embed youtube vimeo",
|
|
1940
|
-
className: "nph-command__item",
|
|
1941
|
-
onCommand: ({
|
|
1942
|
-
editor: editor2,
|
|
1943
|
-
range
|
|
1944
|
-
}) => {
|
|
1945
|
-
;
|
|
1946
|
-
editor2.chain().focus().deleteRange(range).setVideoBlock({ src: "" }).run();
|
|
2139
|
+
children: filteredGroups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { padding: "8px 12px", fontSize: 14, color: "var(--muted-foreground, #6b7280)" }, children: "No commands found" }) : filteredGroups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
|
|
2140
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "nph-command__group-header", children: group.group }),
|
|
2141
|
+
group.items.map((item) => {
|
|
2142
|
+
const Icon = item.icon;
|
|
2143
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2144
|
+
EditorCommandItem,
|
|
2145
|
+
{
|
|
2146
|
+
value: item.value,
|
|
2147
|
+
className: "nph-command__item",
|
|
2148
|
+
onCommand: item.onCommand,
|
|
2149
|
+
children: [
|
|
2150
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "nph-command__item-icon", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, { size: 18 }) }),
|
|
2151
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "nph-command__item-content", children: [
|
|
2152
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "nph-command__item-title", children: item.label }),
|
|
2153
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "nph-command__item-description", children: item.description })
|
|
2154
|
+
] })
|
|
2155
|
+
]
|
|
1947
2156
|
},
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
children: [
|
|
1953
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_react3.IconVideo, { size: 16 }),
|
|
1954
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Video" })
|
|
1955
|
-
]
|
|
1956
|
-
}
|
|
1957
|
-
)
|
|
1958
|
-
}
|
|
1959
|
-
)
|
|
1960
|
-
]
|
|
2157
|
+
item.value
|
|
2158
|
+
);
|
|
2159
|
+
})
|
|
2160
|
+
] }, group.group))
|
|
1961
2161
|
}
|
|
1962
2162
|
) });
|
|
1963
2163
|
}
|
|
@@ -2108,6 +2308,7 @@ function LinkMenu() {
|
|
|
2108
2308
|
}
|
|
2109
2309
|
|
|
2110
2310
|
// src/react/menus/ImageMenu.tsx
|
|
2311
|
+
var import_state5 = require("@tiptap/pm/state");
|
|
2111
2312
|
var import_react19 = require("@tiptap/react");
|
|
2112
2313
|
var import_menus3 = require("@tiptap/react/menus");
|
|
2113
2314
|
var import_icons_react5 = require("@tabler/icons-react");
|
|
@@ -2169,7 +2370,7 @@ function ImageMenu({
|
|
|
2169
2370
|
shouldShow: ({ editor: e, state }) => {
|
|
2170
2371
|
if (!e.isActive("imageBlock")) return false;
|
|
2171
2372
|
const { selection } = state;
|
|
2172
|
-
const isNodeSelection = selection
|
|
2373
|
+
const isNodeSelection = selection instanceof import_state5.NodeSelection;
|
|
2173
2374
|
return isNodeSelection;
|
|
2174
2375
|
},
|
|
2175
2376
|
updateDelay: 0,
|
|
@@ -2281,12 +2482,13 @@ function ImageMenu({
|
|
|
2281
2482
|
}
|
|
2282
2483
|
|
|
2283
2484
|
// src/react/menus/ImageBlock/ImageBlockView.tsx
|
|
2284
|
-
var
|
|
2285
|
-
var
|
|
2485
|
+
var import_react26 = require("@tiptap/react");
|
|
2486
|
+
var import_react27 = require("react");
|
|
2286
2487
|
|
|
2287
2488
|
// src/react/menus/ImageBlock/ImageBlockMenu.tsx
|
|
2288
|
-
var
|
|
2289
|
-
var import_react22 = require("react");
|
|
2489
|
+
var import_state6 = require("@tiptap/pm/state");
|
|
2490
|
+
var import_react22 = require("@tiptap/react");
|
|
2491
|
+
var import_react23 = require("react");
|
|
2290
2492
|
|
|
2291
2493
|
// src/react/menus/ImageBlock/ImageBlockWidth.tsx
|
|
2292
2494
|
var import_react21 = require("react");
|
|
@@ -2351,82 +2553,60 @@ ImageBlockWidth.displayName = "ImageBlockWidth";
|
|
|
2351
2553
|
// src/react/menus/ImageBlock/ImageBlockMenu.tsx
|
|
2352
2554
|
var import_icons_react6 = require("@tabler/icons-react");
|
|
2353
2555
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2354
|
-
var ImageBlockMenu = ({ editor, appendTo }) => {
|
|
2355
|
-
const menuRef = (0,
|
|
2356
|
-
const
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
const
|
|
2363
|
-
|
|
2364
|
-
const
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
editor.off("transaction", update);
|
|
2373
|
-
};
|
|
2374
|
-
}, [editor]);
|
|
2375
|
-
const getReferenceClientRect = (0, import_react22.useCallback)(() => {
|
|
2376
|
-
if (!editor) return new DOMRect(-1e3, -1e3, 0, 0);
|
|
2377
|
-
const { view } = editor;
|
|
2378
|
-
const { state } = view;
|
|
2379
|
-
const { selection } = state;
|
|
2380
|
-
const node = selection instanceof window.ProseMirror?.state?.NodeSelection ? selection.node : null;
|
|
2381
|
-
if (node && node.type.name === "imageBlock") {
|
|
2382
|
-
const nodePos = selection.from;
|
|
2383
|
-
const domNode = view.nodeDOM(nodePos);
|
|
2384
|
-
if (domNode && domNode instanceof HTMLElement) {
|
|
2385
|
-
return domNode.getBoundingClientRect();
|
|
2386
|
-
}
|
|
2387
|
-
}
|
|
2388
|
-
const imageBlockElements = document.querySelectorAll("[data-node-view-wrapper]");
|
|
2389
|
-
for (const el of Array.from(imageBlockElements)) {
|
|
2390
|
-
if (el.querySelector("img")) {
|
|
2391
|
-
return el.getBoundingClientRect();
|
|
2556
|
+
var ImageBlockMenu = ({ editor, getPos, appendTo }) => {
|
|
2557
|
+
const menuRef = (0, import_react23.useRef)(null);
|
|
2558
|
+
const { isVisible, align, width } = (0, import_react22.useEditorState)({
|
|
2559
|
+
editor,
|
|
2560
|
+
selector: (ctx) => {
|
|
2561
|
+
if (!ctx.editor) return { isVisible: false, align: "center", width: 100 };
|
|
2562
|
+
const { state } = ctx.editor;
|
|
2563
|
+
const { selection } = state;
|
|
2564
|
+
const isNodeSel = selection instanceof import_state6.NodeSelection;
|
|
2565
|
+
const isThisNode = isNodeSel && selection.from === getPos();
|
|
2566
|
+
const visible = isThisNode;
|
|
2567
|
+
let currentAlign = "center";
|
|
2568
|
+
let currentWidth = 100;
|
|
2569
|
+
if (visible) {
|
|
2570
|
+
const attrs = ctx.editor.getAttributes("imageBlock");
|
|
2571
|
+
currentAlign = attrs.align || "center";
|
|
2572
|
+
const widthStr = attrs.width || "100%";
|
|
2573
|
+
currentWidth = parseInt(widthStr) || 100;
|
|
2392
2574
|
}
|
|
2575
|
+
return { isVisible: visible, align: currentAlign, width: currentWidth };
|
|
2393
2576
|
}
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
const shouldShow = (0, import_react22.useCallback)(() => {
|
|
2397
|
-
if (!editor) return false;
|
|
2398
|
-
const isActive = editor.isActive("imageBlock");
|
|
2399
|
-
if (!isActive) return false;
|
|
2400
|
-
const { state } = editor;
|
|
2401
|
-
const { selection } = state;
|
|
2402
|
-
const isNodeSelection = selection.constructor.name === "NodeSelection";
|
|
2403
|
-
return isNodeSelection;
|
|
2404
|
-
}, [editor]);
|
|
2405
|
-
const onAlignImageLeft = (0, import_react22.useCallback)(() => {
|
|
2577
|
+
});
|
|
2578
|
+
const onAlignImageLeft = (0, import_react23.useCallback)(() => {
|
|
2406
2579
|
editor.chain().focus(void 0, { scrollIntoView: false }).setImageBlockAlign("left").run();
|
|
2407
2580
|
}, [editor]);
|
|
2408
|
-
const onAlignImageCenter = (0,
|
|
2581
|
+
const onAlignImageCenter = (0, import_react23.useCallback)(() => {
|
|
2409
2582
|
editor.chain().focus(void 0, { scrollIntoView: false }).setImageBlockAlign("center").run();
|
|
2410
2583
|
}, [editor]);
|
|
2411
|
-
const onAlignImageRight = (0,
|
|
2584
|
+
const onAlignImageRight = (0, import_react23.useCallback)(() => {
|
|
2412
2585
|
editor.chain().focus(void 0, { scrollIntoView: false }).setImageBlockAlign("right").run();
|
|
2413
2586
|
}, [editor]);
|
|
2414
|
-
const onWidthChange = (0,
|
|
2587
|
+
const onWidthChange = (0, import_react23.useCallback)(
|
|
2415
2588
|
(value) => {
|
|
2416
2589
|
editor.chain().focus(void 0, { scrollIntoView: false }).setImageBlockWidth(value).run();
|
|
2417
2590
|
},
|
|
2418
2591
|
[editor]
|
|
2419
2592
|
);
|
|
2420
|
-
const onRemoveImage = (0,
|
|
2593
|
+
const onRemoveImage = (0, import_react23.useCallback)(() => {
|
|
2421
2594
|
editor.chain().focus(void 0, { scrollIntoView: false }).deleteSelection().run();
|
|
2422
2595
|
}, [editor]);
|
|
2423
|
-
|
|
2424
|
-
|
|
2596
|
+
if (!isVisible) return null;
|
|
2597
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2598
|
+
"div",
|
|
2425
2599
|
{
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2600
|
+
className: "bubble-menu",
|
|
2601
|
+
ref: menuRef,
|
|
2602
|
+
style: {
|
|
2603
|
+
position: "absolute",
|
|
2604
|
+
top: "-40px",
|
|
2605
|
+
left: "50%",
|
|
2606
|
+
transform: "translateX(-50%)",
|
|
2607
|
+
zIndex: "var(--nph-z, 50)"
|
|
2608
|
+
},
|
|
2609
|
+
children: [
|
|
2430
2610
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2431
2611
|
"button",
|
|
2432
2612
|
{
|
|
@@ -2486,14 +2666,14 @@ var ImageBlockMenu = ({ editor, appendTo }) => {
|
|
|
2486
2666
|
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_icons_react6.IconTrash, { size: 16 })
|
|
2487
2667
|
}
|
|
2488
2668
|
)
|
|
2489
|
-
]
|
|
2669
|
+
]
|
|
2490
2670
|
}
|
|
2491
2671
|
);
|
|
2492
2672
|
};
|
|
2493
2673
|
|
|
2494
2674
|
// src/react/menus/ImageBlock/ImageUploader.tsx
|
|
2495
2675
|
var import_icons_react8 = require("@tabler/icons-react");
|
|
2496
|
-
var
|
|
2676
|
+
var import_react24 = require("react");
|
|
2497
2677
|
|
|
2498
2678
|
// src/react/menus/ImageBlock/ImageBlockLoading.tsx
|
|
2499
2679
|
var import_icons_react7 = require("@tabler/icons-react");
|
|
@@ -2511,10 +2691,10 @@ var ImageBlockLoading = () => {
|
|
|
2511
2691
|
// src/react/menus/ImageBlock/ImageUploader.tsx
|
|
2512
2692
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2513
2693
|
var ImageUploader = ({ onUpload, editor }) => {
|
|
2514
|
-
const [loading, setLoading] = (0,
|
|
2515
|
-
const [draggedInside, setDraggedInside] = (0,
|
|
2516
|
-
const fileInputRef = (0,
|
|
2517
|
-
const uploadFile = (0,
|
|
2694
|
+
const [loading, setLoading] = (0, import_react24.useState)(false);
|
|
2695
|
+
const [draggedInside, setDraggedInside] = (0, import_react24.useState)(false);
|
|
2696
|
+
const fileInputRef = (0, import_react24.useRef)(null);
|
|
2697
|
+
const uploadFile = (0, import_react24.useCallback)(
|
|
2518
2698
|
async (file) => {
|
|
2519
2699
|
setLoading(true);
|
|
2520
2700
|
try {
|
|
@@ -2536,10 +2716,10 @@ var ImageUploader = ({ onUpload, editor }) => {
|
|
|
2536
2716
|
},
|
|
2537
2717
|
[editor, onUpload]
|
|
2538
2718
|
);
|
|
2539
|
-
const handleUploadClick = (0,
|
|
2719
|
+
const handleUploadClick = (0, import_react24.useCallback)(() => {
|
|
2540
2720
|
fileInputRef.current?.click();
|
|
2541
2721
|
}, []);
|
|
2542
|
-
const onFileChange = (0,
|
|
2722
|
+
const onFileChange = (0, import_react24.useCallback)(
|
|
2543
2723
|
(e) => {
|
|
2544
2724
|
const file = e.target.files?.[0];
|
|
2545
2725
|
if (file) {
|
|
@@ -2548,7 +2728,7 @@ var ImageUploader = ({ onUpload, editor }) => {
|
|
|
2548
2728
|
},
|
|
2549
2729
|
[uploadFile]
|
|
2550
2730
|
);
|
|
2551
|
-
const onDrop = (0,
|
|
2731
|
+
const onDrop = (0, import_react24.useCallback)(
|
|
2552
2732
|
(e) => {
|
|
2553
2733
|
e.preventDefault();
|
|
2554
2734
|
e.stopPropagation();
|
|
@@ -2560,12 +2740,12 @@ var ImageUploader = ({ onUpload, editor }) => {
|
|
|
2560
2740
|
},
|
|
2561
2741
|
[uploadFile]
|
|
2562
2742
|
);
|
|
2563
|
-
const onDragEnter = (0,
|
|
2743
|
+
const onDragEnter = (0, import_react24.useCallback)((e) => {
|
|
2564
2744
|
e.preventDefault();
|
|
2565
2745
|
e.stopPropagation();
|
|
2566
2746
|
setDraggedInside(true);
|
|
2567
2747
|
}, []);
|
|
2568
|
-
const onDragLeave = (0,
|
|
2748
|
+
const onDragLeave = (0, import_react24.useCallback)((e) => {
|
|
2569
2749
|
e.preventDefault();
|
|
2570
2750
|
e.stopPropagation();
|
|
2571
2751
|
setDraggedInside(false);
|
|
@@ -2614,21 +2794,93 @@ var ImageUploader = ({ onUpload, editor }) => {
|
|
|
2614
2794
|
);
|
|
2615
2795
|
};
|
|
2616
2796
|
|
|
2617
|
-
// src/react/menus/ImageBlock/
|
|
2797
|
+
// src/react/menus/ImageBlock/ImageResizeHandle.tsx
|
|
2798
|
+
var import_react25 = require("react");
|
|
2618
2799
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2800
|
+
function ImageResizeHandle({
|
|
2801
|
+
children,
|
|
2802
|
+
onResize,
|
|
2803
|
+
currentWidth
|
|
2804
|
+
}) {
|
|
2805
|
+
const containerRef = (0, import_react25.useRef)(null);
|
|
2806
|
+
const [isResizing, setIsResizing] = (0, import_react25.useState)(false);
|
|
2807
|
+
const handleMouseDown = (0, import_react25.useCallback)(
|
|
2808
|
+
(e, side) => {
|
|
2809
|
+
e.preventDefault();
|
|
2810
|
+
e.stopPropagation();
|
|
2811
|
+
setIsResizing(true);
|
|
2812
|
+
const startX = e.clientX;
|
|
2813
|
+
const containerEl = containerRef.current;
|
|
2814
|
+
if (!containerEl) return;
|
|
2815
|
+
const editorEl = containerEl.closest(".nph-editor") || containerEl.parentElement;
|
|
2816
|
+
if (!editorEl) return;
|
|
2817
|
+
const editorWidth = editorEl.getBoundingClientRect().width;
|
|
2818
|
+
const startWidth = containerEl.getBoundingClientRect().width;
|
|
2819
|
+
const startPercent = startWidth / editorWidth * 100;
|
|
2820
|
+
const handleMouseMove = (moveEvent) => {
|
|
2821
|
+
const deltaX = moveEvent.clientX - startX;
|
|
2822
|
+
const direction = side === "right" ? 1 : -1;
|
|
2823
|
+
const deltaPercent = deltaX * direction / editorWidth * 100 * 2;
|
|
2824
|
+
const newPercent = Math.max(10, Math.min(100, startPercent + deltaPercent));
|
|
2825
|
+
onResize(Math.round(newPercent));
|
|
2826
|
+
};
|
|
2827
|
+
const handleMouseUp = () => {
|
|
2828
|
+
setIsResizing(false);
|
|
2829
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
2830
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
2831
|
+
};
|
|
2832
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
2833
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
2834
|
+
},
|
|
2835
|
+
[onResize]
|
|
2836
|
+
);
|
|
2837
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2838
|
+
"div",
|
|
2839
|
+
{
|
|
2840
|
+
ref: containerRef,
|
|
2841
|
+
className: `nph-resize-wrapper${isResizing ? " nph-resize-wrapper--active" : ""}`,
|
|
2842
|
+
children: [
|
|
2843
|
+
children,
|
|
2844
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2845
|
+
"div",
|
|
2846
|
+
{
|
|
2847
|
+
className: "nph-resize-handle nph-resize-handle--left",
|
|
2848
|
+
onMouseDown: (e) => handleMouseDown(e, "left")
|
|
2849
|
+
}
|
|
2850
|
+
),
|
|
2851
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2852
|
+
"div",
|
|
2853
|
+
{
|
|
2854
|
+
className: "nph-resize-handle nph-resize-handle--right",
|
|
2855
|
+
onMouseDown: (e) => handleMouseDown(e, "right")
|
|
2856
|
+
}
|
|
2857
|
+
)
|
|
2858
|
+
]
|
|
2859
|
+
}
|
|
2860
|
+
);
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2863
|
+
// src/react/menus/ImageBlock/ImageBlockView.tsx
|
|
2864
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
2619
2865
|
var ImageBlockView = (props) => {
|
|
2620
2866
|
const { editor, getPos, node, updateAttributes } = props;
|
|
2621
|
-
const imageWrapperRef = (0,
|
|
2867
|
+
const imageWrapperRef = (0, import_react27.useRef)(null);
|
|
2622
2868
|
const { src, width, align, alt, loading } = node.attrs;
|
|
2623
|
-
const handleUpload = (0,
|
|
2869
|
+
const handleUpload = (0, import_react27.useCallback)(
|
|
2624
2870
|
(url) => {
|
|
2625
2871
|
updateAttributes({ src: url, loading: false });
|
|
2626
2872
|
},
|
|
2627
2873
|
[updateAttributes]
|
|
2628
2874
|
);
|
|
2629
|
-
const onClick = (0,
|
|
2875
|
+
const onClick = (0, import_react27.useCallback)(() => {
|
|
2630
2876
|
editor.commands.setNodeSelection(getPos());
|
|
2631
2877
|
}, [getPos, editor.commands]);
|
|
2878
|
+
const handleResize = (0, import_react27.useCallback)(
|
|
2879
|
+
(widthPercent) => {
|
|
2880
|
+
updateAttributes({ width: `${widthPercent}%` });
|
|
2881
|
+
},
|
|
2882
|
+
[updateAttributes]
|
|
2883
|
+
);
|
|
2632
2884
|
const getWrapperStyle = () => {
|
|
2633
2885
|
const baseStyle = {
|
|
2634
2886
|
width: width || "100%",
|
|
@@ -2642,14 +2894,17 @@ var ImageBlockView = (props) => {
|
|
|
2642
2894
|
return { ...baseStyle, marginLeft: "auto", marginRight: "auto" };
|
|
2643
2895
|
}
|
|
2644
2896
|
};
|
|
2897
|
+
const getContentStyle = () => ({
|
|
2898
|
+
position: "relative"
|
|
2899
|
+
});
|
|
2645
2900
|
if (!src || src === "") {
|
|
2646
|
-
return /* @__PURE__ */ (0,
|
|
2901
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react26.NodeViewWrapper, { style: { width: "100%", marginTop: "0.5rem", marginBottom: "0.5rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: imageWrapperRef, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ImageUploader, { onUpload: handleUpload, editor }) }) });
|
|
2647
2902
|
}
|
|
2648
2903
|
if (loading) {
|
|
2649
|
-
return /* @__PURE__ */ (0,
|
|
2904
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react26.NodeViewWrapper, { style: getWrapperStyle(), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { ref: imageWrapperRef, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ImageBlockLoading, {}) }) });
|
|
2650
2905
|
}
|
|
2651
|
-
return /* @__PURE__ */ (0,
|
|
2652
|
-
/* @__PURE__ */ (0,
|
|
2906
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react26.NodeViewWrapper, { style: getWrapperStyle(), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { contentEditable: false, ref: imageWrapperRef, style: getContentStyle(), children: [
|
|
2907
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ImageResizeHandle, { onResize: handleResize, currentWidth: width, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2653
2908
|
"img",
|
|
2654
2909
|
{
|
|
2655
2910
|
src,
|
|
@@ -2657,76 +2912,76 @@ var ImageBlockView = (props) => {
|
|
|
2657
2912
|
onClick,
|
|
2658
2913
|
className: "nph-image-block"
|
|
2659
2914
|
}
|
|
2660
|
-
) })
|
|
2661
|
-
/* @__PURE__ */ (0,
|
|
2662
|
-
] });
|
|
2915
|
+
) }),
|
|
2916
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ImageBlockMenu, { editor, getPos, appendTo: imageWrapperRef })
|
|
2917
|
+
] }) });
|
|
2663
2918
|
};
|
|
2664
2919
|
|
|
2665
2920
|
// src/react/menus/VideoBlock/VideoBlockView.tsx
|
|
2666
|
-
var
|
|
2667
|
-
var
|
|
2921
|
+
var import_state8 = require("@tiptap/pm/state");
|
|
2922
|
+
var import_react30 = require("@tiptap/react");
|
|
2923
|
+
var import_react31 = require("react");
|
|
2668
2924
|
|
|
2669
2925
|
// src/react/menus/VideoBlock/VideoBlockMenu.tsx
|
|
2670
|
-
var
|
|
2671
|
-
var
|
|
2926
|
+
var import_state7 = require("@tiptap/pm/state");
|
|
2927
|
+
var import_react28 = require("@tiptap/react");
|
|
2928
|
+
var import_react29 = require("react");
|
|
2672
2929
|
var import_icons_react9 = require("@tabler/icons-react");
|
|
2673
|
-
var
|
|
2674
|
-
var VideoBlockMenu = ({ editor }) => {
|
|
2675
|
-
const menuRef = (0,
|
|
2676
|
-
const
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
const
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
}
|
|
2694
|
-
}
|
|
2695
|
-
const
|
|
2696
|
-
if (!editor) return false;
|
|
2697
|
-
const { state } = editor;
|
|
2698
|
-
const { selection } = state;
|
|
2699
|
-
if (selection.constructor.name !== "NodeSelection") return false;
|
|
2700
|
-
const node = selection.node;
|
|
2701
|
-
if (!node || node.type.name !== "videoBlock") return false;
|
|
2702
|
-
return true;
|
|
2703
|
-
}, [editor]);
|
|
2704
|
-
const onAlignLeft = (0, import_react26.useCallback)(() => {
|
|
2930
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
2931
|
+
var VideoBlockMenu = ({ editor, getPos }) => {
|
|
2932
|
+
const menuRef = (0, import_react29.useRef)(null);
|
|
2933
|
+
const { isVisible, align, width } = (0, import_react28.useEditorState)({
|
|
2934
|
+
editor,
|
|
2935
|
+
selector: (ctx) => {
|
|
2936
|
+
if (!ctx.editor) return { isVisible: false, align: "center", width: 100 };
|
|
2937
|
+
const { state } = ctx.editor;
|
|
2938
|
+
const { selection } = state;
|
|
2939
|
+
const isNodeSel = selection instanceof import_state7.NodeSelection;
|
|
2940
|
+
const isThisNode = isNodeSel && selection.from === getPos();
|
|
2941
|
+
let currentAlign = "center";
|
|
2942
|
+
let currentWidth = 100;
|
|
2943
|
+
if (isThisNode) {
|
|
2944
|
+
const attrs = ctx.editor.getAttributes("videoBlock");
|
|
2945
|
+
currentAlign = attrs.align || "center";
|
|
2946
|
+
const widthStr = attrs.width || "100%";
|
|
2947
|
+
currentWidth = parseInt(widthStr) || 100;
|
|
2948
|
+
}
|
|
2949
|
+
return { isVisible: isThisNode, align: currentAlign, width: currentWidth };
|
|
2950
|
+
}
|
|
2951
|
+
});
|
|
2952
|
+
const onAlignLeft = (0, import_react29.useCallback)(() => {
|
|
2705
2953
|
editor.chain().focus(void 0, { scrollIntoView: false }).setVideoBlockAlign("left").run();
|
|
2706
2954
|
}, [editor]);
|
|
2707
|
-
const onAlignCenter = (0,
|
|
2955
|
+
const onAlignCenter = (0, import_react29.useCallback)(() => {
|
|
2708
2956
|
editor.chain().focus(void 0, { scrollIntoView: false }).setVideoBlockAlign("center").run();
|
|
2709
2957
|
}, [editor]);
|
|
2710
|
-
const onAlignRight = (0,
|
|
2958
|
+
const onAlignRight = (0, import_react29.useCallback)(() => {
|
|
2711
2959
|
editor.chain().focus(void 0, { scrollIntoView: false }).setVideoBlockAlign("right").run();
|
|
2712
2960
|
}, [editor]);
|
|
2713
|
-
const onWidthChange = (0,
|
|
2961
|
+
const onWidthChange = (0, import_react29.useCallback)(
|
|
2714
2962
|
(value) => {
|
|
2715
2963
|
editor.chain().focus(void 0, { scrollIntoView: false }).setVideoBlockWidth(value).run();
|
|
2716
2964
|
},
|
|
2717
2965
|
[editor]
|
|
2718
2966
|
);
|
|
2719
|
-
const onRemove = (0,
|
|
2967
|
+
const onRemove = (0, import_react29.useCallback)(() => {
|
|
2720
2968
|
editor.chain().focus(void 0, { scrollIntoView: false }).deleteSelection().run();
|
|
2721
2969
|
}, [editor]);
|
|
2722
|
-
|
|
2723
|
-
|
|
2970
|
+
if (!isVisible) return null;
|
|
2971
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
2972
|
+
"div",
|
|
2724
2973
|
{
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2974
|
+
className: "bubble-menu",
|
|
2975
|
+
ref: menuRef,
|
|
2976
|
+
style: {
|
|
2977
|
+
position: "absolute",
|
|
2978
|
+
top: "-40px",
|
|
2979
|
+
left: "50%",
|
|
2980
|
+
transform: "translateX(-50%)",
|
|
2981
|
+
zIndex: "var(--nph-z, 50)"
|
|
2982
|
+
},
|
|
2983
|
+
children: [
|
|
2984
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2730
2985
|
"button",
|
|
2731
2986
|
{
|
|
2732
2987
|
type: "button",
|
|
@@ -2734,10 +2989,10 @@ var VideoBlockMenu = ({ editor }) => {
|
|
|
2734
2989
|
title: "Align left",
|
|
2735
2990
|
onMouseDown: (e) => e.preventDefault(),
|
|
2736
2991
|
onClick: onAlignLeft,
|
|
2737
|
-
children: /* @__PURE__ */ (0,
|
|
2992
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons_react9.IconAlignLeft, { size: 16 })
|
|
2738
2993
|
}
|
|
2739
2994
|
),
|
|
2740
|
-
/* @__PURE__ */ (0,
|
|
2995
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2741
2996
|
"button",
|
|
2742
2997
|
{
|
|
2743
2998
|
type: "button",
|
|
@@ -2745,10 +3000,10 @@ var VideoBlockMenu = ({ editor }) => {
|
|
|
2745
3000
|
title: "Align center",
|
|
2746
3001
|
onMouseDown: (e) => e.preventDefault(),
|
|
2747
3002
|
onClick: onAlignCenter,
|
|
2748
|
-
children: /* @__PURE__ */ (0,
|
|
3003
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons_react9.IconAlignCenter, { size: 16 })
|
|
2749
3004
|
}
|
|
2750
3005
|
),
|
|
2751
|
-
/* @__PURE__ */ (0,
|
|
3006
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2752
3007
|
"button",
|
|
2753
3008
|
{
|
|
2754
3009
|
type: "button",
|
|
@@ -2756,25 +3011,25 @@ var VideoBlockMenu = ({ editor }) => {
|
|
|
2756
3011
|
title: "Align right",
|
|
2757
3012
|
onMouseDown: (e) => e.preventDefault(),
|
|
2758
3013
|
onClick: onAlignRight,
|
|
2759
|
-
children: /* @__PURE__ */ (0,
|
|
3014
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons_react9.IconAlignRight, { size: 16 })
|
|
2760
3015
|
}
|
|
2761
3016
|
),
|
|
2762
|
-
/* @__PURE__ */ (0,
|
|
3017
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2763
3018
|
"div",
|
|
2764
3019
|
{
|
|
2765
3020
|
className: "nph-link-popover__divider",
|
|
2766
3021
|
style: { margin: "0 4px" }
|
|
2767
3022
|
}
|
|
2768
3023
|
),
|
|
2769
|
-
/* @__PURE__ */ (0,
|
|
2770
|
-
/* @__PURE__ */ (0,
|
|
3024
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ImageBlockWidth, { onChange: onWidthChange, value: width }),
|
|
3025
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2771
3026
|
"div",
|
|
2772
3027
|
{
|
|
2773
3028
|
className: "nph-link-popover__divider",
|
|
2774
3029
|
style: { margin: "0 4px" }
|
|
2775
3030
|
}
|
|
2776
3031
|
),
|
|
2777
|
-
/* @__PURE__ */ (0,
|
|
3032
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2778
3033
|
"button",
|
|
2779
3034
|
{
|
|
2780
3035
|
type: "button",
|
|
@@ -2782,17 +3037,17 @@ var VideoBlockMenu = ({ editor }) => {
|
|
|
2782
3037
|
title: "Remove video",
|
|
2783
3038
|
onMouseDown: (e) => e.preventDefault(),
|
|
2784
3039
|
onClick: onRemove,
|
|
2785
|
-
children: /* @__PURE__ */ (0,
|
|
3040
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons_react9.IconTrash, { size: 16 })
|
|
2786
3041
|
}
|
|
2787
3042
|
)
|
|
2788
|
-
]
|
|
3043
|
+
]
|
|
2789
3044
|
}
|
|
2790
3045
|
);
|
|
2791
3046
|
};
|
|
2792
3047
|
|
|
2793
3048
|
// src/react/menus/VideoBlock/VideoBlockView.tsx
|
|
2794
3049
|
var import_icons_react10 = require("@tabler/icons-react");
|
|
2795
|
-
var
|
|
3050
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
2796
3051
|
function toEmbedUrl(url) {
|
|
2797
3052
|
const ytMatch = url.match(
|
|
2798
3053
|
/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/
|
|
@@ -2808,15 +3063,23 @@ function toEmbedUrl(url) {
|
|
|
2808
3063
|
}
|
|
2809
3064
|
var VideoBlockView = (props) => {
|
|
2810
3065
|
const { editor, getPos, node, updateAttributes } = props;
|
|
2811
|
-
const wrapperRef = (0,
|
|
3066
|
+
const wrapperRef = (0, import_react31.useRef)(null);
|
|
2812
3067
|
const { src, width, align } = node.attrs;
|
|
2813
|
-
const [inputUrl, setInputUrl] = (0,
|
|
2814
|
-
const
|
|
3068
|
+
const [inputUrl, setInputUrl] = (0, import_react31.useState)("");
|
|
3069
|
+
const isSelected = (0, import_react30.useEditorState)({
|
|
3070
|
+
editor,
|
|
3071
|
+
selector: (ctx) => {
|
|
3072
|
+
if (!ctx.editor) return false;
|
|
3073
|
+
const { selection } = ctx.editor.state;
|
|
3074
|
+
return selection instanceof import_state8.NodeSelection && selection.from === getPos();
|
|
3075
|
+
}
|
|
3076
|
+
});
|
|
3077
|
+
const handleEmbed = (0, import_react31.useCallback)(() => {
|
|
2815
3078
|
if (!inputUrl.trim()) return;
|
|
2816
3079
|
const embedUrl = toEmbedUrl(inputUrl.trim());
|
|
2817
3080
|
updateAttributes({ src: embedUrl });
|
|
2818
3081
|
}, [inputUrl, updateAttributes]);
|
|
2819
|
-
const handleKeyDown = (0,
|
|
3082
|
+
const handleKeyDown = (0, import_react31.useCallback)(
|
|
2820
3083
|
(e) => {
|
|
2821
3084
|
if (e.key === "Enter") {
|
|
2822
3085
|
e.preventDefault();
|
|
@@ -2825,13 +3088,13 @@ var VideoBlockView = (props) => {
|
|
|
2825
3088
|
},
|
|
2826
3089
|
[handleEmbed]
|
|
2827
3090
|
);
|
|
2828
|
-
const onClick = (0,
|
|
3091
|
+
const onClick = (0, import_react31.useCallback)(() => {
|
|
2829
3092
|
editor.commands.setNodeSelection(getPos());
|
|
2830
3093
|
}, [getPos, editor.commands]);
|
|
2831
3094
|
const getWrapperStyle = () => {
|
|
2832
3095
|
const baseStyle = {
|
|
2833
|
-
width:
|
|
2834
|
-
maxWidth: "100%"
|
|
3096
|
+
width: "fit-content",
|
|
3097
|
+
maxWidth: width || "100%"
|
|
2835
3098
|
};
|
|
2836
3099
|
if (align === "left") {
|
|
2837
3100
|
return { ...baseStyle, marginLeft: 0, marginRight: "auto" };
|
|
@@ -2842,10 +3105,10 @@ var VideoBlockView = (props) => {
|
|
|
2842
3105
|
}
|
|
2843
3106
|
};
|
|
2844
3107
|
if (!src || src === "") {
|
|
2845
|
-
return /* @__PURE__ */ (0,
|
|
2846
|
-
/* @__PURE__ */ (0,
|
|
2847
|
-
/* @__PURE__ */ (0,
|
|
2848
|
-
/* @__PURE__ */ (0,
|
|
3108
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react30.NodeViewWrapper, { style: getWrapperStyle(), children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: wrapperRef, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "nph-video-input", children: [
|
|
3109
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "nph-video-input__icon", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_icons_react10.IconVideo, { size: 24 }) }),
|
|
3110
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "nph-video-input__content", children: [
|
|
3111
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2849
3112
|
"input",
|
|
2850
3113
|
{
|
|
2851
3114
|
type: "text",
|
|
@@ -2856,7 +3119,7 @@ var VideoBlockView = (props) => {
|
|
|
2856
3119
|
onKeyDown: handleKeyDown
|
|
2857
3120
|
}
|
|
2858
3121
|
),
|
|
2859
|
-
/* @__PURE__ */ (0,
|
|
3122
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2860
3123
|
"button",
|
|
2861
3124
|
{
|
|
2862
3125
|
type: "button",
|
|
@@ -2869,38 +3132,730 @@ var VideoBlockView = (props) => {
|
|
|
2869
3132
|
] })
|
|
2870
3133
|
] }) }) });
|
|
2871
3134
|
}
|
|
2872
|
-
return /* @__PURE__ */ (0,
|
|
2873
|
-
|
|
2874
|
-
|
|
3135
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react30.NodeViewWrapper, { style: getWrapperStyle(), children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
3136
|
+
"div",
|
|
3137
|
+
{
|
|
3138
|
+
contentEditable: false,
|
|
3139
|
+
ref: wrapperRef,
|
|
3140
|
+
style: { position: "relative" },
|
|
3141
|
+
children: [
|
|
3142
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "nph-video-block", children: [
|
|
3143
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3144
|
+
"iframe",
|
|
3145
|
+
{
|
|
3146
|
+
src,
|
|
3147
|
+
className: "nph-video-block__iframe",
|
|
3148
|
+
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
3149
|
+
allowFullScreen: true
|
|
3150
|
+
}
|
|
3151
|
+
),
|
|
3152
|
+
!isSelected && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3153
|
+
"div",
|
|
3154
|
+
{
|
|
3155
|
+
className: "nph-video-block__overlay",
|
|
3156
|
+
onClick
|
|
3157
|
+
}
|
|
3158
|
+
)
|
|
3159
|
+
] }),
|
|
3160
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VideoBlockMenu, { editor, getPos })
|
|
3161
|
+
]
|
|
3162
|
+
}
|
|
3163
|
+
) });
|
|
3164
|
+
};
|
|
3165
|
+
|
|
3166
|
+
// src/react/menus/DragHandle/BlockActionMenu.tsx
|
|
3167
|
+
var import_icons_react11 = require("@tabler/icons-react");
|
|
3168
|
+
var import_react32 = require("react");
|
|
3169
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
3170
|
+
function BlockActionMenu({ editor, onClose }) {
|
|
3171
|
+
const executeAndClose = (0, import_react32.useCallback)(
|
|
3172
|
+
(fn) => {
|
|
3173
|
+
fn();
|
|
3174
|
+
onClose();
|
|
3175
|
+
},
|
|
3176
|
+
[onClose]
|
|
3177
|
+
);
|
|
3178
|
+
const handleDelete = (0, import_react32.useCallback)(() => {
|
|
3179
|
+
executeAndClose(() => {
|
|
3180
|
+
editor.commands.deleteSelection();
|
|
3181
|
+
});
|
|
3182
|
+
}, [editor, executeAndClose]);
|
|
3183
|
+
const handleDuplicate = (0, import_react32.useCallback)(() => {
|
|
3184
|
+
executeAndClose(() => {
|
|
3185
|
+
const { state } = editor;
|
|
3186
|
+
const { selection } = state;
|
|
3187
|
+
const { $anchor } = selection;
|
|
3188
|
+
const depth = $anchor.depth > 0 ? 1 : 0;
|
|
3189
|
+
const start = $anchor.start(depth);
|
|
3190
|
+
const end = $anchor.end(depth);
|
|
3191
|
+
const node = state.doc.nodeAt(start - 1);
|
|
3192
|
+
if (node) {
|
|
3193
|
+
const insertPos = end + 1;
|
|
3194
|
+
editor.chain().focus().insertContentAt(insertPos, node.toJSON()).run();
|
|
3195
|
+
}
|
|
3196
|
+
});
|
|
3197
|
+
}, [editor, executeAndClose]);
|
|
3198
|
+
const handleMoveUp = (0, import_react32.useCallback)(() => {
|
|
3199
|
+
executeAndClose(() => {
|
|
3200
|
+
const { state } = editor;
|
|
3201
|
+
const { selection } = state;
|
|
3202
|
+
const { $anchor } = selection;
|
|
3203
|
+
const depth = $anchor.depth > 0 ? 1 : 0;
|
|
3204
|
+
const blockStart = $anchor.start(depth) - 1;
|
|
3205
|
+
if (blockStart <= 0) return;
|
|
3206
|
+
const node = state.doc.nodeAt(blockStart);
|
|
3207
|
+
if (!node) return;
|
|
3208
|
+
const $pos = state.doc.resolve(blockStart);
|
|
3209
|
+
const index = $pos.index($pos.depth);
|
|
3210
|
+
if (index === 0) return;
|
|
3211
|
+
const prevNode = $pos.node($pos.depth).child(index - 1);
|
|
3212
|
+
const prevStart = blockStart - prevNode.nodeSize;
|
|
3213
|
+
editor.chain().focus().command(({ tr }) => {
|
|
3214
|
+
const currentSlice = state.doc.slice(blockStart, blockStart + node.nodeSize);
|
|
3215
|
+
tr.delete(blockStart, blockStart + node.nodeSize);
|
|
3216
|
+
tr.insert(prevStart, currentSlice.content);
|
|
3217
|
+
return true;
|
|
3218
|
+
}).run();
|
|
3219
|
+
});
|
|
3220
|
+
}, [editor, executeAndClose]);
|
|
3221
|
+
const handleMoveDown = (0, import_react32.useCallback)(() => {
|
|
3222
|
+
executeAndClose(() => {
|
|
3223
|
+
const { state } = editor;
|
|
3224
|
+
const { selection } = state;
|
|
3225
|
+
const { $anchor } = selection;
|
|
3226
|
+
const depth = $anchor.depth > 0 ? 1 : 0;
|
|
3227
|
+
const blockStart = $anchor.start(depth) - 1;
|
|
3228
|
+
const node = state.doc.nodeAt(blockStart);
|
|
3229
|
+
if (!node) return;
|
|
3230
|
+
const blockEnd = blockStart + node.nodeSize;
|
|
3231
|
+
const $pos = state.doc.resolve(blockStart);
|
|
3232
|
+
const parent = $pos.node($pos.depth);
|
|
3233
|
+
const index = $pos.index($pos.depth);
|
|
3234
|
+
if (index >= parent.childCount - 1) return;
|
|
3235
|
+
const nextNode = parent.child(index + 1);
|
|
3236
|
+
const nextEnd = blockEnd + nextNode.nodeSize;
|
|
3237
|
+
editor.chain().focus().command(({ tr }) => {
|
|
3238
|
+
const currentSlice = state.doc.slice(blockStart, blockEnd);
|
|
3239
|
+
tr.delete(blockStart, blockEnd);
|
|
3240
|
+
const insertPos = blockStart + nextNode.nodeSize;
|
|
3241
|
+
tr.insert(insertPos, currentSlice.content);
|
|
3242
|
+
return true;
|
|
3243
|
+
}).run();
|
|
3244
|
+
});
|
|
3245
|
+
}, [editor, executeAndClose]);
|
|
3246
|
+
const handleCopyToClipboard = (0, import_react32.useCallback)(() => {
|
|
3247
|
+
executeAndClose(() => {
|
|
3248
|
+
const { state } = editor;
|
|
3249
|
+
const { selection } = state;
|
|
3250
|
+
const { $anchor } = selection;
|
|
3251
|
+
const depth = $anchor.depth > 0 ? 1 : 0;
|
|
3252
|
+
const start = $anchor.start(depth) - 1;
|
|
3253
|
+
const node = state.doc.nodeAt(start);
|
|
3254
|
+
if (node) {
|
|
3255
|
+
const text = node.textContent;
|
|
3256
|
+
navigator.clipboard.writeText(text).catch(() => {
|
|
3257
|
+
});
|
|
3258
|
+
}
|
|
3259
|
+
});
|
|
3260
|
+
}, [editor, executeAndClose]);
|
|
3261
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "nph-block-action-menu nph-command", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "nph-command__list", style: { maxHeight: "none" }, children: [
|
|
3262
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
3263
|
+
"button",
|
|
2875
3264
|
{
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
{
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
3265
|
+
type: "button",
|
|
3266
|
+
className: "nph-command__item",
|
|
3267
|
+
onClick: handleDelete,
|
|
3268
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3269
|
+
children: [
|
|
3270
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_icons_react11.IconTrash, { size: 16 }),
|
|
3271
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "Delete" })
|
|
3272
|
+
]
|
|
3273
|
+
}
|
|
3274
|
+
),
|
|
3275
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
3276
|
+
"button",
|
|
3277
|
+
{
|
|
3278
|
+
type: "button",
|
|
3279
|
+
className: "nph-command__item",
|
|
3280
|
+
onClick: handleDuplicate,
|
|
3281
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3282
|
+
children: [
|
|
3283
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_icons_react11.IconCopy, { size: 16 }),
|
|
3284
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "Duplicate" })
|
|
3285
|
+
]
|
|
3286
|
+
}
|
|
3287
|
+
),
|
|
3288
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
3289
|
+
"button",
|
|
3290
|
+
{
|
|
3291
|
+
type: "button",
|
|
3292
|
+
className: "nph-command__item",
|
|
3293
|
+
onClick: handleCopyToClipboard,
|
|
3294
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3295
|
+
children: [
|
|
3296
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_icons_react11.IconClipboard, { size: 16 }),
|
|
3297
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "Copy to clipboard" })
|
|
3298
|
+
]
|
|
3299
|
+
}
|
|
3300
|
+
),
|
|
3301
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
3302
|
+
"button",
|
|
3303
|
+
{
|
|
3304
|
+
type: "button",
|
|
3305
|
+
className: "nph-command__item",
|
|
3306
|
+
onClick: handleMoveUp,
|
|
3307
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3308
|
+
children: [
|
|
3309
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_icons_react11.IconArrowUp, { size: 16 }),
|
|
3310
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "Move up" })
|
|
3311
|
+
]
|
|
3312
|
+
}
|
|
3313
|
+
),
|
|
3314
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
3315
|
+
"button",
|
|
3316
|
+
{
|
|
3317
|
+
type: "button",
|
|
3318
|
+
className: "nph-command__item",
|
|
3319
|
+
onClick: handleMoveDown,
|
|
3320
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3321
|
+
children: [
|
|
3322
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_icons_react11.IconArrowDown, { size: 16 }),
|
|
3323
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "Move down" })
|
|
3324
|
+
]
|
|
3325
|
+
}
|
|
3326
|
+
)
|
|
3327
|
+
] }) });
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
// src/react/menus/TableMenu.tsx
|
|
3331
|
+
var import_react33 = require("@tiptap/react");
|
|
3332
|
+
var import_icons_react12 = require("@tabler/icons-react");
|
|
3333
|
+
var import_react34 = require("react");
|
|
3334
|
+
var import_react_dom2 = require("react-dom");
|
|
3335
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
3336
|
+
function TableMenu({ className: _className }) {
|
|
3337
|
+
const { editor } = (0, import_react33.useCurrentEditor)();
|
|
3338
|
+
const tableInfo = (0, import_react33.useEditorState)({
|
|
3339
|
+
editor,
|
|
3340
|
+
selector: (ctx) => {
|
|
3341
|
+
if (!ctx.editor) return null;
|
|
3342
|
+
const { $from } = ctx.editor.state.selection;
|
|
3343
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
3344
|
+
if ($from.node(d).type.name === "table") {
|
|
3345
|
+
return { pos: $from.start(d) - 1, depth: d };
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3348
|
+
return null;
|
|
3349
|
+
}
|
|
3350
|
+
});
|
|
3351
|
+
const [colGrips, setColGrips] = (0, import_react34.useState)([]);
|
|
3352
|
+
const [rowGrips, setRowGrips] = (0, import_react34.useState)([]);
|
|
3353
|
+
const [dropdown, setDropdown] = (0, import_react34.useState)(null);
|
|
3354
|
+
const [tableRect, setTableRect] = (0, import_react34.useState)(null);
|
|
3355
|
+
const [isHovering, setIsHovering] = (0, import_react34.useState)(false);
|
|
3356
|
+
const [drag, setDrag] = (0, import_react34.useState)(null);
|
|
3357
|
+
const dropdownRef = (0, import_react34.useRef)(null);
|
|
3358
|
+
const dragRef = (0, import_react34.useRef)(null);
|
|
3359
|
+
const getTableDom = (0, import_react34.useCallback)(() => {
|
|
3360
|
+
if (!editor || !tableInfo) return null;
|
|
3361
|
+
return editor.view.nodeDOM(tableInfo.pos);
|
|
3362
|
+
}, [editor, tableInfo]);
|
|
3363
|
+
const measureGrips = (0, import_react34.useCallback)(() => {
|
|
3364
|
+
const tableDom = getTableDom();
|
|
3365
|
+
if (!tableDom) {
|
|
3366
|
+
setColGrips([]);
|
|
3367
|
+
setRowGrips([]);
|
|
3368
|
+
setTableRect(null);
|
|
3369
|
+
return;
|
|
3370
|
+
}
|
|
3371
|
+
const rect = tableDom.getBoundingClientRect();
|
|
3372
|
+
setTableRect(rect);
|
|
3373
|
+
const firstRow = tableDom.querySelector("tr");
|
|
3374
|
+
if (!firstRow) return;
|
|
3375
|
+
const cells = firstRow.querySelectorAll("th, td");
|
|
3376
|
+
const newColGrips = [];
|
|
3377
|
+
cells.forEach((cell) => {
|
|
3378
|
+
const cellRect = cell.getBoundingClientRect();
|
|
3379
|
+
newColGrips.push({
|
|
3380
|
+
left: cellRect.left,
|
|
3381
|
+
top: rect.top,
|
|
3382
|
+
width: cellRect.width,
|
|
3383
|
+
height: 0
|
|
3384
|
+
});
|
|
3385
|
+
});
|
|
3386
|
+
setColGrips(newColGrips);
|
|
3387
|
+
const rows = tableDom.querySelectorAll("tr");
|
|
3388
|
+
const newRowGrips = [];
|
|
3389
|
+
rows.forEach((row) => {
|
|
3390
|
+
const rowRect = row.getBoundingClientRect();
|
|
3391
|
+
newRowGrips.push({
|
|
3392
|
+
left: rect.left,
|
|
3393
|
+
top: rowRect.top,
|
|
3394
|
+
width: 0,
|
|
3395
|
+
height: rowRect.height
|
|
3396
|
+
});
|
|
3397
|
+
});
|
|
3398
|
+
setRowGrips(newRowGrips);
|
|
3399
|
+
}, [getTableDom]);
|
|
3400
|
+
(0, import_react34.useEffect)(() => {
|
|
3401
|
+
if (!tableInfo) {
|
|
3402
|
+
setColGrips([]);
|
|
3403
|
+
setRowGrips([]);
|
|
3404
|
+
setTableRect(null);
|
|
3405
|
+
setDropdown(null);
|
|
3406
|
+
return;
|
|
3407
|
+
}
|
|
3408
|
+
measureGrips();
|
|
3409
|
+
const tableDom = getTableDom();
|
|
3410
|
+
if (!tableDom) return;
|
|
3411
|
+
const ro = new ResizeObserver(() => measureGrips());
|
|
3412
|
+
ro.observe(tableDom);
|
|
3413
|
+
const HOVER_PAD = 32;
|
|
3414
|
+
const handleMouseMove = (e) => {
|
|
3415
|
+
const r = tableDom.getBoundingClientRect();
|
|
3416
|
+
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;
|
|
3417
|
+
setIsHovering(inside);
|
|
3418
|
+
};
|
|
3419
|
+
document.addEventListener("mousemove", handleMouseMove, { passive: true });
|
|
3420
|
+
const scrollParent = tableDom.closest(".nph-editor") || window;
|
|
3421
|
+
const handleScroll = () => {
|
|
3422
|
+
measureGrips();
|
|
3423
|
+
setDropdown(null);
|
|
3424
|
+
};
|
|
3425
|
+
scrollParent.addEventListener("scroll", handleScroll, { passive: true });
|
|
3426
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
3427
|
+
return () => {
|
|
3428
|
+
ro.disconnect();
|
|
3429
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
3430
|
+
scrollParent.removeEventListener("scroll", handleScroll);
|
|
3431
|
+
window.removeEventListener("scroll", handleScroll);
|
|
3432
|
+
};
|
|
3433
|
+
}, [tableInfo, getTableDom, measureGrips]);
|
|
3434
|
+
(0, import_react34.useEffect)(() => {
|
|
3435
|
+
if (!dropdown) return;
|
|
3436
|
+
const handlePointerDown = (e) => {
|
|
3437
|
+
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
3438
|
+
setDropdown(null);
|
|
3439
|
+
}
|
|
3440
|
+
};
|
|
3441
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
3442
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown);
|
|
3443
|
+
}, [dropdown]);
|
|
3444
|
+
(0, import_react34.useEffect)(() => {
|
|
3445
|
+
if (!editor || !dropdown) return;
|
|
3446
|
+
const handleTransaction = () => setDropdown(null);
|
|
3447
|
+
editor.on("transaction", handleTransaction);
|
|
3448
|
+
return () => {
|
|
3449
|
+
editor.off("transaction", handleTransaction);
|
|
3450
|
+
};
|
|
3451
|
+
}, [editor, dropdown]);
|
|
3452
|
+
const handleColGripClick = (0, import_react34.useCallback)(
|
|
3453
|
+
(index, e) => {
|
|
3454
|
+
e.preventDefault();
|
|
3455
|
+
e.stopPropagation();
|
|
3456
|
+
if (!editor || !tableInfo) return;
|
|
3457
|
+
const tableDom = getTableDom();
|
|
3458
|
+
if (!tableDom) return;
|
|
3459
|
+
const firstRow = tableDom.querySelector("tr");
|
|
3460
|
+
if (!firstRow) return;
|
|
3461
|
+
const cells = firstRow.querySelectorAll("th, td");
|
|
3462
|
+
const cell = cells[index];
|
|
3463
|
+
if (!cell) return;
|
|
3464
|
+
const pos = editor.view.posAtDOM(cell, 0);
|
|
3465
|
+
editor.chain().focus().setTextSelection(pos).run();
|
|
3466
|
+
const rect = cell.getBoundingClientRect();
|
|
3467
|
+
setDropdown({
|
|
3468
|
+
type: "column",
|
|
3469
|
+
index,
|
|
3470
|
+
x: rect.left,
|
|
3471
|
+
y: rect.top - 4
|
|
3472
|
+
});
|
|
3473
|
+
},
|
|
3474
|
+
[editor, tableInfo, getTableDom]
|
|
3475
|
+
);
|
|
3476
|
+
const handleRowGripClick = (0, import_react34.useCallback)(
|
|
3477
|
+
(index, e) => {
|
|
3478
|
+
e.preventDefault();
|
|
3479
|
+
e.stopPropagation();
|
|
3480
|
+
if (!editor || !tableInfo) return;
|
|
3481
|
+
const tableDom = getTableDom();
|
|
3482
|
+
if (!tableDom) return;
|
|
3483
|
+
const rows = tableDom.querySelectorAll("tr");
|
|
3484
|
+
const row = rows[index];
|
|
3485
|
+
if (!row) return;
|
|
3486
|
+
const firstCell = row.querySelector("th, td");
|
|
3487
|
+
if (!firstCell) return;
|
|
3488
|
+
const pos = editor.view.posAtDOM(firstCell, 0);
|
|
3489
|
+
editor.chain().focus().setTextSelection(pos).run();
|
|
3490
|
+
const gripEl = e.currentTarget;
|
|
3491
|
+
const gripRect = gripEl.getBoundingClientRect();
|
|
3492
|
+
setDropdown({
|
|
3493
|
+
type: "row",
|
|
3494
|
+
index,
|
|
3495
|
+
x: gripRect.left,
|
|
3496
|
+
y: gripRect.bottom + 4
|
|
3497
|
+
});
|
|
3498
|
+
},
|
|
3499
|
+
[editor, tableInfo, getTableDom]
|
|
3500
|
+
);
|
|
3501
|
+
const moveColumn = (0, import_react34.useCallback)(
|
|
3502
|
+
(from, to) => {
|
|
3503
|
+
if (!editor || !tableInfo || from === to) return;
|
|
3504
|
+
const { state } = editor;
|
|
3505
|
+
const tableStart = tableInfo.pos;
|
|
3506
|
+
const tableNode = state.doc.nodeAt(tableStart);
|
|
3507
|
+
if (!tableNode) return;
|
|
3508
|
+
const tr = state.tr;
|
|
3509
|
+
tableNode.forEach((row, rowOffset) => {
|
|
3510
|
+
if (row.type.name !== "tableRow") return;
|
|
3511
|
+
const cells = [];
|
|
3512
|
+
row.forEach((cell, cellOffset) => {
|
|
3513
|
+
cells.push({ node: cell, pos: tableStart + 1 + rowOffset + 1 + cellOffset });
|
|
3514
|
+
});
|
|
3515
|
+
if (from >= cells.length || to >= cells.length) return;
|
|
3516
|
+
const reordered = [...cells];
|
|
3517
|
+
const [moved] = reordered.splice(from, 1);
|
|
3518
|
+
reordered.splice(to, 0, moved);
|
|
3519
|
+
const rowPos = tableStart + 1 + rowOffset;
|
|
3520
|
+
const mappedRowPos = tr.mapping.map(rowPos);
|
|
3521
|
+
const newRow = row.type.create(row.attrs, reordered.map((c) => c.node));
|
|
3522
|
+
tr.replaceWith(mappedRowPos, mappedRowPos + row.nodeSize, newRow);
|
|
3523
|
+
});
|
|
3524
|
+
editor.view.dispatch(tr);
|
|
3525
|
+
},
|
|
3526
|
+
[editor, tableInfo]
|
|
3527
|
+
);
|
|
3528
|
+
const moveRow = (0, import_react34.useCallback)(
|
|
3529
|
+
(from, to) => {
|
|
3530
|
+
if (!editor || !tableInfo || from === to) return;
|
|
3531
|
+
const { state } = editor;
|
|
3532
|
+
const tableStart = tableInfo.pos;
|
|
3533
|
+
const tableNode = state.doc.nodeAt(tableStart);
|
|
3534
|
+
if (!tableNode) return;
|
|
3535
|
+
const rows = [];
|
|
3536
|
+
tableNode.forEach((row) => rows.push(row));
|
|
3537
|
+
if (from >= rows.length || to >= rows.length) return;
|
|
3538
|
+
const reordered = [...rows];
|
|
3539
|
+
const [moved] = reordered.splice(from, 1);
|
|
3540
|
+
reordered.splice(to, 0, moved);
|
|
3541
|
+
const tr = state.tr;
|
|
3542
|
+
tr.replaceWith(
|
|
3543
|
+
tableStart + 1,
|
|
3544
|
+
tableStart + 1 + tableNode.content.size,
|
|
3545
|
+
reordered
|
|
3546
|
+
);
|
|
3547
|
+
editor.view.dispatch(tr);
|
|
3548
|
+
},
|
|
3549
|
+
[editor, tableInfo]
|
|
3550
|
+
);
|
|
3551
|
+
const handleGripDragStart = (0, import_react34.useCallback)(
|
|
3552
|
+
(type, index, e) => {
|
|
3553
|
+
e.preventDefault();
|
|
3554
|
+
e.stopPropagation();
|
|
3555
|
+
setDropdown(null);
|
|
3556
|
+
const startX = e.clientX;
|
|
3557
|
+
const startY = e.clientY;
|
|
3558
|
+
let hasMoved = false;
|
|
3559
|
+
const dragState = { type, fromIndex: index, toIndex: index };
|
|
3560
|
+
dragRef.current = dragState;
|
|
3561
|
+
setDrag(dragState);
|
|
3562
|
+
const handleMouseMove = (ev) => {
|
|
3563
|
+
const dx = ev.clientX - startX;
|
|
3564
|
+
const dy = ev.clientY - startY;
|
|
3565
|
+
if (!hasMoved && Math.abs(type === "column" ? dx : dy) < 5) return;
|
|
3566
|
+
hasMoved = true;
|
|
3567
|
+
let newIndex = index;
|
|
3568
|
+
if (type === "column") {
|
|
3569
|
+
for (let i = 0; i < colGrips.length; i++) {
|
|
3570
|
+
const mid = colGrips[i].left + colGrips[i].width / 2;
|
|
3571
|
+
if (ev.clientX < mid) {
|
|
3572
|
+
newIndex = i;
|
|
3573
|
+
break;
|
|
3574
|
+
}
|
|
3575
|
+
newIndex = i;
|
|
3576
|
+
}
|
|
3577
|
+
} else {
|
|
3578
|
+
for (let i = 0; i < rowGrips.length; i++) {
|
|
3579
|
+
const mid = rowGrips[i].top + rowGrips[i].height / 2;
|
|
3580
|
+
if (ev.clientY < mid) {
|
|
3581
|
+
newIndex = i;
|
|
3582
|
+
break;
|
|
3583
|
+
}
|
|
3584
|
+
newIndex = i;
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
const updated = { type, fromIndex: index, toIndex: newIndex };
|
|
3588
|
+
dragRef.current = updated;
|
|
3589
|
+
setDrag(updated);
|
|
3590
|
+
};
|
|
3591
|
+
const handleMouseUp = () => {
|
|
3592
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
3593
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
3594
|
+
const finalDrag = dragRef.current;
|
|
3595
|
+
dragRef.current = null;
|
|
3596
|
+
setDrag(null);
|
|
3597
|
+
if (finalDrag && hasMoved && finalDrag.fromIndex !== finalDrag.toIndex) {
|
|
3598
|
+
if (type === "column") {
|
|
3599
|
+
moveColumn(finalDrag.fromIndex, finalDrag.toIndex);
|
|
3600
|
+
} else {
|
|
3601
|
+
moveRow(finalDrag.fromIndex, finalDrag.toIndex);
|
|
2886
3602
|
}
|
|
2887
|
-
|
|
3603
|
+
}
|
|
3604
|
+
};
|
|
3605
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
3606
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
3607
|
+
},
|
|
3608
|
+
[colGrips, rowGrips, moveColumn, moveRow]
|
|
3609
|
+
);
|
|
3610
|
+
if (!editor || !tableInfo || colGrips.length === 0) return null;
|
|
3611
|
+
const GRIP_SIZE = 20;
|
|
3612
|
+
const GRIP_GAP = 4;
|
|
3613
|
+
const gripsVisible = isHovering || !!dropdown;
|
|
3614
|
+
const columnDropdownItems = [
|
|
3615
|
+
{
|
|
3616
|
+
label: "Toggle header column",
|
|
3617
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconTableColumn, { size: 16 }),
|
|
3618
|
+
action: () => {
|
|
3619
|
+
editor.chain().focus().toggleHeaderColumn().run();
|
|
3620
|
+
setDropdown(null);
|
|
2888
3621
|
}
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
}
|
|
3622
|
+
},
|
|
3623
|
+
{
|
|
3624
|
+
label: "Insert column before",
|
|
3625
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconColumnInsertLeft, { size: 16 }),
|
|
3626
|
+
action: () => {
|
|
3627
|
+
editor.chain().focus().addColumnBefore().run();
|
|
3628
|
+
setDropdown(null);
|
|
3629
|
+
},
|
|
3630
|
+
separator: true
|
|
3631
|
+
},
|
|
3632
|
+
{
|
|
3633
|
+
label: "Insert column after",
|
|
3634
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconColumnInsertRight, { size: 16 }),
|
|
3635
|
+
action: () => {
|
|
3636
|
+
editor.chain().focus().addColumnAfter().run();
|
|
3637
|
+
setDropdown(null);
|
|
3638
|
+
}
|
|
3639
|
+
},
|
|
3640
|
+
{
|
|
3641
|
+
label: "Merge cells",
|
|
3642
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconArrowMerge, { size: 16 }),
|
|
3643
|
+
action: () => {
|
|
3644
|
+
editor.chain().focus().mergeCells().run();
|
|
3645
|
+
setDropdown(null);
|
|
3646
|
+
},
|
|
3647
|
+
separator: true
|
|
3648
|
+
},
|
|
3649
|
+
{
|
|
3650
|
+
label: "Split cell",
|
|
3651
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconArrowsSplit, { size: 16 }),
|
|
3652
|
+
action: () => {
|
|
3653
|
+
editor.chain().focus().splitCell().run();
|
|
3654
|
+
setDropdown(null);
|
|
3655
|
+
}
|
|
3656
|
+
},
|
|
3657
|
+
{
|
|
3658
|
+
label: "Delete column",
|
|
3659
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconColumnRemove, { size: 16 }),
|
|
3660
|
+
action: () => {
|
|
3661
|
+
editor.chain().focus().deleteColumn().run();
|
|
3662
|
+
setDropdown(null);
|
|
3663
|
+
},
|
|
3664
|
+
destructive: true,
|
|
3665
|
+
separator: true
|
|
3666
|
+
}
|
|
3667
|
+
];
|
|
3668
|
+
const rowDropdownItems = [
|
|
3669
|
+
{
|
|
3670
|
+
label: "Toggle header row",
|
|
3671
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconTableRow, { size: 16 }),
|
|
3672
|
+
action: () => {
|
|
3673
|
+
editor.chain().focus().toggleHeaderRow().run();
|
|
3674
|
+
setDropdown(null);
|
|
3675
|
+
}
|
|
3676
|
+
},
|
|
3677
|
+
{
|
|
3678
|
+
label: "Insert row above",
|
|
3679
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconRowInsertTop, { size: 16 }),
|
|
3680
|
+
action: () => {
|
|
3681
|
+
editor.chain().focus().addRowBefore().run();
|
|
3682
|
+
setDropdown(null);
|
|
3683
|
+
},
|
|
3684
|
+
separator: true
|
|
3685
|
+
},
|
|
3686
|
+
{
|
|
3687
|
+
label: "Insert row below",
|
|
3688
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconRowInsertBottom, { size: 16 }),
|
|
3689
|
+
action: () => {
|
|
3690
|
+
editor.chain().focus().addRowAfter().run();
|
|
3691
|
+
setDropdown(null);
|
|
3692
|
+
}
|
|
3693
|
+
},
|
|
3694
|
+
{
|
|
3695
|
+
label: "Merge cells",
|
|
3696
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconArrowMerge, { size: 16 }),
|
|
3697
|
+
action: () => {
|
|
3698
|
+
editor.chain().focus().mergeCells().run();
|
|
3699
|
+
setDropdown(null);
|
|
3700
|
+
},
|
|
3701
|
+
separator: true
|
|
3702
|
+
},
|
|
3703
|
+
{
|
|
3704
|
+
label: "Split cell",
|
|
3705
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconArrowsSplit, { size: 16 }),
|
|
3706
|
+
action: () => {
|
|
3707
|
+
editor.chain().focus().splitCell().run();
|
|
3708
|
+
setDropdown(null);
|
|
3709
|
+
}
|
|
3710
|
+
},
|
|
3711
|
+
{
|
|
3712
|
+
label: "Delete row",
|
|
3713
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconRowRemove, { size: 16 }),
|
|
3714
|
+
action: () => {
|
|
3715
|
+
editor.chain().focus().deleteRow().run();
|
|
3716
|
+
setDropdown(null);
|
|
3717
|
+
},
|
|
3718
|
+
destructive: true,
|
|
3719
|
+
separator: true
|
|
3720
|
+
}
|
|
3721
|
+
];
|
|
3722
|
+
const dropdownItems = dropdown?.type === "column" ? columnDropdownItems : rowDropdownItems;
|
|
3723
|
+
return (0, import_react_dom2.createPortal)(
|
|
3724
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3725
|
+
colGrips.map((grip, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3726
|
+
"button",
|
|
3727
|
+
{
|
|
3728
|
+
type: "button",
|
|
3729
|
+
className: `nph-table-grip nph-table-grip--col${gripsVisible ? " nph-table-grip--visible" : ""}${drag?.type === "column" && drag.fromIndex === i ? " nph-table-grip--dragging" : ""}`,
|
|
3730
|
+
style: {
|
|
3731
|
+
position: "fixed",
|
|
3732
|
+
left: grip.left + grip.width / 2 - GRIP_SIZE / 2,
|
|
3733
|
+
top: grip.top - GRIP_SIZE - GRIP_GAP,
|
|
3734
|
+
width: GRIP_SIZE,
|
|
3735
|
+
height: GRIP_SIZE,
|
|
3736
|
+
cursor: "grab"
|
|
3737
|
+
},
|
|
3738
|
+
onMouseDown: (e) => handleGripDragStart("column", i, e),
|
|
3739
|
+
onClick: (e) => handleColGripClick(i, e),
|
|
3740
|
+
"aria-label": `Column ${i + 1} options`,
|
|
3741
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconGripHorizontal, { size: 14 })
|
|
3742
|
+
},
|
|
3743
|
+
`col-${i}`
|
|
3744
|
+
)),
|
|
3745
|
+
rowGrips.map((grip, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3746
|
+
"button",
|
|
3747
|
+
{
|
|
3748
|
+
type: "button",
|
|
3749
|
+
className: `nph-table-grip nph-table-grip--row${gripsVisible ? " nph-table-grip--visible" : ""}${drag?.type === "row" && drag.fromIndex === i ? " nph-table-grip--dragging" : ""}`,
|
|
3750
|
+
style: {
|
|
3751
|
+
position: "fixed",
|
|
3752
|
+
left: grip.left - GRIP_SIZE - GRIP_GAP,
|
|
3753
|
+
top: grip.top + grip.height / 2 - GRIP_SIZE / 2,
|
|
3754
|
+
width: GRIP_SIZE,
|
|
3755
|
+
height: GRIP_SIZE,
|
|
3756
|
+
cursor: "grab"
|
|
3757
|
+
},
|
|
3758
|
+
onMouseDown: (e) => handleGripDragStart("row", i, e),
|
|
3759
|
+
onClick: (e) => handleRowGripClick(i, e),
|
|
3760
|
+
"aria-label": `Row ${i + 1} options`,
|
|
3761
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconGripVertical, { size: 14 })
|
|
3762
|
+
},
|
|
3763
|
+
`row-${i}`
|
|
3764
|
+
)),
|
|
3765
|
+
tableRect && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3766
|
+
"button",
|
|
3767
|
+
{
|
|
3768
|
+
type: "button",
|
|
3769
|
+
className: `nph-table-grip nph-table-grip--delete${gripsVisible ? " nph-table-grip--visible" : ""}`,
|
|
3770
|
+
style: {
|
|
3771
|
+
position: "fixed",
|
|
3772
|
+
left: tableRect.left + tableRect.width / 2 - 60,
|
|
3773
|
+
top: tableRect.bottom + GRIP_GAP,
|
|
3774
|
+
width: 120,
|
|
3775
|
+
height: 24
|
|
3776
|
+
},
|
|
3777
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3778
|
+
onClick: () => {
|
|
3779
|
+
editor.chain().focus().deleteTable().run();
|
|
3780
|
+
},
|
|
3781
|
+
"aria-label": "Delete table",
|
|
3782
|
+
children: [
|
|
3783
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_icons_react12.IconTableOff, { size: 14 }),
|
|
3784
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { fontSize: 12 }, children: "Delete table" })
|
|
3785
|
+
]
|
|
3786
|
+
}
|
|
3787
|
+
),
|
|
3788
|
+
dropdown && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3789
|
+
"div",
|
|
3790
|
+
{
|
|
3791
|
+
ref: dropdownRef,
|
|
3792
|
+
className: "nph-table-dropdown",
|
|
3793
|
+
style: {
|
|
3794
|
+
position: "fixed",
|
|
3795
|
+
left: dropdown.x,
|
|
3796
|
+
top: dropdown.y,
|
|
3797
|
+
transform: dropdown.type === "column" ? "translateY(-100%)" : void 0
|
|
3798
|
+
},
|
|
3799
|
+
children: dropdownItems.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
|
|
3800
|
+
item.separator && i > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "nph-table-dropdown__separator" }),
|
|
3801
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3802
|
+
"button",
|
|
3803
|
+
{
|
|
3804
|
+
type: "button",
|
|
3805
|
+
className: `nph-table-dropdown__item ${item.destructive ? "nph-table-dropdown__item--destructive" : ""}`,
|
|
3806
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
3807
|
+
onClick: item.action,
|
|
3808
|
+
children: [
|
|
3809
|
+
item.icon,
|
|
3810
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: item.label })
|
|
3811
|
+
]
|
|
3812
|
+
}
|
|
3813
|
+
)
|
|
3814
|
+
] }, i))
|
|
3815
|
+
}
|
|
3816
|
+
),
|
|
3817
|
+
drag && drag.fromIndex !== drag.toIndex && tableRect && (drag.type === "column" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3818
|
+
"div",
|
|
3819
|
+
{
|
|
3820
|
+
className: "nph-table-drop-indicator nph-table-drop-indicator--col",
|
|
3821
|
+
style: {
|
|
3822
|
+
position: "fixed",
|
|
3823
|
+
left: drag.toIndex < colGrips.length ? colGrips[drag.toIndex].left - 1 : colGrips[colGrips.length - 1].left + colGrips[colGrips.length - 1].width,
|
|
3824
|
+
top: tableRect.top,
|
|
3825
|
+
width: 2,
|
|
3826
|
+
height: tableRect.height
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3830
|
+
"div",
|
|
3831
|
+
{
|
|
3832
|
+
className: "nph-table-drop-indicator nph-table-drop-indicator--row",
|
|
3833
|
+
style: {
|
|
3834
|
+
position: "fixed",
|
|
3835
|
+
left: tableRect.left,
|
|
3836
|
+
top: drag.toIndex < rowGrips.length ? rowGrips[drag.toIndex].top - 1 : rowGrips[rowGrips.length - 1].top + rowGrips[rowGrips.length - 1].height,
|
|
3837
|
+
width: tableRect.width,
|
|
3838
|
+
height: 2
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
))
|
|
3842
|
+
] }),
|
|
3843
|
+
document.body
|
|
3844
|
+
);
|
|
3845
|
+
}
|
|
2893
3846
|
|
|
2894
3847
|
// src/react/Editor.tsx
|
|
2895
|
-
var
|
|
2896
|
-
|
|
3848
|
+
var import_react35 = require("react");
|
|
3849
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
3850
|
+
function Editor5({
|
|
2897
3851
|
content,
|
|
2898
3852
|
className,
|
|
2899
3853
|
editable = true,
|
|
2900
3854
|
immediatelyRender = false,
|
|
2901
3855
|
showTextMenu = true,
|
|
2902
3856
|
showSlashMenu = true,
|
|
2903
|
-
showImageMenu =
|
|
3857
|
+
showImageMenu = false,
|
|
3858
|
+
showDragHandle = true,
|
|
2904
3859
|
extensions,
|
|
2905
3860
|
bubbleMenuExtras,
|
|
2906
3861
|
onUpdate,
|
|
@@ -2912,6 +3867,41 @@ function Editor3({
|
|
|
2912
3867
|
slashCommand,
|
|
2913
3868
|
placeholder
|
|
2914
3869
|
}) {
|
|
3870
|
+
const [actionMenuAnchor, setActionMenuAnchor] = (0, import_react35.useState)(null);
|
|
3871
|
+
const [actionMenuEditor, setActionMenuEditor] = (0, import_react35.useState)(null);
|
|
3872
|
+
const actionMenuRef = (0, import_react35.useRef)(null);
|
|
3873
|
+
(0, import_react35.useEffect)(() => {
|
|
3874
|
+
if (!actionMenuAnchor) return;
|
|
3875
|
+
const handlePointerDown = (e) => {
|
|
3876
|
+
if (actionMenuRef.current && !actionMenuRef.current.contains(e.target)) {
|
|
3877
|
+
setActionMenuAnchor(null);
|
|
3878
|
+
}
|
|
3879
|
+
};
|
|
3880
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
3881
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown);
|
|
3882
|
+
}, [actionMenuAnchor]);
|
|
3883
|
+
const dragHandleCallbacks = (0, import_react35.useMemo)(
|
|
3884
|
+
() => ({
|
|
3885
|
+
onAddBlock: (editor) => {
|
|
3886
|
+
const { state } = editor;
|
|
3887
|
+
const { selection } = state;
|
|
3888
|
+
const { $anchor } = selection;
|
|
3889
|
+
const topDepth = Math.min($anchor.depth, 1);
|
|
3890
|
+
const endOfBlock = $anchor.end(topDepth);
|
|
3891
|
+
const insertPos = endOfBlock + 1;
|
|
3892
|
+
editor.chain().focus().insertContentAt(insertPos, { type: "paragraph" }).focus(insertPos + 1).run();
|
|
3893
|
+
requestAnimationFrame(() => {
|
|
3894
|
+
editor.commands.insertContent("/");
|
|
3895
|
+
});
|
|
3896
|
+
},
|
|
3897
|
+
onGripClick: (editor, _node, element) => {
|
|
3898
|
+
setActionMenuEditor(editor);
|
|
3899
|
+
setActionMenuAnchor((prev) => prev === element ? null : element);
|
|
3900
|
+
}
|
|
3901
|
+
}),
|
|
3902
|
+
[]
|
|
3903
|
+
);
|
|
3904
|
+
const enableDragHandle = showDragHandle && editable;
|
|
2915
3905
|
const normalizeExtras = (extras) => {
|
|
2916
3906
|
const result = {
|
|
2917
3907
|
start: [],
|
|
@@ -2928,59 +3918,185 @@ function Editor3({
|
|
|
2928
3918
|
};
|
|
2929
3919
|
const textExtras = normalizeExtras(bubbleMenuExtras?.text);
|
|
2930
3920
|
const imageExtras = normalizeExtras(bubbleMenuExtras?.image);
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
leadingExtras: textExtras.start,
|
|
2965
|
-
trailingExtras: textExtras.end
|
|
3921
|
+
const handleCloseActionMenu = (0, import_react35.useCallback)(() => {
|
|
3922
|
+
setActionMenuAnchor(null);
|
|
3923
|
+
}, []);
|
|
3924
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className, children: [
|
|
3925
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(EditorRoot, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
3926
|
+
EditorContent,
|
|
3927
|
+
{
|
|
3928
|
+
onUpdate,
|
|
3929
|
+
onCreate,
|
|
3930
|
+
immediatelyRender,
|
|
3931
|
+
editable,
|
|
3932
|
+
content,
|
|
3933
|
+
extensions: [
|
|
3934
|
+
...extension_kit_default({
|
|
3935
|
+
uploadImage,
|
|
3936
|
+
collaboration,
|
|
3937
|
+
imageBlockView: ImageBlockView,
|
|
3938
|
+
videoBlockView: VideoBlockView,
|
|
3939
|
+
mention: mentionOptions,
|
|
3940
|
+
reference: referenceOptions,
|
|
3941
|
+
slashCommand,
|
|
3942
|
+
dragHandle: enableDragHandle,
|
|
3943
|
+
dragHandleCallbacks: enableDragHandle ? dragHandleCallbacks : void 0,
|
|
3944
|
+
placeholder
|
|
3945
|
+
}),
|
|
3946
|
+
...extensions ?? []
|
|
3947
|
+
],
|
|
3948
|
+
editorProps: {
|
|
3949
|
+
attributes: {
|
|
3950
|
+
class: "nph-editor max-w-none outline-none"
|
|
3951
|
+
},
|
|
3952
|
+
handleKeyDown: (view, event) => {
|
|
3953
|
+
return !!handleCommandNavigation?.(event);
|
|
2966
3954
|
}
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
3955
|
+
},
|
|
3956
|
+
children: [
|
|
3957
|
+
showTextMenu ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
3958
|
+
TextMenu,
|
|
3959
|
+
{
|
|
3960
|
+
leadingExtras: textExtras.start,
|
|
3961
|
+
trailingExtras: textExtras.end
|
|
3962
|
+
}
|
|
3963
|
+
) : null,
|
|
3964
|
+
showImageMenu ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
3965
|
+
ImageMenu,
|
|
3966
|
+
{
|
|
3967
|
+
leadingExtras: imageExtras.start,
|
|
3968
|
+
trailingExtras: imageExtras.end
|
|
3969
|
+
}
|
|
3970
|
+
) : null,
|
|
3971
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LinkMenu, {}),
|
|
3972
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TableMenu, {}),
|
|
3973
|
+
showSlashMenu ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SlashMenu, {}) : null
|
|
3974
|
+
]
|
|
3975
|
+
}
|
|
3976
|
+
) }),
|
|
3977
|
+
actionMenuAnchor && actionMenuEditor && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
3978
|
+
"div",
|
|
3979
|
+
{
|
|
3980
|
+
ref: actionMenuRef,
|
|
3981
|
+
style: {
|
|
3982
|
+
position: "fixed",
|
|
3983
|
+
zIndex: 1e4,
|
|
3984
|
+
top: actionMenuAnchor.getBoundingClientRect().bottom + 4,
|
|
3985
|
+
left: actionMenuAnchor.getBoundingClientRect().left
|
|
3986
|
+
},
|
|
3987
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
3988
|
+
BlockActionMenu,
|
|
2970
3989
|
{
|
|
2971
|
-
|
|
2972
|
-
|
|
3990
|
+
editor: actionMenuEditor,
|
|
3991
|
+
onClose: handleCloseActionMenu
|
|
2973
3992
|
}
|
|
2974
|
-
)
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
3993
|
+
)
|
|
3994
|
+
}
|
|
3995
|
+
)
|
|
3996
|
+
] });
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3999
|
+
// src/react/TableOfContents.tsx
|
|
4000
|
+
var import_react36 = require("@tiptap/react");
|
|
4001
|
+
var import_react37 = require("react");
|
|
4002
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
4003
|
+
function TableOfContents({
|
|
4004
|
+
className,
|
|
4005
|
+
itemClassName,
|
|
4006
|
+
activeClassName
|
|
4007
|
+
}) {
|
|
4008
|
+
const { editor } = (0, import_react36.useCurrentEditor)();
|
|
4009
|
+
const [activeId, setActiveId] = (0, import_react37.useState)(null);
|
|
4010
|
+
const observerRef = (0, import_react37.useRef)(null);
|
|
4011
|
+
const headings = (0, import_react36.useEditorState)({
|
|
4012
|
+
editor,
|
|
4013
|
+
selector: (ctx) => {
|
|
4014
|
+
if (!ctx.editor) return [];
|
|
4015
|
+
const items = [];
|
|
4016
|
+
ctx.editor.state.doc.descendants((node, pos) => {
|
|
4017
|
+
if (node.type.name === "heading") {
|
|
4018
|
+
const id = `heading-${pos}`;
|
|
4019
|
+
items.push({
|
|
4020
|
+
id,
|
|
4021
|
+
level: node.attrs.level,
|
|
4022
|
+
text: node.textContent,
|
|
4023
|
+
pos
|
|
4024
|
+
});
|
|
4025
|
+
}
|
|
4026
|
+
});
|
|
4027
|
+
return items;
|
|
2978
4028
|
}
|
|
2979
|
-
|
|
4029
|
+
});
|
|
4030
|
+
(0, import_react37.useEffect)(() => {
|
|
4031
|
+
if (!editor || !headings || headings.length === 0) return;
|
|
4032
|
+
observerRef.current?.disconnect();
|
|
4033
|
+
const callback = (entries) => {
|
|
4034
|
+
const visibleEntries = entries.filter((e) => e.isIntersecting);
|
|
4035
|
+
if (visibleEntries.length > 0) {
|
|
4036
|
+
const firstVisible = visibleEntries[0];
|
|
4037
|
+
const id = firstVisible.target.getAttribute("data-toc-id");
|
|
4038
|
+
if (id) setActiveId(id);
|
|
4039
|
+
}
|
|
4040
|
+
};
|
|
4041
|
+
const observer = new IntersectionObserver(callback, {
|
|
4042
|
+
rootMargin: "-80px 0px -70% 0px",
|
|
4043
|
+
threshold: 0
|
|
4044
|
+
});
|
|
4045
|
+
observerRef.current = observer;
|
|
4046
|
+
const editorEl = editor.view.dom;
|
|
4047
|
+
headings.forEach((heading) => {
|
|
4048
|
+
try {
|
|
4049
|
+
const domNode = editor.view.nodeDOM(heading.pos);
|
|
4050
|
+
if (domNode && domNode instanceof HTMLElement) {
|
|
4051
|
+
domNode.setAttribute("data-toc-id", heading.id);
|
|
4052
|
+
observer.observe(domNode);
|
|
4053
|
+
}
|
|
4054
|
+
} catch {
|
|
4055
|
+
}
|
|
4056
|
+
});
|
|
4057
|
+
return () => {
|
|
4058
|
+
observer.disconnect();
|
|
4059
|
+
};
|
|
4060
|
+
}, [editor, headings]);
|
|
4061
|
+
const handleClick = (0, import_react37.useCallback)(
|
|
4062
|
+
(pos) => {
|
|
4063
|
+
if (!editor) return;
|
|
4064
|
+
editor.chain().focus().setTextSelection(pos + 1).run();
|
|
4065
|
+
try {
|
|
4066
|
+
const domNode = editor.view.nodeDOM(pos);
|
|
4067
|
+
if (domNode && domNode instanceof HTMLElement) {
|
|
4068
|
+
domNode.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
4069
|
+
}
|
|
4070
|
+
} catch {
|
|
4071
|
+
}
|
|
4072
|
+
},
|
|
4073
|
+
[editor]
|
|
4074
|
+
);
|
|
4075
|
+
if (!editor || !headings || headings.length === 0) return null;
|
|
4076
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("nav", { className: className ?? "nph-toc", children: headings.map((heading) => {
|
|
4077
|
+
const isActive = activeId === heading.id;
|
|
4078
|
+
const itemClass = [
|
|
4079
|
+
itemClassName ?? "nph-toc__item",
|
|
4080
|
+
isActive ? activeClassName ?? "nph-toc__item--active" : ""
|
|
4081
|
+
].filter(Boolean).join(" ");
|
|
4082
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
4083
|
+
"button",
|
|
4084
|
+
{
|
|
4085
|
+
type: "button",
|
|
4086
|
+
className: itemClass,
|
|
4087
|
+
style: { paddingLeft: `${(heading.level - 1) * 12 + 8}px` },
|
|
4088
|
+
onClick: () => handleClick(heading.pos),
|
|
4089
|
+
title: heading.text,
|
|
4090
|
+
children: heading.text || `Heading ${heading.level}`
|
|
4091
|
+
},
|
|
4092
|
+
heading.id
|
|
4093
|
+
);
|
|
4094
|
+
}) });
|
|
2980
4095
|
}
|
|
2981
4096
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2982
4097
|
0 && (module.exports = {
|
|
2983
4098
|
Editor,
|
|
4099
|
+
TableOfContents,
|
|
2984
4100
|
TextMenu
|
|
2985
4101
|
});
|
|
2986
4102
|
//# sourceMappingURL=index.cjs.map
|