dishui 0.0.83 → 0.0.89
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/components/Demo/index.js +2 -2
- package/dist/components/Demo/index.js.map +1 -1
- package/dist/components/Input/index.js +52 -62
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/LayoutSuite/index.d.ts +10 -0
- package/dist/components/LayoutSuite/index.js +27 -18
- package/dist/components/LayoutSuite/index.js.map +1 -1
- package/dist/components/Markdown/markdownComponents.js +1 -0
- package/dist/components/Markdown/markdownComponents.js.map +1 -1
- package/dist/components/Markdown/useMarkdownSource.js +46 -31
- package/dist/components/Markdown/useMarkdownSource.js.map +1 -1
- package/dist/components/Markdown/useMarkmapRenderer.js +44 -31
- package/dist/components/Markdown/useMarkmapRenderer.js.map +1 -1
- package/dist/components/Markdown/useMermaidRenderer.js +49 -31
- package/dist/components/Markdown/useMermaidRenderer.js.map +1 -1
- package/dist/components/Page/index.d.ts +18 -4
- package/dist/components/Page/index.js +47 -35
- package/dist/components/Page/index.js.map +1 -1
- package/dist/components/ProtectedRoute/index.d.ts +2 -1
- package/dist/components/ProtectedRoute/index.js +16 -6
- package/dist/components/ProtectedRoute/index.js.map +1 -1
- package/dist/components/Select/index.d.ts +6 -0
- package/dist/components/Select/index.js +166 -136
- package/dist/components/Select/index.js.map +1 -1
- package/dist/components/Tabs/index.d.ts +12 -1
- package/dist/components/Tabs/index.js +42 -17
- package/dist/components/Tabs/index.js.map +1 -1
- package/dist/components/Text/index.d.ts +1 -1
- package/dist/components/Text/index.js +2 -2
- package/dist/components/Text/index.js.map +1 -1
- package/dist/components/pro/ArticleContextMenu/index.js +7 -7
- package/dist/components/pro/CodeEditor/editors/CodeEditor.js +200 -189
- package/dist/components/pro/CodeEditor/editors/CodeEditor.js.map +1 -1
- package/dist/components/pro/CodeEditor/types.d.ts +3 -0
- package/dist/components/pro/DataTable/DataTable.d.ts +1 -1
- package/dist/components/pro/DataTable/DataTable.js +98 -240
- package/dist/components/pro/DataTable/DataTable.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableBody.js +14 -16
- package/dist/components/pro/DataTable/components/TableBody.js.map +1 -1
- package/dist/components/pro/DataTable/components/TablePagination.d.ts +3 -9
- package/dist/components/pro/DataTable/components/TablePagination.js +29 -25
- package/dist/components/pro/DataTable/components/TablePagination.js.map +1 -1
- package/dist/components/pro/DataTable/tanstack-table.d.ts +2 -2
- package/dist/components/pro/DataTable/tanstack-table.js +29 -21
- package/dist/components/pro/DataTable/tanstack-table.js.map +1 -1
- package/dist/components/pro/DataTable/treeDataUtils.d.ts +2 -0
- package/dist/components/pro/DataTable/treeDataUtils.js +18 -19
- package/dist/components/pro/DataTable/treeDataUtils.js.map +1 -1
- package/dist/components/pro/DataTable/types.d.ts +24 -0
- package/dist/components/pro/DataTable/useDataTableModel.d.ts +16 -0
- package/dist/components/pro/DataTable/useDataTableModel.js +148 -0
- package/dist/components/pro/DataTable/useDataTableModel.js.map +1 -0
- package/dist/components/pro/FileExplorer/Breadcrumb.js +12 -12
- package/dist/components/pro/Flow/nodes/DataNodeView.js +87 -82
- package/dist/components/pro/Flow/nodes/DataNodeView.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaContent.js +1 -1
- package/dist/components/pro/Flow/nodes/MediaContent.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaOverlay.js +1 -1
- package/dist/components/pro/Flow/nodes/MediaOverlay.js.map +1 -1
- package/dist/components/pro/Flow/nodes/Model3DView.js +69 -63
- package/dist/components/pro/Flow/nodes/Model3DView.js.map +1 -1
- package/dist/components/pro/Flow/nodes/chartGeometryBudget.d.ts +14 -0
- package/dist/components/pro/Flow/nodes/chartGeometryBudget.js +76 -0
- package/dist/components/pro/Flow/nodes/chartGeometryBudget.js.map +1 -0
- package/dist/components/pro/Flow/nodes/demandRender.d.ts +11 -0
- package/dist/components/pro/Flow/nodes/demandRender.js +26 -0
- package/dist/components/pro/Flow/nodes/demandRender.js.map +1 -0
- package/dist/components/pro/RichTextEditor/SlashMenu.js +68 -58
- package/dist/components/pro/RichTextEditor/SlashMenu.js.map +1 -1
- package/dist/contexts/AppContext.d.ts +1 -0
- package/dist/contexts/AppContext.js +144 -145
- package/dist/contexts/AppContext.js.map +1 -1
- package/dist/contexts/authentication.d.ts +7 -0
- package/dist/contexts/authentication.js +22 -0
- package/dist/contexts/authentication.js.map +1 -0
- package/dist/dishui.css +1 -1
- package/dist/hooks/useControllableState.d.ts +6 -14
- package/dist/hooks/useControllableState.js +21 -15
- package/dist/hooks/useControllableState.js.map +1 -1
- package/dist/hooks/useTableColumns.d.ts +3 -3
- package/dist/hooks/useTableColumns.js +2 -2
- package/dist/hooks/useTableColumns.js.map +1 -1
- package/dist/hooks/useTableData.d.ts +11 -4
- package/dist/hooks/useTableData.js +44 -26
- package/dist/hooks/useTableData.js.map +1 -1
- package/dist/index-nostyled.d.ts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +97 -97
- package/dist/layouts/Sidebar.d.ts +3 -1
- package/dist/layouts/Sidebar.js +131 -122
- package/dist/layouts/Sidebar.js.map +1 -1
- package/dist/utils/i18n.js +1 -1
- package/dist/utils/i18n.js.map +1 -1
- package/dist/utils/routerBuilder.d.ts +7 -5
- package/dist/utils/routerBuilder.js +11 -15
- package/dist/utils/routerBuilder.js.map +1 -1
- package/dist/utils/safeStorage.d.ts +8 -0
- package/dist/utils/safeStorage.js +45 -0
- package/dist/utils/safeStorage.js.map +1 -0
- package/package.json +22 -5
|
@@ -121,11 +121,11 @@ var c = [
|
|
|
121
121
|
action: (e) => e
|
|
122
122
|
}
|
|
123
123
|
], l = ({ editor: l }) => {
|
|
124
|
-
let [u, d] = i(!1), [f, p] = i(""
|
|
124
|
+
let [u, d] = i(!1), [f, p] = i(null), m = f?.query ?? "", [h, g] = i({
|
|
125
125
|
x: 0,
|
|
126
126
|
y: 0
|
|
127
|
-
}), [
|
|
128
|
-
if (l.isDestroyed || !l.isEditable || l.view.composing ||
|
|
127
|
+
}), [_, v] = i(0), y = r(null), b = r(null), x = r(null), S = r(!1), C = e(() => {
|
|
128
|
+
if (l.isDestroyed || !l.isEditable || l.view.composing || S.current) return null;
|
|
129
129
|
let e = l.state.selection;
|
|
130
130
|
if (!(e instanceof s) || !e.empty || e.$from.parent.type.spec.code || e.$from.marks().some((e) => e.type.spec.code)) return null;
|
|
131
131
|
let { $from: t } = e, n = t.parent.textBetween(0, t.parentOffset, "\n", (e) => e.type.name === "hardBreak" ? "\n" : ""), r = /(?:^|\s)\/([^\s/]*)$/.exec(n);
|
|
@@ -134,70 +134,80 @@ var c = [
|
|
|
134
134
|
to: t.pos,
|
|
135
135
|
query: r[1]
|
|
136
136
|
} : null;
|
|
137
|
-
}, [l]),
|
|
138
|
-
let t =
|
|
137
|
+
}, [l]), w = m ? c.filter((e) => {
|
|
138
|
+
let t = m.toLowerCase();
|
|
139
139
|
return e.label.toLowerCase().includes(t) || e.description.toLowerCase().includes(t) || e.keywords?.some((e) => e.includes(t));
|
|
140
|
-
}) : c,
|
|
141
|
-
d(!1), p(
|
|
142
|
-
let e =
|
|
143
|
-
e && (
|
|
144
|
-
}, []),
|
|
145
|
-
let t =
|
|
146
|
-
if (!t || !n || t.from !== n.from || t.to !== n.to || t.query !== n.query) {
|
|
147
|
-
|
|
140
|
+
}) : c, T = e(() => {
|
|
141
|
+
d(!1), p(null);
|
|
142
|
+
let e = b.current;
|
|
143
|
+
e && (x.current = `${e.from}:${e.to}:${e.query}`), b.current = null, v(0);
|
|
144
|
+
}, []), E = e((e) => {
|
|
145
|
+
let t = C(), n = b.current;
|
|
146
|
+
if (!t || !n || !f || t.from !== f.from || t.to !== f.to || t.query !== f.query || t.from !== n.from || t.to !== n.to || t.query !== n.query) {
|
|
147
|
+
T();
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
150
|
let r = e.label === "Image" ? window.prompt("Image URL:")?.trim() : void 0;
|
|
151
151
|
if (e.label === "Image" && !r) {
|
|
152
|
-
|
|
152
|
+
T();
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
|
-
|
|
155
|
+
T();
|
|
156
156
|
let i = l.chain().focus().deleteRange(t);
|
|
157
157
|
(r ? i.setImage({ src: r }) : e.action(i)).run();
|
|
158
158
|
}, [
|
|
159
159
|
l,
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
C,
|
|
161
|
+
T,
|
|
162
|
+
f
|
|
162
163
|
]);
|
|
163
164
|
return t(() => {
|
|
164
165
|
let e, t = () => {
|
|
165
|
-
let e = l.isFocused ?
|
|
166
|
+
let e = l.isFocused ? C() : null;
|
|
166
167
|
if (!e) {
|
|
167
|
-
|
|
168
|
+
b.current = null, x.current = null, d(!1);
|
|
168
169
|
return;
|
|
169
170
|
}
|
|
170
|
-
|
|
171
|
+
x.current !== `${e.from}:${e.to}:${e.query}` && ((b.current?.query !== e.query || b.current?.from !== e.from) && v(0), b.current = e, p(e), d(!0));
|
|
171
172
|
}, n = () => {
|
|
172
|
-
|
|
173
|
+
S.current = !0, T();
|
|
173
174
|
}, r = () => {
|
|
174
|
-
|
|
175
|
+
S.current = !1, x.current = null, e = setTimeout(t, 0);
|
|
175
176
|
}, i = l.view.dom;
|
|
176
|
-
return l.on("transaction", t), l.on("blur",
|
|
177
|
-
clearTimeout(e), l.off("transaction", t), l.off("blur",
|
|
177
|
+
return l.on("transaction", t), l.on("blur", T), i.addEventListener("compositionstart", n), i.addEventListener("compositionend", r), () => {
|
|
178
|
+
clearTimeout(e), l.off("transaction", t), l.off("blur", T), i.removeEventListener("compositionstart", n), i.removeEventListener("compositionend", r);
|
|
178
179
|
};
|
|
179
180
|
}, [
|
|
180
181
|
l,
|
|
181
|
-
|
|
182
|
-
|
|
182
|
+
C,
|
|
183
|
+
T
|
|
183
184
|
]), n(() => {
|
|
184
|
-
if (!u || !
|
|
185
|
-
let e = l.view.dom.closest(".rte-editor-wrapper"), t =
|
|
185
|
+
if (!u || !w.length) return;
|
|
186
|
+
let e = l.view.dom.closest(".rte-editor-wrapper"), t = y.current;
|
|
186
187
|
if (!e || !t) return;
|
|
187
188
|
let n = () => {
|
|
188
|
-
let n =
|
|
189
|
+
let n = C();
|
|
189
190
|
if (!n) {
|
|
190
|
-
|
|
191
|
+
T();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
let r = l.view.coordsAtPos(n.to), i = e.getBoundingClientRect(), a = Math.max(8, i.left + e.clientLeft), o = Math.min(window.innerWidth - 8, i.right), s = Math.max(8, i.top + e.clientTop), c = Math.min(window.innerHeight - 8, i.bottom);
|
|
195
|
+
if (o <= a || c <= s || r.bottom < s || r.top > c) {
|
|
196
|
+
T();
|
|
191
197
|
return;
|
|
192
198
|
}
|
|
193
|
-
let
|
|
194
|
-
t.style.minWidth = "0", t.style.width = `${Math.min(280,
|
|
195
|
-
let f = c - d - 4, p = u - s - 4, m = f < Math.min(300, t.scrollHeight) && p > f;
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
let u = Math.min(Math.max(r.top, s), c), d = Math.min(Math.max(r.bottom, s), c);
|
|
200
|
+
t.style.boxSizing = "border-box", t.style.minWidth = "0", t.style.width = `${Math.min(280, o - a)}px`;
|
|
201
|
+
let f = c - d - 4, p = u - s - 4, m = f < Math.min(300, t.scrollHeight) && p > f, h = m ? p : f;
|
|
202
|
+
if (h <= 0) {
|
|
203
|
+
T();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
t.style.maxHeight = `${Math.min(300, h)}px`;
|
|
207
|
+
let _ = Math.max(a, Math.min(r.left, o - t.offsetWidth)), v = m ? u - t.offsetHeight - 4 : d + 4;
|
|
208
|
+
g({
|
|
209
|
+
x: _ - i.left - e.clientLeft + e.scrollLeft,
|
|
210
|
+
y: v - i.top - e.clientTop + e.scrollTop
|
|
201
211
|
});
|
|
202
212
|
};
|
|
203
213
|
return n(), window.addEventListener("scroll", n, !0), window.addEventListener("resize", n), l.on("transaction", n), () => {
|
|
@@ -206,54 +216,54 @@ var c = [
|
|
|
206
216
|
}, [
|
|
207
217
|
l,
|
|
208
218
|
u,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
219
|
+
w.length,
|
|
220
|
+
C,
|
|
221
|
+
T
|
|
212
222
|
]), t(() => {
|
|
213
223
|
if (!u) return;
|
|
214
|
-
let e =
|
|
224
|
+
let e = y.current, t = e?.children[_];
|
|
215
225
|
e && t && (t.offsetTop < e.scrollTop ? e.scrollTop = t.offsetTop : t.offsetTop + t.offsetHeight > e.scrollTop + e.clientHeight && (e.scrollTop = t.offsetTop + t.offsetHeight - e.clientHeight));
|
|
216
|
-
}, [
|
|
226
|
+
}, [_, u]), t(() => {
|
|
217
227
|
if (!u) return;
|
|
218
228
|
let e = (e) => {
|
|
219
|
-
!l.view.dom.contains(e.target) || !l.isFocused || e.isComposing || e.keyCode === 229 || l.view.composing ||
|
|
229
|
+
!l.view.dom.contains(e.target) || !l.isFocused || e.isComposing || e.keyCode === 229 || l.view.composing || S.current || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || (w.length || e.key === "Escape") && ([
|
|
220
230
|
"ArrowDown",
|
|
221
231
|
"ArrowUp",
|
|
222
232
|
"Enter",
|
|
223
233
|
"Escape"
|
|
224
|
-
].includes(e.key) && e.stopImmediatePropagation(), e.key === "ArrowDown" ? (e.preventDefault(),
|
|
234
|
+
].includes(e.key) && e.stopImmediatePropagation(), e.key === "ArrowDown" ? (e.preventDefault(), v((e) => (e + 1) % w.length)) : e.key === "ArrowUp" ? (e.preventDefault(), v((e) => (e - 1 + w.length) % w.length)) : e.key === "Enter" ? (e.preventDefault(), w[_] && E(w[_])) : e.key === "Escape" && (e.preventDefault(), T()));
|
|
225
235
|
};
|
|
226
236
|
return document.addEventListener("keydown", e, !0), () => document.removeEventListener("keydown", e, !0);
|
|
227
237
|
}, [
|
|
228
238
|
l,
|
|
229
239
|
u,
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
240
|
+
w,
|
|
241
|
+
_,
|
|
242
|
+
E,
|
|
243
|
+
T
|
|
234
244
|
]), t(() => {
|
|
235
245
|
if (!u) return;
|
|
236
246
|
let e = (e) => {
|
|
237
|
-
|
|
247
|
+
y.current && !y.current.contains(e.target) && T();
|
|
238
248
|
};
|
|
239
249
|
return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
240
|
-
}, [u,
|
|
241
|
-
ref:
|
|
250
|
+
}, [u, T]), !u || w.length === 0 ? null : /* @__PURE__ */ a("div", {
|
|
251
|
+
ref: y,
|
|
242
252
|
className: "rte-slash-menu",
|
|
243
253
|
role: "menu",
|
|
244
254
|
"aria-label": "Insert block",
|
|
245
255
|
style: {
|
|
246
|
-
left:
|
|
247
|
-
top:
|
|
256
|
+
left: h.x,
|
|
257
|
+
top: h.y
|
|
248
258
|
},
|
|
249
259
|
onMouseDown: (e) => e.preventDefault(),
|
|
250
|
-
children:
|
|
260
|
+
children: w.map((e, t) => /* @__PURE__ */ o("button", {
|
|
251
261
|
type: "button",
|
|
252
262
|
role: "menuitem",
|
|
253
263
|
tabIndex: -1,
|
|
254
|
-
className: `rte-slash-menu__item ${t ===
|
|
255
|
-
onClick: () =>
|
|
256
|
-
onMouseEnter: () =>
|
|
264
|
+
className: `rte-slash-menu__item ${t === _ ? "rte-slash-menu__item--active" : ""}`,
|
|
265
|
+
onClick: () => E(e),
|
|
266
|
+
onMouseEnter: () => v(t),
|
|
257
267
|
children: [/* @__PURE__ */ a("span", {
|
|
258
268
|
className: "rte-slash-menu__icon",
|
|
259
269
|
children: e.icon
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SlashMenu.js","names":[],"sources":["../../../../src/components/pro/RichTextEditor/SlashMenu.tsx"],"sourcesContent":["import React, { useState, useEffect, useLayoutEffect, useRef, useCallback } from 'react';\nimport type { Editor } from '@tiptap/react';\nimport type { ChainedCommands } from '@tiptap/core';\nimport { TextSelection } from '@tiptap/pm/state';\n\ninterface SlashMenuItem {\n label: string;\n icon: string;\n description: string;\n action: (chain: ChainedCommands) => ChainedCommands;\n keywords?: string[];\n}\n\nconst SLASH_ITEMS: SlashMenuItem[] = [\n { label: 'Text', icon: 'Aa', description: 'Plain text', keywords: ['paragraph', 'text', 'plain'],\n action: (e) => e.setParagraph() },\n { label: 'Heading 1', icon: 'H1', description: 'Large heading', keywords: ['h1', 'title'],\n action: (e) => e.setHeading({ level: 1 }) },\n { label: 'Heading 2', icon: 'H2', description: 'Medium heading', keywords: ['h2', 'subtitle'],\n action: (e) => e.setHeading({ level: 2 }) },\n { label: 'Heading 3', icon: 'H3', description: 'Small heading', keywords: ['h3'],\n action: (e) => e.setHeading({ level: 3 }) },\n { label: 'Bullet List', icon: '•', description: 'Unordered list', keywords: ['ul', 'bullet', 'unordered'],\n action: (e) => e.toggleBulletList() },\n { label: 'Numbered List', icon: '1.', description: 'Ordered list', keywords: ['ol', 'ordered', 'number'],\n action: (e) => e.toggleOrderedList() },\n { label: 'Task List', icon: '☑', description: 'To-do list', keywords: ['todo', 'checkbox', 'task'],\n action: (e) => e.toggleTaskList() },\n { label: 'Blockquote', icon: '\"', description: 'Quote block', keywords: ['quote', 'blockquote'],\n action: (e) => e.toggleBlockquote() },\n { label: 'Code Block', icon: '</>', description: 'Code snippet', keywords: ['code', 'pre', 'snippet'],\n action: (e) => e.setNode('codeBlock') },\n { label: 'Divider', icon: '—', description: 'Horizontal rule', keywords: ['hr', 'divider', 'separator', 'line'],\n action: (e) => e.setHorizontalRule() },\n { label: 'Table', icon: '⊞', description: 'Insert 3×3 table', keywords: ['table', 'grid'],\n action: (e) => e.insertTable({ rows: 3, cols: 3, withHeaderRow: true }) },\n { label: 'Image', icon: '🖼', description: 'Insert image by URL', keywords: ['image', 'picture', 'photo'],\n action: (e) => e},\n];\n\ninterface SlashMenuProps {\n editor: Editor;\n}\n\nconst SlashMenu: React.FC<SlashMenuProps> = ({ editor }) => {\n const [visible, setVisible] = useState(false);\n const [query, setQuery] = useState('');\n const [pos, setPos] = useState({ x: 0, y: 0 });\n const [selected, setSelected] = useState(0);\n const menuRef = useRef<HTMLDivElement>(null);\n const activeRange = useRef<{ from: number; to: number; query: string } | null>(null);\n const dismissed = useRef<string | null>(null);\n const composing = useRef(false);\n\n const getRange = useCallback(() => {\n if (editor.isDestroyed || !editor.isEditable || editor.view.composing || composing.current) return null;\n const selection = editor.state.selection;\n if (!(selection instanceof TextSelection) || !selection.empty || selection.$from.parent.type.spec.code || selection.$from.marks().some((mark) => mark.type.spec.code)) return null;\n const { $from } = selection;\n // textContent drops inline atoms and hard breaks, so its offsets are not document offsets.\n const text = $from.parent.textBetween(0, $from.parentOffset, '\\n', (node) => node.type.name === 'hardBreak' ? '\\n' : '\\ufffc');\n const match = /(?:^|\\s)\\/([^\\s/]*)$/.exec(text);\n if (!match) return null;\n return { from: $from.pos - match[1].length - 1, to: $from.pos, query: match[1] };\n }, [editor]);\n\n const filtered = query\n ? SLASH_ITEMS.filter((item) => {\n const q = query.toLowerCase();\n return item.label.toLowerCase().includes(q)\n || item.description.toLowerCase().includes(q)\n || item.keywords?.some((k) => k.includes(q));\n })\n : SLASH_ITEMS;\n\n const close = useCallback(() => {\n setVisible(false);\n setQuery('');\n const range = activeRange.current;\n if (range) dismissed.current = `${range.from}:${range.to}:${range.query}`;\n activeRange.current = null;\n setSelected(0);\n }, []);\n\n const executeItem = useCallback((item: SlashMenuItem) => {\n const range = getRange();\n const active = activeRange.current;\n if (!range || !active || range.from !== active.from || range.to !== active.to || range.query !== active.query) {\n close();\n return;\n }\n const url = item.label === 'Image' ? window.prompt('Image URL:')?.trim() : undefined;\n if (item.label === 'Image' && !url) { close(); return; }\n close();\n // Delete the trigger and insert/convert in one transaction (one undo step).\n const chain = editor.chain().focus().deleteRange(range);\n (url ? chain.setImage({ src: url }) : item.action(chain)).run();\n }, [editor, getRange, close]);\n\n useEffect(() => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const handleUpdate = () => {\n const range = editor.isFocused ? getRange() : null;\n if (!range) {\n activeRange.current = null;\n dismissed.current = null;\n setVisible(false);\n return;\n }\n if (dismissed.current === `${range.from}:${range.to}:${range.query}`) return;\n if (activeRange.current?.query !== range.query || activeRange.current?.from !== range.from) setSelected(0);\n activeRange.current = range;\n setQuery(range.query);\n setVisible(true);\n };\n const start = () => { composing.current = true; close(); };\n const end = () => {\n composing.current = false;\n dismissed.current = null;\n timer = setTimeout(handleUpdate, 0);\n };\n const dom = editor.view.dom;\n editor.on('transaction', handleUpdate);\n editor.on('blur', close);\n dom.addEventListener('compositionstart', start);\n dom.addEventListener('compositionend', end);\n return () => {\n clearTimeout(timer);\n editor.off('transaction', handleUpdate);\n editor.off('blur', close);\n dom.removeEventListener('compositionstart', start);\n dom.removeEventListener('compositionend', end);\n };\n }, [editor, getRange, close]);\n\n useLayoutEffect(() => {\n if (!visible || !filtered.length) return;\n const wrapper = editor.view.dom.closest('.rte-editor-wrapper') as HTMLElement | null;\n const menu = menuRef.current;\n if (!wrapper || !menu) return;\n const place = () => {\n const range = getRange();\n if (!range) { close(); return; }\n const coords = editor.view.coordsAtPos(range.to);\n const rect = wrapper.getBoundingClientRect();\n const left = Math.max(8, rect.left + wrapper.clientLeft);\n const right = Math.min(window.innerWidth - 8, rect.right);\n const top = Math.max(8, rect.top + wrapper.clientTop);\n const bottom = Math.min(window.innerHeight - 8, rect.bottom);\n // Clamp the anchor into the visible area instead of closing: on small /\n // scrollable (phone) containers the cursor can sit at the very edge, and\n // closing the menu makes block insertion feel broken (\" stuck \").\n const anchorTop = Math.min(Math.max(coords.top, top), bottom);\n const anchorBottom = Math.min(Math.max(coords.bottom, top), bottom);\n menu.style.minWidth = '0';\n menu.style.width = `${Math.min(280, Math.max(120, right - left))}px`;\n const below = bottom - anchorBottom - 4;\n const above = anchorTop - top - 4;\n const flip = below < Math.min(300, menu.scrollHeight) && above > below;\n // Keep a minimum visible height: a 0-height menu still hijacks Enter and\n // arrow keys, which looks like a frozen editor.\n menu.style.maxHeight = `${Math.max(120, Math.min(300, flip ? above : below))}px`;\n const x = Math.max(left, Math.min(coords.left, right - menu.offsetWidth));\n const y = flip ? anchorTop - menu.offsetHeight - 4 : anchorBottom + 4;\n setPos({ x: x - rect.left - wrapper.clientLeft + wrapper.scrollLeft,\n y: y - rect.top - wrapper.clientTop + wrapper.scrollTop });\n };\n place();\n window.addEventListener('scroll', place, true);\n window.addEventListener('resize', place);\n editor.on('transaction', place);\n return () => {\n window.removeEventListener('scroll', place, true);\n window.removeEventListener('resize', place);\n editor.off('transaction', place);\n };\n }, [editor, visible, filtered.length, getRange, close]);\n\n useEffect(() => {\n if (!visible) return;\n const menu = menuRef.current;\n const item = menu?.children[selected] as HTMLElement | undefined;\n if (!menu || !item) return;\n if (item.offsetTop < menu.scrollTop) menu.scrollTop = item.offsetTop;\n else if (item.offsetTop + item.offsetHeight > menu.scrollTop + menu.clientHeight) {\n menu.scrollTop = item.offsetTop + item.offsetHeight - menu.clientHeight;\n }\n }, [selected, visible]);\n\n useEffect(() => {\n if (!visible) return;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (!editor.view.dom.contains(e.target as Node) || !editor.isFocused || e.isComposing || e.keyCode === 229 || editor.view.composing || composing.current || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) return;\n if (!filtered.length && e.key !== 'Escape') return;\n if (['ArrowDown', 'ArrowUp', 'Enter', 'Escape'].includes(e.key)) e.stopImmediatePropagation();\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setSelected((s) => (s + 1) % filtered.length);\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setSelected((s) => (s - 1 + filtered.length) % filtered.length);\n } else if (e.key === 'Enter') {\n e.preventDefault();\n if (filtered[selected]) executeItem(filtered[selected]);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n close();\n }\n };\n document.addEventListener('keydown', handleKeyDown, true);\n return () => document.removeEventListener('keydown', handleKeyDown, true);\n }, [editor, visible, filtered, selected, executeItem, close]);\n\n useEffect(() => {\n if (!visible) return;\n const handler = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) close();\n };\n document.addEventListener('mousedown', handler);\n return () => document.removeEventListener('mousedown', handler);\n }, [visible, close]);\n\n if (!visible || filtered.length === 0) return null;\n\n return (\n <div ref={menuRef} className=\"rte-slash-menu\" role=\"menu\" aria-label=\"Insert block\" style={{ left: pos.x, top: pos.y }} onMouseDown={(e) => e.preventDefault()}>\n {filtered.map((item, i) => (\n <button\n key={item.label}\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className={`rte-slash-menu__item ${i === selected ? 'rte-slash-menu__item--active' : ''}`}\n onClick={() => executeItem(item)}\n onMouseEnter={() => setSelected(i)}\n >\n <span className=\"rte-slash-menu__icon\">{item.icon}</span>\n <div className=\"rte-slash-menu__text\">\n <span className=\"rte-slash-menu__label\">{item.label}</span>\n <span className=\"rte-slash-menu__desc\">{item.description}</span>\n </div>\n </button>\n ))}\n </div>\n );\n};\n\nexport default SlashMenu;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SlashMenu.js","names":[],"sources":["../../../../src/components/pro/RichTextEditor/SlashMenu.tsx"],"sourcesContent":["import React, { useState, useEffect, useLayoutEffect, useRef, useCallback } from 'react';\nimport type { Editor } from '@tiptap/react';\nimport type { ChainedCommands } from '@tiptap/core';\nimport { TextSelection } from '@tiptap/pm/state';\n\ninterface SlashMenuItem {\n label: string;\n icon: string;\n description: string;\n action: (chain: ChainedCommands) => ChainedCommands;\n keywords?: string[];\n}\n\nconst SLASH_ITEMS: SlashMenuItem[] = [\n { label: 'Text', icon: 'Aa', description: 'Plain text', keywords: ['paragraph', 'text', 'plain'],\n action: (e) => e.setParagraph() },\n { label: 'Heading 1', icon: 'H1', description: 'Large heading', keywords: ['h1', 'title'],\n action: (e) => e.setHeading({ level: 1 }) },\n { label: 'Heading 2', icon: 'H2', description: 'Medium heading', keywords: ['h2', 'subtitle'],\n action: (e) => e.setHeading({ level: 2 }) },\n { label: 'Heading 3', icon: 'H3', description: 'Small heading', keywords: ['h3'],\n action: (e) => e.setHeading({ level: 3 }) },\n { label: 'Bullet List', icon: '•', description: 'Unordered list', keywords: ['ul', 'bullet', 'unordered'],\n action: (e) => e.toggleBulletList() },\n { label: 'Numbered List', icon: '1.', description: 'Ordered list', keywords: ['ol', 'ordered', 'number'],\n action: (e) => e.toggleOrderedList() },\n { label: 'Task List', icon: '☑', description: 'To-do list', keywords: ['todo', 'checkbox', 'task'],\n action: (e) => e.toggleTaskList() },\n { label: 'Blockquote', icon: '\"', description: 'Quote block', keywords: ['quote', 'blockquote'],\n action: (e) => e.toggleBlockquote() },\n { label: 'Code Block', icon: '</>', description: 'Code snippet', keywords: ['code', 'pre', 'snippet'],\n action: (e) => e.setNode('codeBlock') },\n { label: 'Divider', icon: '—', description: 'Horizontal rule', keywords: ['hr', 'divider', 'separator', 'line'],\n action: (e) => e.setHorizontalRule() },\n { label: 'Table', icon: '⊞', description: 'Insert 3×3 table', keywords: ['table', 'grid'],\n action: (e) => e.insertTable({ rows: 3, cols: 3, withHeaderRow: true }) },\n { label: 'Image', icon: '🖼', description: 'Insert image by URL', keywords: ['image', 'picture', 'photo'],\n action: (e) => e},\n];\n\ninterface SlashRange {\n from: number;\n to: number;\n query: string;\n}\n\ninterface SlashMenuProps {\n editor: Editor;\n}\n\nconst SlashMenu: React.FC<SlashMenuProps> = ({ editor }) => {\n const [visible, setVisible] = useState(false);\n const [renderedRange, setRenderedRange] = useState<SlashRange | null>(null);\n const query = renderedRange?.query ?? '';\n const [pos, setPos] = useState({ x: 0, y: 0 });\n const [selected, setSelected] = useState(0);\n const menuRef = useRef<HTMLDivElement>(null);\n const activeRange = useRef<SlashRange | null>(null);\n const dismissed = useRef<string | null>(null);\n const composing = useRef(false);\n\n const getRange = useCallback(() => {\n if (editor.isDestroyed || !editor.isEditable || editor.view.composing || composing.current) return null;\n const selection = editor.state.selection;\n if (!(selection instanceof TextSelection) || !selection.empty || selection.$from.parent.type.spec.code || selection.$from.marks().some((mark) => mark.type.spec.code)) return null;\n const { $from } = selection;\n // textContent drops inline atoms and hard breaks, so its offsets are not document offsets.\n const text = $from.parent.textBetween(0, $from.parentOffset, '\\n', (node) => node.type.name === 'hardBreak' ? '\\n' : '\\ufffc');\n const match = /(?:^|\\s)\\/([^\\s/]*)$/.exec(text);\n if (!match) return null;\n return { from: $from.pos - match[1].length - 1, to: $from.pos, query: match[1] };\n }, [editor]);\n\n const filtered = query\n ? SLASH_ITEMS.filter((item) => {\n const q = query.toLowerCase();\n return item.label.toLowerCase().includes(q)\n || item.description.toLowerCase().includes(q)\n || item.keywords?.some((k) => k.includes(q));\n })\n : SLASH_ITEMS;\n\n const close = useCallback(() => {\n setVisible(false);\n setRenderedRange(null);\n const range = activeRange.current;\n if (range) dismissed.current = `${range.from}:${range.to}:${range.query}`;\n activeRange.current = null;\n setSelected(0);\n }, []);\n\n const executeItem = useCallback((item: SlashMenuItem) => {\n const range = getRange();\n const active = activeRange.current;\n // Transactions update the ref before React commits the new menu. An old\n // rendered action must not operate on that newer trigger/selection.\n if (!range || !active || !renderedRange\n || range.from !== renderedRange.from || range.to !== renderedRange.to || range.query !== renderedRange.query\n || range.from !== active.from || range.to !== active.to || range.query !== active.query) {\n close();\n return;\n }\n const url = item.label === 'Image' ? window.prompt('Image URL:')?.trim() : undefined;\n if (item.label === 'Image' && !url) { close(); return; }\n close();\n // Delete the trigger and insert/convert in one transaction (one undo step).\n const chain = editor.chain().focus().deleteRange(range);\n (url ? chain.setImage({ src: url }) : item.action(chain)).run();\n }, [editor, getRange, close, renderedRange]);\n\n useEffect(() => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const handleUpdate = () => {\n const range = editor.isFocused ? getRange() : null;\n if (!range) {\n activeRange.current = null;\n dismissed.current = null;\n setVisible(false);\n return;\n }\n if (dismissed.current === `${range.from}:${range.to}:${range.query}`) return;\n if (activeRange.current?.query !== range.query || activeRange.current?.from !== range.from) setSelected(0);\n activeRange.current = range;\n setRenderedRange(range);\n setVisible(true);\n };\n const start = () => { composing.current = true; close(); };\n const end = () => {\n composing.current = false;\n dismissed.current = null;\n timer = setTimeout(handleUpdate, 0);\n };\n const dom = editor.view.dom;\n editor.on('transaction', handleUpdate);\n editor.on('blur', close);\n dom.addEventListener('compositionstart', start);\n dom.addEventListener('compositionend', end);\n return () => {\n clearTimeout(timer);\n editor.off('transaction', handleUpdate);\n editor.off('blur', close);\n dom.removeEventListener('compositionstart', start);\n dom.removeEventListener('compositionend', end);\n };\n }, [editor, getRange, close]);\n\n useLayoutEffect(() => {\n if (!visible || !filtered.length) return;\n const wrapper = editor.view.dom.closest('.rte-editor-wrapper') as HTMLElement | null;\n const menu = menuRef.current;\n if (!wrapper || !menu) return;\n const place = () => {\n const range = getRange();\n if (!range) { close(); return; }\n const coords = editor.view.coordsAtPos(range.to);\n const rect = wrapper.getBoundingClientRect();\n const left = Math.max(8, rect.left + wrapper.clientLeft);\n const right = Math.min(window.innerWidth - 8, rect.right);\n const top = Math.max(8, rect.top + wrapper.clientTop);\n const bottom = Math.min(window.innerHeight - 8, rect.bottom);\n // Keep partially visible edge anchors usable, but never leave an\n // offscreen menu intercepting the editor's Enter/arrow keys.\n if (right <= left || bottom <= top || coords.bottom < top || coords.top > bottom) {\n close();\n return;\n }\n const anchorTop = Math.min(Math.max(coords.top, top), bottom);\n const anchorBottom = Math.min(Math.max(coords.bottom, top), bottom);\n menu.style.boxSizing = 'border-box';\n menu.style.minWidth = '0';\n menu.style.width = `${Math.min(280, right - left)}px`;\n const below = bottom - anchorBottom - 4;\n const above = anchorTop - top - 4;\n const flip = below < Math.min(300, menu.scrollHeight) && above > below;\n const availableHeight = flip ? above : below;\n if (availableHeight <= 0) { close(); return; }\n menu.style.maxHeight = `${Math.min(300, availableHeight)}px`;\n const x = Math.max(left, Math.min(coords.left, right - menu.offsetWidth));\n const y = flip ? anchorTop - menu.offsetHeight - 4 : anchorBottom + 4;\n setPos({ x: x - rect.left - wrapper.clientLeft + wrapper.scrollLeft,\n y: y - rect.top - wrapper.clientTop + wrapper.scrollTop });\n };\n place();\n window.addEventListener('scroll', place, true);\n window.addEventListener('resize', place);\n editor.on('transaction', place);\n return () => {\n window.removeEventListener('scroll', place, true);\n window.removeEventListener('resize', place);\n editor.off('transaction', place);\n };\n }, [editor, visible, filtered.length, getRange, close]);\n\n useEffect(() => {\n if (!visible) return;\n const menu = menuRef.current;\n const item = menu?.children[selected] as HTMLElement | undefined;\n if (!menu || !item) return;\n if (item.offsetTop < menu.scrollTop) menu.scrollTop = item.offsetTop;\n else if (item.offsetTop + item.offsetHeight > menu.scrollTop + menu.clientHeight) {\n menu.scrollTop = item.offsetTop + item.offsetHeight - menu.clientHeight;\n }\n }, [selected, visible]);\n\n useEffect(() => {\n if (!visible) return;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (!editor.view.dom.contains(e.target as Node) || !editor.isFocused || e.isComposing || e.keyCode === 229 || editor.view.composing || composing.current || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) return;\n if (!filtered.length && e.key !== 'Escape') return;\n if (['ArrowDown', 'ArrowUp', 'Enter', 'Escape'].includes(e.key)) e.stopImmediatePropagation();\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setSelected((s) => (s + 1) % filtered.length);\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setSelected((s) => (s - 1 + filtered.length) % filtered.length);\n } else if (e.key === 'Enter') {\n e.preventDefault();\n if (filtered[selected]) executeItem(filtered[selected]);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n close();\n }\n };\n document.addEventListener('keydown', handleKeyDown, true);\n return () => document.removeEventListener('keydown', handleKeyDown, true);\n }, [editor, visible, filtered, selected, executeItem, close]);\n\n useEffect(() => {\n if (!visible) return;\n const handler = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) close();\n };\n document.addEventListener('mousedown', handler);\n return () => document.removeEventListener('mousedown', handler);\n }, [visible, close]);\n\n if (!visible || filtered.length === 0) return null;\n\n return (\n <div ref={menuRef} className=\"rte-slash-menu\" role=\"menu\" aria-label=\"Insert block\" style={{ left: pos.x, top: pos.y }} onMouseDown={(e) => e.preventDefault()}>\n {filtered.map((item, i) => (\n <button\n key={item.label}\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className={`rte-slash-menu__item ${i === selected ? 'rte-slash-menu__item--active' : ''}`}\n onClick={() => executeItem(item)}\n onMouseEnter={() => setSelected(i)}\n >\n <span className=\"rte-slash-menu__icon\">{item.icon}</span>\n <div className=\"rte-slash-menu__text\">\n <span className=\"rte-slash-menu__label\">{item.label}</span>\n <span className=\"rte-slash-menu__desc\">{item.description}</span>\n </div>\n </button>\n ))}\n </div>\n );\n};\n\nexport default SlashMenu;\n"],"mappings":"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"}
|
|
@@ -41,6 +41,7 @@ export type AppShellConfig = {
|
|
|
41
41
|
menuItems?: MenuItem[];
|
|
42
42
|
getMenuItems?: (language: 'zh' | 'en') => MenuItem[];
|
|
43
43
|
themes?: ThemeConfig[];
|
|
44
|
+
/** Enables client auth state and navigation notifications, not loader protection. Use route beforeLoad for loaders. */
|
|
44
45
|
enableAuth?: boolean;
|
|
45
46
|
onAuthRequired?: () => boolean | void;
|
|
46
47
|
protectedRoutes?: string[];
|