dishui 0.0.64 → 0.0.65
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/Badge/index.js +12 -9
- package/dist/components/Badge/index.js.map +1 -1
- package/dist/components/Button/index.js +43 -40
- package/dist/components/Button/index.js.map +1 -1
- package/dist/components/Card/index.d.ts +11 -0
- package/dist/components/Card/index.js +62 -37
- package/dist/components/Card/index.js.map +1 -1
- package/dist/components/Chart/Chart.js.map +1 -1
- package/dist/components/Checkbox/index.js +32 -30
- package/dist/components/Checkbox/index.js.map +1 -1
- package/dist/components/Demo/index.js.map +1 -1
- package/dist/components/EmptyState/index.d.ts +4 -1
- package/dist/components/EmptyState/index.js +3 -3
- package/dist/components/EmptyState/index.js.map +1 -1
- package/dist/components/FileUploader/DropZone.js.map +1 -1
- package/dist/components/FileUploader/FileItem.js.map +1 -1
- package/dist/components/FileUploader/FileUploader.js.map +1 -1
- package/dist/components/FileUploader/UploadQueue.js.map +1 -1
- package/dist/components/Input/index.js +30 -30
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/Label/index.js +41 -40
- package/dist/components/Label/index.js.map +1 -1
- package/dist/components/List/index.js +47 -32
- package/dist/components/List/index.js.map +1 -1
- package/dist/components/Markdown/Markdown.js +61 -57
- package/dist/components/Markdown/Markdown.js.map +1 -1
- package/dist/components/Markdown/markdownComponents.js +49 -49
- package/dist/components/Markdown/markdownComponents.js.map +1 -1
- package/dist/components/Modal/index.d.ts +7 -3
- package/dist/components/Modal/index.js +77 -65
- package/dist/components/Modal/index.js.map +1 -1
- package/dist/components/Page/index.d.ts +58 -0
- package/dist/components/Page/index.js +74 -0
- package/dist/components/Page/index.js.map +1 -0
- package/dist/components/PageHeader/index.js.map +1 -1
- package/dist/components/Select/index.js +0 -3
- package/dist/components/Select/index.js.map +1 -1
- package/dist/components/SidebarToggleButton/index.js.map +1 -1
- package/dist/components/StatCard/index.d.ts +3 -1
- package/dist/components/StatCard/index.js +39 -11
- package/dist/components/StatCard/index.js.map +1 -1
- package/dist/components/Table/index.js +103 -85
- package/dist/components/Table/index.js.map +1 -1
- package/dist/components/Tabs/index.d.ts +19 -5
- package/dist/components/Tabs/index.js +32 -13
- package/dist/components/Tabs/index.js.map +1 -1
- package/dist/components/Tree/Tree.js.map +1 -1
- package/dist/components/pro/ArticleContextMenu/index.js.map +1 -1
- package/dist/components/pro/ArticleControl/ArticleControl2.js.map +1 -1
- package/dist/components/pro/CodeEditor/editors/CodeEditor.js +141 -134
- package/dist/components/pro/CodeEditor/editors/CodeEditor.js.map +1 -1
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js +81 -78
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/FootnotePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/HistoryPanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/OutlinePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/TableOfContentsPanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/VariablePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/themes.js +59 -59
- package/dist/components/pro/CodeEditor/themes.js.map +1 -1
- package/dist/components/pro/DataTable/DataTable.js +49 -48
- package/dist/components/pro/DataTable/DataTable.js.map +1 -1
- package/dist/components/pro/DataTable/Filters.js +42 -42
- package/dist/components/pro/DataTable/Filters.js.map +1 -1
- package/dist/components/pro/DataTable/PivotPanel.js.map +1 -1
- package/dist/components/pro/DataTable/PivotView.js +35 -32
- package/dist/components/pro/DataTable/PivotView.js.map +1 -1
- package/dist/components/pro/DataTable/components/BulkActions.js.map +1 -1
- package/dist/components/pro/DataTable/components/ColumnSelector.js.map +1 -1
- package/dist/components/pro/DataTable/components/FilterBar.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableBody.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableHeader.js.map +1 -1
- package/dist/components/pro/DataTable/components/TablePagination.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableToolbar.js.map +1 -1
- package/dist/components/pro/DataTable/pivotExtension.js.map +1 -1
- package/dist/components/pro/DataTable/rowDetailExtension.js.map +1 -1
- package/dist/components/pro/DataTable/tanstack-table.js +37 -37
- package/dist/components/pro/DataTable/tanstack-table.js.map +1 -1
- package/dist/components/pro/FileExplorer/Breadcrumb.js +53 -53
- package/dist/components/pro/FileExplorer/Breadcrumb.js.map +1 -1
- package/dist/components/pro/FileExplorer/CreateDialog.js.map +1 -1
- package/dist/components/pro/FileExplorer/FileExplorer.js.map +1 -1
- package/dist/components/pro/FileExplorer/FileIcon.js +22 -22
- package/dist/components/pro/FileExplorer/FileIcon.js.map +1 -1
- package/dist/components/pro/FileExplorer/GridView.js.map +1 -1
- package/dist/components/pro/FileExplorer/ListView.js.map +1 -1
- package/dist/components/pro/FileExplorer/Toolbar.js.map +1 -1
- package/dist/components/pro/Flow/Flow.js +496 -494
- package/dist/components/pro/Flow/Flow.js.map +1 -1
- package/dist/components/pro/Flow/FlowCanvas.js +916 -917
- package/dist/components/pro/Flow/FlowCanvas.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowUIContext.js.map +1 -1
- package/dist/components/pro/Flow/edges/EdgeRenderer.js +59 -59
- package/dist/components/pro/Flow/edges/EdgeRenderer.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useClipboard.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useDeleteSelection.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useEdgeMutations.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useLayoutActions.js +9 -11
- package/dist/components/pro/Flow/hooks/useLayoutActions.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useLineMutations.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useMindMap.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useNodeMutations.js.map +1 -1
- package/dist/components/pro/Flow/lines/FreehandTool.js +9 -9
- package/dist/components/pro/Flow/lines/FreehandTool.js.map +1 -1
- package/dist/components/pro/Flow/lines/LineRenderer.js +5 -5
- package/dist/components/pro/Flow/lines/LineRenderer.js.map +1 -1
- package/dist/components/pro/Flow/nodes/DataNodeView.js +113 -93
- package/dist/components/pro/Flow/nodes/DataNodeView.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaContent.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaOverlay.js.map +1 -1
- package/dist/components/pro/Flow/nodes/Model3DView.js +1 -1
- package/dist/components/pro/Flow/nodes/Model3DView.js.map +1 -1
- package/dist/components/pro/Flow/nodes/NodeRenderer.js +118 -118
- package/dist/components/pro/Flow/nodes/NodeRenderer.js.map +1 -1
- package/dist/components/pro/Flow/nodes/dataUtils.js.map +1 -1
- package/dist/components/pro/Flow/nodes/shapes.js +1 -3
- package/dist/components/pro/Flow/nodes/shapes.js.map +1 -1
- package/dist/components/pro/Flow/panels/AIPanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/Breadcrumb.js.map +1 -1
- package/dist/components/pro/Flow/panels/ContextMenu.js.map +1 -1
- package/dist/components/pro/Flow/panels/DataInspectorSection.js.map +1 -1
- package/dist/components/pro/Flow/panels/ExpandableTextarea.js.map +1 -1
- package/dist/components/pro/Flow/panels/FlowPanelGroup.js.map +1 -1
- package/dist/components/pro/Flow/panels/FlowToolbar.js +97 -97
- package/dist/components/pro/Flow/panels/FlowToolbar.js.map +1 -1
- package/dist/components/pro/Flow/panels/InspectorPanel.js +57 -57
- package/dist/components/pro/Flow/panels/InspectorPanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/OutlinePanel.js +26 -22
- package/dist/components/pro/Flow/panels/OutlinePanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/PresentationMode.js +18 -16
- package/dist/components/pro/Flow/panels/PresentationMode.js.map +1 -1
- package/dist/components/pro/Flow/panels/ProjectTitle.js.map +1 -1
- package/dist/components/pro/Flow/panels/ShapePalette.js +109 -104
- package/dist/components/pro/Flow/panels/ShapePalette.js.map +1 -1
- package/dist/components/pro/Flow/panels/toolbarIcons.js.map +1 -1
- package/dist/components/pro/RichTextEditor/AIRewrite.js.map +1 -1
- package/dist/components/pro/RichTextEditor/BlockHandle.js.map +1 -1
- package/dist/components/pro/RichTextEditor/BubbleToolbar.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentDialog.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentPanel.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentPopover.js.map +1 -1
- package/dist/components/pro/RichTextEditor/ContextMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/RichTextEditor.js.map +1 -1
- package/dist/components/pro/RichTextEditor/SlashMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/FloatingDialogMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/MarkdownTemplates.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/TableOpsMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/Toolbar.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/CodeBlockView.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/FloatingDialog.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/MarkdownBlock.js.map +1 -1
- package/dist/contexts/AppContext.js.map +1 -1
- package/dist/dishui.css +1 -1
- package/dist/hooks/useTableColumns.js +7 -8
- package/dist/hooks/useTableColumns.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +227 -221
- package/dist/layouts/MainLayout.js +22 -22
- package/dist/layouts/MainLayout.js.map +1 -1
- package/dist/layouts/Navbar.js.map +1 -1
- package/dist/layouts/Sidebar.js +94 -73
- package/dist/layouts/Sidebar.js.map +1 -1
- package/dist/layouts/headbar/Headbar.js.map +1 -1
- package/dist/layouts/headbar/HeadbarLangSwitcher.js.map +1 -1
- package/dist/layouts/headbar/HeadbarSearch.js.map +1 -1
- package/dist/layouts/headbar/HeadbarThemeSwitcher.js +11 -11
- package/dist/layouts/headbar/HeadbarThemeSwitcher.js.map +1 -1
- package/dist/layouts/headbar/HeadbarTitle.js.map +1 -1
- package/dist/utils/i18n.js +10 -10
- package/dist/utils/i18n.js.map +1 -1
- package/dist/utils/menu.js.map +1 -1
- package/dist/utils/routerBuilder.js +17 -15
- package/dist/utils/routerBuilder.js.map +1 -1
- package/dist/utils/themes.js.map +1 -1
- package/package.json +7 -7
|
@@ -27,25 +27,29 @@ function W({ node: n }) {
|
|
|
27
27
|
}
|
|
28
28
|
}) : null;
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
if (n.type === "mediaNode") {
|
|
31
|
+
const h = n.data.mediaKind === "markdown" ? "M↓" : n.data.mediaKind === "html" ? "</>" : n.data.mediaKind === "mermaid" ? "⎇" : n.data.mediaKind === "math" ? "√x" : n.data.mediaKind === "image" ? "IMG" : n.data.mediaKind === "model3d" ? "3D" : n.data.mediaKind === "data" ? "⊞" : "AI";
|
|
32
|
+
return /* @__PURE__ */ u("span", {
|
|
33
|
+
style: {
|
|
34
|
+
width: 24,
|
|
35
|
+
textAlign: "center",
|
|
36
|
+
fontSize: 10,
|
|
37
|
+
fontWeight: 700
|
|
38
|
+
},
|
|
39
|
+
children: h
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return /* @__PURE__ */ u(E, { type: n.type });
|
|
39
43
|
}
|
|
40
44
|
function _(n, h, s) {
|
|
41
|
-
const g = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Set(),
|
|
45
|
+
const g = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Set(), x = /* @__PURE__ */ new Set();
|
|
42
46
|
for (const e of n) if (e.parentId) {
|
|
43
47
|
const c = g.get(e.parentId) ?? [];
|
|
44
48
|
c.push(e), g.set(e.parentId, c), y.add(e.id);
|
|
45
49
|
}
|
|
46
50
|
for (const e of h) if (e.parentId) {
|
|
47
51
|
const c = f.get(e.parentId) ?? [];
|
|
48
|
-
c.push(e), f.set(e.parentId, c),
|
|
52
|
+
c.push(e), f.set(e.parentId, c), x.add(e.id);
|
|
49
53
|
}
|
|
50
54
|
const v = n.filter((e) => e.type === "geometryGroup");
|
|
51
55
|
for (const e of v) {
|
|
@@ -56,23 +60,23 @@ function _(n, h, s) {
|
|
|
56
60
|
o.push(t), g.set(e.id, o), y.add(t.id);
|
|
57
61
|
}
|
|
58
62
|
for (const t of h)
|
|
59
|
-
if (!
|
|
63
|
+
if (!x.has(t.id) && t.points.length > 0 && t.points.every((o) => o.x >= c && o.x <= c + i && o.y >= r && o.y <= r + l)) {
|
|
60
64
|
const o = f.get(e.id) ?? [];
|
|
61
|
-
o.push(t), f.set(e.id, o),
|
|
65
|
+
o.push(t), f.set(e.id, o), x.add(t.id);
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
const m = n.filter((e) => !y.has(e.id)), C = (e) => ({
|
|
65
69
|
id: e.id,
|
|
66
70
|
label: `${e.type} line`,
|
|
67
71
|
icon: "〰"
|
|
68
|
-
}),
|
|
72
|
+
}), b = (e) => {
|
|
69
73
|
const c = !!e.data.childCanvasId, r = /* @__PURE__ */ u(W, { node: e }), i = g.get(e.id), l = f.get(e.id), t = {
|
|
70
74
|
id: e.id,
|
|
71
75
|
label: e.data.label || e.type,
|
|
72
76
|
icon: r,
|
|
73
77
|
meta: c ? { childCanvasId: e.data.childCanvasId } : void 0
|
|
74
78
|
}, o = [];
|
|
75
|
-
if (i && i.length > 0 && o.push(...i.map(
|
|
79
|
+
if (i && i.length > 0 && o.push(...i.map(b)), l && l.length > 0 && o.push(...l.map(C)), c) {
|
|
76
80
|
const d = s.canvases[e.data.childCanvasId];
|
|
77
81
|
if (d) {
|
|
78
82
|
const a = X(d, s, e.data.childCanvasId);
|
|
@@ -82,8 +86,8 @@ function _(n, h, s) {
|
|
|
82
86
|
return o.length > 0 && (t.children = o), t;
|
|
83
87
|
};
|
|
84
88
|
return {
|
|
85
|
-
nodeTree: m.map(
|
|
86
|
-
assignedLineIds:
|
|
89
|
+
nodeTree: m.map(b),
|
|
90
|
+
assignedLineIds: x
|
|
87
91
|
};
|
|
88
92
|
}
|
|
89
93
|
function X(n, h, s) {
|
|
@@ -133,7 +137,7 @@ function te() {
|
|
|
133
137
|
}) : null;
|
|
134
138
|
}
|
|
135
139
|
function Y() {
|
|
136
|
-
const { focusOnRect: n } = k(), { doc: h, currentCanvas: s, currentCanvasId: g, selection: f, setSelection: y, pushCanvas:
|
|
140
|
+
const { focusOnRect: n } = k(), { doc: h, currentCanvas: s, currentCanvasId: g, selection: f, setSelection: y, pushCanvas: x } = L(), v = M(() => {
|
|
137
141
|
const { nodeTree: r, assignedLineIds: i } = _(s.nodes, s.lines, h), l = s.edges.map((a) => {
|
|
138
142
|
const p = s.nodes.find((N) => N.id === a.source), I = s.nodes.find((N) => N.id === a.target), w = p?.data.label || a.source, S = I?.data.label || a.target;
|
|
139
143
|
return {
|
|
@@ -186,7 +190,7 @@ function Y() {
|
|
|
186
190
|
s.nodes,
|
|
187
191
|
s.lines,
|
|
188
192
|
m
|
|
189
|
-
]),
|
|
193
|
+
]), b = T((r) => {
|
|
190
194
|
const i = String(r.id);
|
|
191
195
|
if (i.startsWith("_")) return;
|
|
192
196
|
const l = s.nodes.find((d) => d.id === i), t = s.edges.find((d) => d.id === i), o = s.lines.find((d) => d.id === i);
|
|
@@ -215,8 +219,8 @@ function Y() {
|
|
|
215
219
|
const i = String(r.id);
|
|
216
220
|
if (i.startsWith("_")) return;
|
|
217
221
|
const l = s.nodes.find((t) => t.id === i);
|
|
218
|
-
l?.data.childCanvasId &&
|
|
219
|
-
}, [s.nodes,
|
|
222
|
+
l?.data.childCanvasId && x(l.data.childCanvasId);
|
|
223
|
+
}, [s.nodes, x]), c = R(null);
|
|
220
224
|
return G(() => {
|
|
221
225
|
if (!m || !c.current) return;
|
|
222
226
|
const r = setTimeout(() => {
|
|
@@ -231,7 +235,7 @@ function Y() {
|
|
|
231
235
|
ref: c,
|
|
232
236
|
children: /* @__PURE__ */ u(z, {
|
|
233
237
|
data: v,
|
|
234
|
-
onNodeClick:
|
|
238
|
+
onNodeClick: b,
|
|
235
239
|
onNodeDoubleClick: e,
|
|
236
240
|
activeNodeId: m,
|
|
237
241
|
defaultExpandedIds: C,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OutlinePanel.js","names":[],"sources":["../../../../../src/components/pro/Flow/panels/OutlinePanel.tsx"],"sourcesContent":["import React, { useMemo, useCallback, useRef, useEffect } from 'react';\nimport { Tree, type TreeNode } from '../../../Tree';\nimport { useFlowUI } from '../context/FlowUIContext';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useDraggablePanel } from '../hooks/useDraggablePanel';\nimport { useResizablePanel } from '../hooks/useResizablePanel';\nimport { PaletteThumbnail } from './ShapePalette';\nimport { buildSvgMarkup } from '../nodes/svgIcons';\nimport { resolveThemeColor } from '../utils/themeColor';\nimport type { FlowNode, FlowLine, FlowDocument, FlowCanvasData } from '../types';\n\nconst SUB_CANVAS_ICON = '⧉';\n\nfunction OutlineNodeIcon({ node }: { node: FlowNode }) {\n if (node.data.childCanvasId) return <span style={{ fontSize: 15 }}>{SUB_CANVAS_ICON}</span>;\n if (node.type === 'shapeSvg' && node.data.svgId) {\n const markup = buildSvgMarkup(node.data.svgId, resolveThemeColor(node.data.color, '#475569') || '#475569');\n return markup ? (\n <img\n src={`data:image/svg+xml;utf8,${encodeURIComponent(markup)}`}\n width={24}\n height={20}\n alt=\"\"\n style={{ display: 'block', objectFit: 'contain' }}\n />\n ) : null;\n }\n if (node.type === 'mediaNode') {\n const text = node.data.mediaKind === 'markdown' ? 'M↓'\n : node.data.mediaKind === 'html' ? '</>'\n : node.data.mediaKind === 'mermaid' ? '⎇'\n : node.data.mediaKind === 'math' ? '√x'\n : node.data.mediaKind === 'image' ? 'IMG'\n : node.data.mediaKind === 'model3d' ? '3D'\n : node.data.mediaKind === 'data' ? '⊞'\n : 'AI';\n return <span style={{ width: 24, textAlign: 'center', fontSize: 10, fontWeight: 700 }}>{text}</span>;\n }\n return <PaletteThumbnail type={node.type} />;\n}\n\nfunction buildNodeTree(nodes: FlowNode[], lines: FlowLine[], doc: FlowDocument): { nodeTree: TreeNode[]; assignedLineIds: Set<string> } {\n const childNodesOf = new Map<string, FlowNode[]>();\n const childLinesOf = new Map<string, FlowLine[]>();\n const assignedNodes = new Set<string>();\n const assignedLineIds = new Set<string>();\n\n for (const n of nodes) {\n if (n.parentId) {\n const list = childNodesOf.get(n.parentId) ?? [];\n list.push(n);\n childNodesOf.set(n.parentId, list);\n assignedNodes.add(n.id);\n }\n }\n for (const l of lines) {\n if (l.parentId) {\n const list = childLinesOf.get(l.parentId) ?? [];\n list.push(l);\n childLinesOf.set(l.parentId, list);\n assignedLineIds.add(l.id);\n }\n }\n\n // Spatial containment only for geometryGroup\n const geoGroups = nodes.filter(n => n.type === 'geometryGroup');\n for (const g of geoGroups) {\n const gx = g.position.x, gy = g.position.y;\n const gw = g.width, gh = g.height;\n for (const n of nodes) {\n if (n.id === g.id || assignedNodes.has(n.id)) continue;\n if (n.position.x >= gx && n.position.y >= gy\n && n.position.x + n.width <= gx + gw\n && n.position.y + n.height <= gy + gh) {\n const list = childNodesOf.get(g.id) ?? [];\n list.push(n);\n childNodesOf.set(g.id, list);\n assignedNodes.add(n.id);\n }\n }\n for (const l of lines) {\n if (assignedLineIds.has(l.id)) continue;\n if (l.points.length > 0 && l.points.every(p =>\n p.x >= gx && p.x <= gx + gw && p.y >= gy && p.y <= gy + gh\n )) {\n const list = childLinesOf.get(g.id) ?? [];\n list.push(l);\n childLinesOf.set(g.id, list);\n assignedLineIds.add(l.id);\n }\n }\n }\n\n const roots = nodes.filter(n => !assignedNodes.has(n.id));\n\n const lineToTree = (l: FlowLine): TreeNode => ({\n id: l.id,\n label: `${l.type} line`,\n icon: '〰',\n });\n\n const toTreeNode = (n: FlowNode): TreeNode => {\n const hasSubCanvas = !!n.data.childCanvasId;\n const icon = <OutlineNodeIcon node={n} />;\n const kidNodes = childNodesOf.get(n.id);\n const kidLines = childLinesOf.get(n.id);\n const item: TreeNode = {\n id: n.id,\n label: n.data.label || n.type,\n icon,\n meta: hasSubCanvas ? { childCanvasId: n.data.childCanvasId } : undefined,\n };\n\n const children: TreeNode[] = [];\n if (kidNodes && kidNodes.length > 0) {\n children.push(...kidNodes.map(toTreeNode));\n }\n if (kidLines && kidLines.length > 0) {\n children.push(...kidLines.map(lineToTree));\n }\n if (hasSubCanvas) {\n const subCanvas = doc.canvases[n.data.childCanvasId!];\n if (subCanvas) {\n const subTree = buildCanvasSubtree(subCanvas, doc, n.data.childCanvasId!);\n if (subTree.length > 0) {\n children.push(...subTree);\n }\n }\n }\n if (children.length > 0) {\n item.children = children;\n }\n return item;\n };\n\n return { nodeTree: roots.map(toTreeNode), assignedLineIds };\n}\n\nfunction buildCanvasSubtree(canvas: FlowCanvasData, doc: FlowDocument, canvasId: string): TreeNode[] {\n if (canvas.nodes.length === 0 && canvas.edges.length === 0 && canvas.lines.length === 0) {\n return [{ id: `_empty_${canvasId}`, label: '(empty sub-canvas)', icon: '○' }];\n }\n return buildNodeTree(canvas.nodes, canvas.lines, doc).nodeTree;\n}\n\n/**\n * Standalone panel (legacy). Prefer using OutlinePanelContent inside a FlowPanelGroup.\n */\nexport default function OutlinePanel() {\n const { showOutline } = useFlowUI();\n const { panelStyle, handleProps } = useDraggablePanel();\n const { panelWidth, resizeHandleProps } = useResizablePanel('left', 180);\n\n if (!showOutline) return null;\n\n return (\n <div className=\"flow-palette\" data-flow-panel style={{ ...(panelStyle ?? { top: 56, left: 'auto', right: 248 + 16 }), width: panelWidth }}>\n <div className=\"flow-panel-resize-handle flow-panel-resize-handle--left\" {...resizeHandleProps} />\n <div\n className=\"flow-panel-drag-handle\"\n {...handleProps}\n style={{ fontSize: 11, fontWeight: 600, marginBottom: 6, color: 'color-mix(in oklch, var(--color-base-content) 50%, transparent)', textTransform: 'uppercase', letterSpacing: '0.05em', cursor: 'grab' }}\n >\n Outline\n </div>\n <div className=\"flow-palette-content\">\n <OutlinePanelContent />\n </div>\n </div>\n );\n}\n\n/** Reusable content for embedding inside FlowPanelGroup tabs. */\nexport function OutlinePanelContent() {\n const { focusOnRect } = useFlowUI();\n const { doc, currentCanvas, currentCanvasId, selection, setSelection, pushCanvas } = useFlowContext();\n\n const treeData = useMemo<TreeNode[]>(() => {\n const { nodeTree, assignedLineIds } = buildNodeTree(currentCanvas.nodes, currentCanvas.lines, doc);\n const edgeItems: TreeNode[] = currentCanvas.edges.map(e => {\n const sn = currentCanvas.nodes.find(n => n.id === e.source);\n const tn = currentCanvas.nodes.find(n => n.id === e.target);\n const srcLabel = sn?.data.label || e.source;\n const tgtLabel = tn?.data.label || e.target;\n return { id: e.id, label: `${srcLabel} → ${tgtLabel}`, icon: '→' };\n });\n const unassignedLines = currentCanvas.lines.filter(l => !assignedLineIds.has(l.id));\n const lineItems: TreeNode[] = unassignedLines.map(l => ({\n id: l.id,\n label: `${l.type} line`,\n icon: '〰',\n }));\n\n const tree: TreeNode[] = [];\n if (nodeTree.length > 0) tree.push({ id: '_nodes', label: `Nodes (${currentCanvas.nodes.length})`, children: nodeTree });\n if (edgeItems.length > 0) tree.push({ id: '_edges', label: `Edges (${edgeItems.length})`, children: edgeItems });\n if (lineItems.length > 0) tree.push({ id: '_lines', label: `Lines (${unassignedLines.length})`, children: lineItems });\n return tree;\n }, [currentCanvas, doc]);\n\n const activeId = useMemo(() => {\n if (selection.nodeIds.size === 1) return [...selection.nodeIds][0];\n if (selection.edgeIds.size === 1) return [...selection.edgeIds][0];\n if (selection.lineIds.size === 1) return [...selection.lineIds][0];\n return undefined;\n }, [selection]);\n\n const defaultExpanded = useMemo(() => {\n const ids: (string | number)[] = ['_nodes', '_edges', '_lines'];\n for (const n of currentCanvas.nodes) {\n if (n.type === 'flowGroup' || n.type === 'geometryGroup' || n.data.isGroup) ids.push(n.id);\n if (n.data.childCanvasId) ids.push(n.id);\n }\n if (activeId) {\n const node = currentCanvas.nodes.find(n => n.id === activeId);\n if (node?.parentId) {\n let pid: string | undefined = node.parentId;\n while (pid) {\n ids.push(pid);\n const parent = currentCanvas.nodes.find(n => n.id === pid);\n pid = parent?.parentId;\n }\n }\n const line = currentCanvas.lines.find(l => l.id === activeId);\n if (line?.parentId) ids.push(line.parentId);\n }\n return ids;\n }, [currentCanvas.nodes, currentCanvas.lines, activeId]);\n\n const handleNodeClick = useCallback((node: TreeNode) => {\n const id = String(node.id);\n if (id.startsWith('_')) return;\n\n const flowNode = currentCanvas.nodes.find(n => n.id === id);\n const flowEdge = currentCanvas.edges.find(e => e.id === id);\n const flowLine = currentCanvas.lines.find(l => l.id === id);\n\n setSelection({\n nodeIds: flowNode ? new Set([id]) : new Set(),\n edgeIds: flowEdge ? new Set([id]) : new Set(),\n lineIds: flowLine ? new Set([id]) : new Set(),\n });\n\n if (flowNode) {\n focusOnRect(flowNode.position.x, flowNode.position.y, flowNode.width, flowNode.height);\n } else if (flowEdge) {\n const sn = currentCanvas.nodes.find(n => n.id === flowEdge.source);\n const tn = currentCanvas.nodes.find(n => n.id === flowEdge.target);\n if (sn && tn) {\n const minX = Math.min(sn.position.x, tn.position.x);\n const minY = Math.min(sn.position.y, tn.position.y);\n const maxX = Math.max(sn.position.x + sn.width, tn.position.x + tn.width);\n const maxY = Math.max(sn.position.y + sn.height, tn.position.y + tn.height);\n focusOnRect(minX, minY, maxX - minX, maxY - minY);\n }\n } else if (flowLine && flowLine.points.length > 0) {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const p of flowLine.points) {\n minX = Math.min(minX, p.x); minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x); maxY = Math.max(maxY, p.y);\n }\n focusOnRect(minX - 20, minY - 20, maxX - minX + 40, maxY - minY + 40);\n }\n }, [currentCanvas, setSelection, focusOnRect]);\n\n const handleNodeDoubleClick = useCallback((node: TreeNode) => {\n const id = String(node.id);\n if (id.startsWith('_')) return;\n const flowNode = currentCanvas.nodes.find(n => n.id === id);\n if (flowNode?.data.childCanvasId) {\n pushCanvas(flowNode.data.childCanvasId);\n }\n }, [currentCanvas.nodes, pushCanvas]);\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!activeId || !containerRef.current) return;\n const timer = setTimeout(() => {\n const el = containerRef.current?.querySelector(`[data-node-id=\"${activeId}\"]`);\n if (el) el.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n }, 60);\n return () => clearTimeout(timer);\n }, [activeId]);\n\n return (\n <div ref={containerRef}>\n <Tree\n data={treeData}\n onNodeClick={handleNodeClick}\n onNodeDoubleClick={handleNodeDoubleClick}\n activeNodeId={activeId}\n defaultExpandedIds={defaultExpanded}\n showSearch={false}\n showExpandAll={false}\n showIcon\n showLine\n indent={16}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OutlinePanel.js","names":[],"sources":["../../../../../src/components/pro/Flow/panels/OutlinePanel.tsx"],"sourcesContent":["import React, { useMemo, useCallback, useRef, useEffect } from 'react';\nimport { Tree, type TreeNode } from '../../../Tree';\nimport { useFlowUI } from '../context/FlowUIContext';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useDraggablePanel } from '../hooks/useDraggablePanel';\nimport { useResizablePanel } from '../hooks/useResizablePanel';\nimport { PaletteThumbnail } from './ShapePalette';\nimport { buildSvgMarkup } from '../nodes/svgIcons';\nimport { resolveThemeColor } from '../utils/themeColor';\nimport type { FlowNode, FlowLine, FlowDocument, FlowCanvasData } from '../types';\n\nconst SUB_CANVAS_ICON = '⧉';\n\nfunction OutlineNodeIcon({ node }: { node: FlowNode }) {\n if (node.data.childCanvasId) return <span style={{ fontSize: 15 }}>{SUB_CANVAS_ICON}</span>;\n if (node.type === 'shapeSvg' && node.data.svgId) {\n const markup = buildSvgMarkup(node.data.svgId, resolveThemeColor(node.data.color, '#475569') || '#475569');\n return markup ? (\n <img\n src={`data:image/svg+xml;utf8,${encodeURIComponent(markup)}`}\n width={24}\n height={20}\n alt=\"\"\n style={{ display: 'block', objectFit: 'contain' }}\n />\n ) : null;\n }\n if (node.type === 'mediaNode') {\n const text = node.data.mediaKind === 'markdown' ? 'M↓'\n : node.data.mediaKind === 'html' ? '</>'\n : node.data.mediaKind === 'mermaid' ? '⎇'\n : node.data.mediaKind === 'math' ? '√x'\n : node.data.mediaKind === 'image' ? 'IMG'\n : node.data.mediaKind === 'model3d' ? '3D'\n : node.data.mediaKind === 'data' ? '⊞'\n : 'AI';\n return <span style={{ width: 24, textAlign: 'center', fontSize: 10, fontWeight: 700 }}>{text}</span>;\n }\n return <PaletteThumbnail type={node.type} />;\n}\n\nfunction buildNodeTree(nodes: FlowNode[], lines: FlowLine[], doc: FlowDocument): { nodeTree: TreeNode[]; assignedLineIds: Set<string> } {\n const childNodesOf = new Map<string, FlowNode[]>();\n const childLinesOf = new Map<string, FlowLine[]>();\n const assignedNodes = new Set<string>();\n const assignedLineIds = new Set<string>();\n\n for (const n of nodes) {\n if (n.parentId) {\n const list = childNodesOf.get(n.parentId) ?? [];\n list.push(n);\n childNodesOf.set(n.parentId, list);\n assignedNodes.add(n.id);\n }\n }\n for (const l of lines) {\n if (l.parentId) {\n const list = childLinesOf.get(l.parentId) ?? [];\n list.push(l);\n childLinesOf.set(l.parentId, list);\n assignedLineIds.add(l.id);\n }\n }\n\n // Spatial containment only for geometryGroup\n const geoGroups = nodes.filter(n => n.type === 'geometryGroup');\n for (const g of geoGroups) {\n const gx = g.position.x, gy = g.position.y;\n const gw = g.width, gh = g.height;\n for (const n of nodes) {\n if (n.id === g.id || assignedNodes.has(n.id)) continue;\n if (n.position.x >= gx && n.position.y >= gy\n && n.position.x + n.width <= gx + gw\n && n.position.y + n.height <= gy + gh) {\n const list = childNodesOf.get(g.id) ?? [];\n list.push(n);\n childNodesOf.set(g.id, list);\n assignedNodes.add(n.id);\n }\n }\n for (const l of lines) {\n if (assignedLineIds.has(l.id)) continue;\n if (l.points.length > 0 && l.points.every(p =>\n p.x >= gx && p.x <= gx + gw && p.y >= gy && p.y <= gy + gh\n )) {\n const list = childLinesOf.get(g.id) ?? [];\n list.push(l);\n childLinesOf.set(g.id, list);\n assignedLineIds.add(l.id);\n }\n }\n }\n\n const roots = nodes.filter(n => !assignedNodes.has(n.id));\n\n const lineToTree = (l: FlowLine): TreeNode => ({\n id: l.id,\n label: `${l.type} line`,\n icon: '〰',\n });\n\n const toTreeNode = (n: FlowNode): TreeNode => {\n const hasSubCanvas = !!n.data.childCanvasId;\n const icon = <OutlineNodeIcon node={n} />;\n const kidNodes = childNodesOf.get(n.id);\n const kidLines = childLinesOf.get(n.id);\n const item: TreeNode = {\n id: n.id,\n label: n.data.label || n.type,\n icon,\n meta: hasSubCanvas ? { childCanvasId: n.data.childCanvasId } : undefined,\n };\n\n const children: TreeNode[] = [];\n if (kidNodes && kidNodes.length > 0) {\n children.push(...kidNodes.map(toTreeNode));\n }\n if (kidLines && kidLines.length > 0) {\n children.push(...kidLines.map(lineToTree));\n }\n if (hasSubCanvas) {\n const subCanvas = doc.canvases[n.data.childCanvasId!];\n if (subCanvas) {\n const subTree = buildCanvasSubtree(subCanvas, doc, n.data.childCanvasId!);\n if (subTree.length > 0) {\n children.push(...subTree);\n }\n }\n }\n if (children.length > 0) {\n item.children = children;\n }\n return item;\n };\n\n return { nodeTree: roots.map(toTreeNode), assignedLineIds };\n}\n\nfunction buildCanvasSubtree(canvas: FlowCanvasData, doc: FlowDocument, canvasId: string): TreeNode[] {\n if (canvas.nodes.length === 0 && canvas.edges.length === 0 && canvas.lines.length === 0) {\n return [{ id: `_empty_${canvasId}`, label: '(empty sub-canvas)', icon: '○' }];\n }\n return buildNodeTree(canvas.nodes, canvas.lines, doc).nodeTree;\n}\n\n/**\n * Standalone panel (legacy). Prefer using OutlinePanelContent inside a FlowPanelGroup.\n */\nexport default function OutlinePanel() {\n const { showOutline } = useFlowUI();\n const { panelStyle, handleProps } = useDraggablePanel();\n const { panelWidth, resizeHandleProps } = useResizablePanel('left', 180);\n\n if (!showOutline) return null;\n\n return (\n <div className=\"flow-palette\" data-flow-panel style={{ ...(panelStyle ?? { top: 56, left: 'auto', right: 248 + 16 }), width: panelWidth }}>\n <div className=\"flow-panel-resize-handle flow-panel-resize-handle--left\" {...resizeHandleProps} />\n <div\n className=\"flow-panel-drag-handle\"\n {...handleProps}\n style={{ fontSize: 11, fontWeight: 600, marginBottom: 6, color: 'color-mix(in oklch, var(--color-base-content) 50%, transparent)', textTransform: 'uppercase', letterSpacing: '0.05em', cursor: 'grab' }}\n >\n Outline\n </div>\n <div className=\"flow-palette-content\">\n <OutlinePanelContent />\n </div>\n </div>\n );\n}\n\n/** Reusable content for embedding inside FlowPanelGroup tabs. */\nexport function OutlinePanelContent() {\n const { focusOnRect } = useFlowUI();\n const { doc, currentCanvas, currentCanvasId, selection, setSelection, pushCanvas } = useFlowContext();\n\n const treeData = useMemo<TreeNode[]>(() => {\n const { nodeTree, assignedLineIds } = buildNodeTree(currentCanvas.nodes, currentCanvas.lines, doc);\n const edgeItems: TreeNode[] = currentCanvas.edges.map(e => {\n const sn = currentCanvas.nodes.find(n => n.id === e.source);\n const tn = currentCanvas.nodes.find(n => n.id === e.target);\n const srcLabel = sn?.data.label || e.source;\n const tgtLabel = tn?.data.label || e.target;\n return { id: e.id, label: `${srcLabel} → ${tgtLabel}`, icon: '→' };\n });\n const unassignedLines = currentCanvas.lines.filter(l => !assignedLineIds.has(l.id));\n const lineItems: TreeNode[] = unassignedLines.map(l => ({\n id: l.id,\n label: `${l.type} line`,\n icon: '〰',\n }));\n\n const tree: TreeNode[] = [];\n if (nodeTree.length > 0) tree.push({ id: '_nodes', label: `Nodes (${currentCanvas.nodes.length})`, children: nodeTree });\n if (edgeItems.length > 0) tree.push({ id: '_edges', label: `Edges (${edgeItems.length})`, children: edgeItems });\n if (lineItems.length > 0) tree.push({ id: '_lines', label: `Lines (${unassignedLines.length})`, children: lineItems });\n return tree;\n }, [currentCanvas, doc]);\n\n const activeId = useMemo(() => {\n if (selection.nodeIds.size === 1) return [...selection.nodeIds][0];\n if (selection.edgeIds.size === 1) return [...selection.edgeIds][0];\n if (selection.lineIds.size === 1) return [...selection.lineIds][0];\n return undefined;\n }, [selection]);\n\n const defaultExpanded = useMemo(() => {\n const ids: (string | number)[] = ['_nodes', '_edges', '_lines'];\n for (const n of currentCanvas.nodes) {\n if (n.type === 'flowGroup' || n.type === 'geometryGroup' || n.data.isGroup) ids.push(n.id);\n if (n.data.childCanvasId) ids.push(n.id);\n }\n if (activeId) {\n const node = currentCanvas.nodes.find(n => n.id === activeId);\n if (node?.parentId) {\n let pid: string | undefined = node.parentId;\n while (pid) {\n ids.push(pid);\n const parent = currentCanvas.nodes.find(n => n.id === pid);\n pid = parent?.parentId;\n }\n }\n const line = currentCanvas.lines.find(l => l.id === activeId);\n if (line?.parentId) ids.push(line.parentId);\n }\n return ids;\n }, [currentCanvas.nodes, currentCanvas.lines, activeId]);\n\n const handleNodeClick = useCallback((node: TreeNode) => {\n const id = String(node.id);\n if (id.startsWith('_')) return;\n\n const flowNode = currentCanvas.nodes.find(n => n.id === id);\n const flowEdge = currentCanvas.edges.find(e => e.id === id);\n const flowLine = currentCanvas.lines.find(l => l.id === id);\n\n setSelection({\n nodeIds: flowNode ? new Set([id]) : new Set(),\n edgeIds: flowEdge ? new Set([id]) : new Set(),\n lineIds: flowLine ? new Set([id]) : new Set(),\n });\n\n if (flowNode) {\n focusOnRect(flowNode.position.x, flowNode.position.y, flowNode.width, flowNode.height);\n } else if (flowEdge) {\n const sn = currentCanvas.nodes.find(n => n.id === flowEdge.source);\n const tn = currentCanvas.nodes.find(n => n.id === flowEdge.target);\n if (sn && tn) {\n const minX = Math.min(sn.position.x, tn.position.x);\n const minY = Math.min(sn.position.y, tn.position.y);\n const maxX = Math.max(sn.position.x + sn.width, tn.position.x + tn.width);\n const maxY = Math.max(sn.position.y + sn.height, tn.position.y + tn.height);\n focusOnRect(minX, minY, maxX - minX, maxY - minY);\n }\n } else if (flowLine && flowLine.points.length > 0) {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const p of flowLine.points) {\n minX = Math.min(minX, p.x); minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x); maxY = Math.max(maxY, p.y);\n }\n focusOnRect(minX - 20, minY - 20, maxX - minX + 40, maxY - minY + 40);\n }\n }, [currentCanvas, setSelection, focusOnRect]);\n\n const handleNodeDoubleClick = useCallback((node: TreeNode) => {\n const id = String(node.id);\n if (id.startsWith('_')) return;\n const flowNode = currentCanvas.nodes.find(n => n.id === id);\n if (flowNode?.data.childCanvasId) {\n pushCanvas(flowNode.data.childCanvasId);\n }\n }, [currentCanvas.nodes, pushCanvas]);\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!activeId || !containerRef.current) return;\n const timer = setTimeout(() => {\n const el = containerRef.current?.querySelector(`[data-node-id=\"${activeId}\"]`);\n if (el) el.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n }, 60);\n return () => clearTimeout(timer);\n }, [activeId]);\n\n return (\n <div ref={containerRef}>\n <Tree\n data={treeData}\n onNodeClick={handleNodeClick}\n onNodeDoubleClick={handleNodeDoubleClick}\n activeNodeId={activeId}\n defaultExpandedIds={defaultExpanded}\n showSearch={false}\n showExpandAll={false}\n showIcon\n showLine\n indent={16}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { useFlowContext as
|
|
2
|
-
import { useFlowUI as
|
|
3
|
-
import { anchorFocusRect as
|
|
4
|
-
import { useCallback as v, useEffect as D, useMemo as
|
|
1
|
+
import { useFlowContext as I } from "../context/FlowContext.js";
|
|
2
|
+
import { useFlowUI as M } from "../context/FlowUIContext.js";
|
|
3
|
+
import { anchorFocusRect as L, canvasStackTo as S, getDocumentSlides as U } from "../nodes/animAnchors.js";
|
|
4
|
+
import { useCallback as v, useEffect as D, useMemo as j, useRef as N, useState as K } from "react";
|
|
5
5
|
import { jsx as i, jsxs as m } from "react/jsx-runtime";
|
|
6
|
-
function
|
|
7
|
-
const { doc: u, setDoc: k, currentCanvasId: y } =
|
|
6
|
+
function G() {
|
|
7
|
+
const { doc: u, setDoc: k, currentCanvasId: y } = I(), { presentation: r, setPresentation: l, focusOnRect: b } = M(), x = N(null), p = j(() => U(u), [u]), s = p.length, a = r !== null, e = r?.current ?? 0, [C, g] = K(null), A = N(u);
|
|
8
8
|
A.current = u;
|
|
9
9
|
const d = v(() => {
|
|
10
10
|
l(null), document.fullscreenElement && document.exitFullscreen().catch(() => {
|
|
@@ -22,15 +22,15 @@ function B() {
|
|
|
22
22
|
});
|
|
23
23
|
const c = p[t];
|
|
24
24
|
if (!c) return;
|
|
25
|
-
const f = A.current, h =
|
|
25
|
+
const f = A.current, h = S(f, c.canvasId), E = c.canvasId === y;
|
|
26
26
|
if (!E && h) {
|
|
27
|
-
const
|
|
28
|
-
g(
|
|
29
|
-
...
|
|
27
|
+
const F = h.length > f.stack.length;
|
|
28
|
+
g(F ? "in" : "out"), k((P) => ({
|
|
29
|
+
...P,
|
|
30
30
|
stack: h
|
|
31
31
|
})), window.setTimeout(() => g(null), 480);
|
|
32
32
|
}
|
|
33
|
-
const w =
|
|
33
|
+
const w = L(c.node);
|
|
34
34
|
window.setTimeout(() => b(w.x, w.y, w.w, w.h, {
|
|
35
35
|
padding: 8,
|
|
36
36
|
maxZoom: 4,
|
|
@@ -45,7 +45,7 @@ function B() {
|
|
|
45
45
|
y,
|
|
46
46
|
k
|
|
47
47
|
]);
|
|
48
|
-
|
|
48
|
+
if (D(() => {
|
|
49
49
|
if (a && (R(), s > 0)) {
|
|
50
50
|
o(e);
|
|
51
51
|
const n = window.setTimeout(() => o(e), 180);
|
|
@@ -76,9 +76,11 @@ function B() {
|
|
|
76
76
|
e,
|
|
77
77
|
o,
|
|
78
78
|
d
|
|
79
|
-
]), a
|
|
79
|
+
]), !a) return null;
|
|
80
|
+
const T = C ? `flow-presentation--${C}` : "";
|
|
81
|
+
return /* @__PURE__ */ m("div", {
|
|
80
82
|
ref: x,
|
|
81
|
-
className: `flow-presentation ${
|
|
83
|
+
className: `flow-presentation ${T}`,
|
|
82
84
|
onClick: d,
|
|
83
85
|
children: [/* @__PURE__ */ i("div", {
|
|
84
86
|
className: "flow-presentation-slide-fx",
|
|
@@ -132,10 +134,10 @@ function B() {
|
|
|
132
134
|
]
|
|
133
135
|
})]
|
|
134
136
|
})]
|
|
135
|
-
})
|
|
137
|
+
});
|
|
136
138
|
}
|
|
137
139
|
export {
|
|
138
|
-
|
|
140
|
+
G as PresentationMode
|
|
139
141
|
};
|
|
140
142
|
|
|
141
143
|
//# sourceMappingURL=PresentationMode.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PresentationMode.js","names":[],"sources":["../../../../../src/components/pro/Flow/panels/PresentationMode.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\nimport { getDocumentSlides, anchorFocusRect, canvasStackTo } from '../nodes/animAnchors';\n\n/**\n * PPT-style presentation mode.\n *\n * While active the editor is read-only (all panels/toolbars are hidden) and the\n * viewport animates between animation anchors (slides). Anchors across the whole\n * document — including nested sub-canvases — share a single sequence, ordered by\n * stable creation index. Navigating to a slide on another canvas drills into or\n * out of that canvas before focusing the slide.\n *\n * Keys: →/↓/Space/PageDown = next, ←/↑/Shift+Space/PageUp = previous, Esc = exit.\n */\nexport function PresentationMode() {\n const { doc, setDoc, currentCanvasId } = useFlowContext();\n const { presentation, setPresentation, focusOnRect } = useFlowUI();\n const overlayRef = useRef<HTMLDivElement>(null);\n\n const slides = useMemo(() => getDocumentSlides(doc), [doc]);\n const count = slides.length;\n const active = presentation !== null;\n const current = presentation?.current ?? 0;\n const [transition, setTransition] = useState<'in' | 'out' | null>(null);\n\n const docRef = useRef(doc);\n docRef.current = doc;\n\n const exit = useCallback(() => {\n setPresentation(null);\n if (document.fullscreenElement) void document.exitFullscreen().catch(() => {});\n }, [setPresentation]);\n\n const enterFullscreen = useCallback(() => {\n const root = overlayRef.current?.closest('.flow-container') as HTMLElement | null;\n if (root && !document.fullscreenElement) {\n void root.requestFullscreen().catch(() => {});\n }\n }, []);\n\n const goTo = useCallback(\n (index: number) => {\n if (count === 0) return;\n const clamped = Math.max(0, Math.min(count - 1, index));\n setPresentation({\n current: clamped,\n showAnchor: presentation?.showAnchor ?? false,\n });\n const slide = slides[clamped];\n if (!slide) return;\n\n const d = docRef.current;\n const stack = canvasStackTo(d, slide.canvasId);\n\n // Decide drill direction by comparing with the current stack top.\n const onCurrentCanvas = slide.canvasId === currentCanvasId;\n if (!onCurrentCanvas && stack) {\n const deeper = stack.length > d.stack.length;\n setTransition(deeper ? 'in' : 'out');\n // Navigate the canvas stack to the slide's canvas.\n setDoc((prev) => ({ ...prev, stack }));\n // Clear the transition marker shortly after it starts.\n window.setTimeout(() => setTransition(null), 480);\n }\n\n const r = anchorFocusRect(slide.node);\n // Presentation uses the exact anchor frame with only a small screen-space\n // inset. The zoom is recomputed for every slide and viewport size.\n window.setTimeout(\n () => focusOnRect(r.x, r.y, r.w, r.h, { padding: 8, maxZoom: 4, duration: 350 }),\n onCurrentCanvas ? 0 : 30,\n );\n },\n [slides, count, presentation?.showAnchor, setPresentation, focusOnRect, currentCanvasId, setDoc],\n );\n\n // Entering: use the full display area and snap to the first slide. Refit once\n // fullscreen settles because the canvas dimensions change asynchronously.\n useEffect(() => {\n if (!active) return;\n enterFullscreen();\n if (count > 0) {\n goTo(current);\n const timer = window.setTimeout(() => goTo(current), 180);\n return () => window.clearTimeout(timer);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [active]);\n\n\n\n // Keyboard navigation (capture phase so arrows work over the canvas).\n useEffect(() => {\n if (!active) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n exit();\n return;\n }\n const next = ['ArrowRight', 'ArrowDown', 'PageDown', ' '];\n const prev = ['ArrowLeft', 'ArrowUp', 'PageUp'];\n if (next.includes(e.key)) {\n e.preventDefault();\n if (e.key === ' ' && e.shiftKey) goTo(current - 1);\n else goTo(current + 1);\n } else if (prev.includes(e.key)) {\n e.preventDefault();\n goTo(current - 1);\n }\n };\n window.addEventListener('keydown', onKey, true);\n return () => window.removeEventListener('keydown', onKey, true);\n }, [active, current, goTo, exit]);\n\n if (!active) return null;\n\n const cls = transition ? `flow-presentation--${transition}` : '';\n return (\n <div ref={overlayRef} className={`flow-presentation ${cls}`} onClick={exit}>\n <div className=\"flow-presentation-slide-fx\" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }} />\n {count === 0 ? (\n <div className=\"flow-presentation-empty\">画布中没有动画锚点,点击退出</div>\n ) : (\n <div className=\"flow-presentation-hud\" onClick={(e) => e.stopPropagation()}>\n <span className=\"flow-presentation-counter\">\n {current + 1} / {count}\n </span>\n <div className=\"flow-presentation-nav\">\n <button type=\"button\" onClick={() => goTo(current - 1)} disabled={current <= 0}>‹ 上一张</button>\n <button type=\"button\" onClick={() => goTo(current + 1)} disabled={current >= count - 1}>下一张 ›</button>\n <button\n type=\"button\"\n onClick={() => setPresentation({\n current,\n showAnchor: !(presentation?.showAnchor ?? false),\n })}\n >\n {presentation?.showAnchor ? '隐藏锚点' : '显示锚点'}\n </button>\n <button type=\"button\" className=\"flow-presentation-exit\" onClick={exit}>退出 Esc</button>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAgBA,SAAgB,IAAmB;AACjC,QAAM,EAAE,KAAA,GAAK,QAAA,GAAQ,iBAAA,EAAA,IAAoB,EAAe,GAClD,EAAE,cAAA,GAAc,iBAAA,GAAiB,aAAA,EAAA,IAAgB,EAAU,GAC3D,IAAa,EAAuB,IAAI,GAExC,IAAS,EAAA,MAAc,EAAkB,CAAG,GAAG,CAAC,CAAG,CAAC,GACpD,IAAQ,EAAO,QACf,IAAS,MAAiB,MAC1B,IAAU,GAAc,WAAW,GACnC,CAAC,GAAY,CAAA,IAAiB,EAA8B,IAAI,GAEhE,IAAS,EAAO,CAAG;AACzB,EAAA,EAAO,UAAU;AAEjB,QAAM,IAAO,EAAA,MAAkB;AAC7B,IAAA,EAAgB,IAAI,GAChB,SAAS,qBAAmB,SAAc,eAAe,EAAE,MAAA,MAAY;AAAA,IAAC,CAAC;AAAA,EAC/E,GAAG,CAAC,CAAe,CAAC,GAEd,IAAkB,EAAA,MAAkB;AACxC,UAAM,IAAO,EAAW,SAAS,QAAQ,iBAAiB;AAC1D,IAAI,KAAQ,CAAC,SAAS,qBACpB,EAAU,kBAAkB,EAAE,MAAA,MAAY;AAAA,IAAC,CAAC;AAAA,EAEhD,GAAG,CAAC,CAAC,GAEC,IAAO,EAAA,CACV,MAAkB;AACjB,QAAI,MAAU,EAAG;AACjB,UAAM,IAAU,KAAK,IAAI,GAAG,KAAK,IAAI,IAAQ,GAAG,CAAK,CAAC;AACtD,IAAA,EAAgB;AAAA,MACd,SAAS;AAAA,MACT,YAAY,GAAc,cAAc;AAAA,IAC1C,CAAC;AACD,UAAM,IAAQ,EAAO,CAAA;AACrB,QAAI,CAAC,EAAO;AAEZ,UAAM,IAAI,EAAO,SACX,IAAQ,EAAc,GAAG,EAAM,QAAQ,GAGvC,IAAkB,EAAM,aAAa;AAC3C,QAAI,CAAC,KAAmB,GAAO;AAC7B,YAAM,IAAS,EAAM,SAAS,EAAE,MAAM;AACtC,MAAA,EAAc,IAAS,OAAO,KAAK,GAEnC,EAAA,CAAQ,OAAU;AAAA,QAAE,GAAG;AAAA,QAAM,OAAA;AAAA,MAAM,EAAE,GAErC,OAAO,WAAA,MAAiB,EAAc,IAAI,GAAG,GAAG;AAAA,IAClD;AAEA,UAAM,IAAI,EAAgB,EAAM,IAAI;AAGpC,WAAO,WAAA,MACC,EAAY,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;AAAA,MAAE,SAAS;AAAA,MAAG,SAAS;AAAA,MAAG,UAAU;AAAA,IAAI,CAAC,GAC/E,IAAkB,IAAI,EACxB;AAAA,EACF,GACA;AAAA,IAAC;AAAA,IAAQ;AAAA,IAAO,GAAc;AAAA,IAAY;AAAA,IAAiB;AAAA,IAAa;AAAA,IAAiB;AAAA,EAAM,CACjG;AAyCA,
|
|
1
|
+
{"version":3,"file":"PresentationMode.js","names":[],"sources":["../../../../../src/components/pro/Flow/panels/PresentationMode.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\nimport { getDocumentSlides, anchorFocusRect, canvasStackTo } from '../nodes/animAnchors';\n\n/**\n * PPT-style presentation mode.\n *\n * While active the editor is read-only (all panels/toolbars are hidden) and the\n * viewport animates between animation anchors (slides). Anchors across the whole\n * document — including nested sub-canvases — share a single sequence, ordered by\n * stable creation index. Navigating to a slide on another canvas drills into or\n * out of that canvas before focusing the slide.\n *\n * Keys: →/↓/Space/PageDown = next, ←/↑/Shift+Space/PageUp = previous, Esc = exit.\n */\nexport function PresentationMode() {\n const { doc, setDoc, currentCanvasId } = useFlowContext();\n const { presentation, setPresentation, focusOnRect } = useFlowUI();\n const overlayRef = useRef<HTMLDivElement>(null);\n\n const slides = useMemo(() => getDocumentSlides(doc), [doc]);\n const count = slides.length;\n const active = presentation !== null;\n const current = presentation?.current ?? 0;\n const [transition, setTransition] = useState<'in' | 'out' | null>(null);\n\n const docRef = useRef(doc);\n docRef.current = doc;\n\n const exit = useCallback(() => {\n setPresentation(null);\n if (document.fullscreenElement) void document.exitFullscreen().catch(() => {});\n }, [setPresentation]);\n\n const enterFullscreen = useCallback(() => {\n const root = overlayRef.current?.closest('.flow-container') as HTMLElement | null;\n if (root && !document.fullscreenElement) {\n void root.requestFullscreen().catch(() => {});\n }\n }, []);\n\n const goTo = useCallback(\n (index: number) => {\n if (count === 0) return;\n const clamped = Math.max(0, Math.min(count - 1, index));\n setPresentation({\n current: clamped,\n showAnchor: presentation?.showAnchor ?? false,\n });\n const slide = slides[clamped];\n if (!slide) return;\n\n const d = docRef.current;\n const stack = canvasStackTo(d, slide.canvasId);\n\n // Decide drill direction by comparing with the current stack top.\n const onCurrentCanvas = slide.canvasId === currentCanvasId;\n if (!onCurrentCanvas && stack) {\n const deeper = stack.length > d.stack.length;\n setTransition(deeper ? 'in' : 'out');\n // Navigate the canvas stack to the slide's canvas.\n setDoc((prev) => ({ ...prev, stack }));\n // Clear the transition marker shortly after it starts.\n window.setTimeout(() => setTransition(null), 480);\n }\n\n const r = anchorFocusRect(slide.node);\n // Presentation uses the exact anchor frame with only a small screen-space\n // inset. The zoom is recomputed for every slide and viewport size.\n window.setTimeout(\n () => focusOnRect(r.x, r.y, r.w, r.h, { padding: 8, maxZoom: 4, duration: 350 }),\n onCurrentCanvas ? 0 : 30,\n );\n },\n [slides, count, presentation?.showAnchor, setPresentation, focusOnRect, currentCanvasId, setDoc],\n );\n\n // Entering: use the full display area and snap to the first slide. Refit once\n // fullscreen settles because the canvas dimensions change asynchronously.\n useEffect(() => {\n if (!active) return;\n enterFullscreen();\n if (count > 0) {\n goTo(current);\n const timer = window.setTimeout(() => goTo(current), 180);\n return () => window.clearTimeout(timer);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [active]);\n\n\n\n // Keyboard navigation (capture phase so arrows work over the canvas).\n useEffect(() => {\n if (!active) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n exit();\n return;\n }\n const next = ['ArrowRight', 'ArrowDown', 'PageDown', ' '];\n const prev = ['ArrowLeft', 'ArrowUp', 'PageUp'];\n if (next.includes(e.key)) {\n e.preventDefault();\n if (e.key === ' ' && e.shiftKey) goTo(current - 1);\n else goTo(current + 1);\n } else if (prev.includes(e.key)) {\n e.preventDefault();\n goTo(current - 1);\n }\n };\n window.addEventListener('keydown', onKey, true);\n return () => window.removeEventListener('keydown', onKey, true);\n }, [active, current, goTo, exit]);\n\n if (!active) return null;\n\n const cls = transition ? `flow-presentation--${transition}` : '';\n return (\n <div ref={overlayRef} className={`flow-presentation ${cls}`} onClick={exit}>\n <div className=\"flow-presentation-slide-fx\" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }} />\n {count === 0 ? (\n <div className=\"flow-presentation-empty\">画布中没有动画锚点,点击退出</div>\n ) : (\n <div className=\"flow-presentation-hud\" onClick={(e) => e.stopPropagation()}>\n <span className=\"flow-presentation-counter\">\n {current + 1} / {count}\n </span>\n <div className=\"flow-presentation-nav\">\n <button type=\"button\" onClick={() => goTo(current - 1)} disabled={current <= 0}>‹ 上一张</button>\n <button type=\"button\" onClick={() => goTo(current + 1)} disabled={current >= count - 1}>下一张 ›</button>\n <button\n type=\"button\"\n onClick={() => setPresentation({\n current,\n showAnchor: !(presentation?.showAnchor ?? false),\n })}\n >\n {presentation?.showAnchor ? '隐藏锚点' : '显示锚点'}\n </button>\n <button type=\"button\" className=\"flow-presentation-exit\" onClick={exit}>退出 Esc</button>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAgBA,SAAgB,IAAmB;AACjC,QAAM,EAAE,KAAA,GAAK,QAAA,GAAQ,iBAAA,EAAA,IAAoB,EAAe,GAClD,EAAE,cAAA,GAAc,iBAAA,GAAiB,aAAA,EAAA,IAAgB,EAAU,GAC3D,IAAa,EAAuB,IAAI,GAExC,IAAS,EAAA,MAAc,EAAkB,CAAG,GAAG,CAAC,CAAG,CAAC,GACpD,IAAQ,EAAO,QACf,IAAS,MAAiB,MAC1B,IAAU,GAAc,WAAW,GACnC,CAAC,GAAY,CAAA,IAAiB,EAA8B,IAAI,GAEhE,IAAS,EAAO,CAAG;AACzB,EAAA,EAAO,UAAU;AAEjB,QAAM,IAAO,EAAA,MAAkB;AAC7B,IAAA,EAAgB,IAAI,GAChB,SAAS,qBAAmB,SAAc,eAAe,EAAE,MAAA,MAAY;AAAA,IAAC,CAAC;AAAA,EAC/E,GAAG,CAAC,CAAe,CAAC,GAEd,IAAkB,EAAA,MAAkB;AACxC,UAAM,IAAO,EAAW,SAAS,QAAQ,iBAAiB;AAC1D,IAAI,KAAQ,CAAC,SAAS,qBACpB,EAAU,kBAAkB,EAAE,MAAA,MAAY;AAAA,IAAC,CAAC;AAAA,EAEhD,GAAG,CAAC,CAAC,GAEC,IAAO,EAAA,CACV,MAAkB;AACjB,QAAI,MAAU,EAAG;AACjB,UAAM,IAAU,KAAK,IAAI,GAAG,KAAK,IAAI,IAAQ,GAAG,CAAK,CAAC;AACtD,IAAA,EAAgB;AAAA,MACd,SAAS;AAAA,MACT,YAAY,GAAc,cAAc;AAAA,IAC1C,CAAC;AACD,UAAM,IAAQ,EAAO,CAAA;AACrB,QAAI,CAAC,EAAO;AAEZ,UAAM,IAAI,EAAO,SACX,IAAQ,EAAc,GAAG,EAAM,QAAQ,GAGvC,IAAkB,EAAM,aAAa;AAC3C,QAAI,CAAC,KAAmB,GAAO;AAC7B,YAAM,IAAS,EAAM,SAAS,EAAE,MAAM;AACtC,MAAA,EAAc,IAAS,OAAO,KAAK,GAEnC,EAAA,CAAQ,OAAU;AAAA,QAAE,GAAG;AAAA,QAAM,OAAA;AAAA,MAAM,EAAE,GAErC,OAAO,WAAA,MAAiB,EAAc,IAAI,GAAG,GAAG;AAAA,IAClD;AAEA,UAAM,IAAI,EAAgB,EAAM,IAAI;AAGpC,WAAO,WAAA,MACC,EAAY,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;AAAA,MAAE,SAAS;AAAA,MAAG,SAAS;AAAA,MAAG,UAAU;AAAA,IAAI,CAAC,GAC/E,IAAkB,IAAI,EACxB;AAAA,EACF,GACA;AAAA,IAAC;AAAA,IAAQ;AAAA,IAAO,GAAc;AAAA,IAAY;AAAA,IAAiB;AAAA,IAAa;AAAA,IAAiB;AAAA,EAAM,CACjG;AAyCA,MArCA,EAAA,MAAgB;AACd,QAAK,MACL,EAAgB,GACZ,IAAQ,IAAG;AACb,MAAA,EAAK,CAAO;AACZ,YAAM,IAAQ,OAAO,WAAA,MAAiB,EAAK,CAAO,GAAG,GAAG;AACxD,aAAA,MAAa,OAAO,aAAa,CAAK;AAAA,IACxC;AAAA,EAEF,GAAG,CAAC,CAAM,CAAC,GAKX,EAAA,MAAgB;AACd,QAAI,CAAC,EAAQ;AACb,UAAM,IAAA,CAAS,MAAqB;AAClC,UAAI,EAAE,QAAQ,UAAU;AACtB,QAAA,EAAE,eAAe,GACjB,EAAK;AACL;AAAA,MACF;AACA,YAAM,IAAO;AAAA,QAAC;AAAA,QAAc;AAAA,QAAa;AAAA,QAAY;AAAA,MAAG,GAClD,IAAO;AAAA,QAAC;AAAA,QAAa;AAAA,QAAW;AAAA,MAAQ;AAC9C,MAAI,EAAK,SAAS,EAAE,GAAG,KACrB,EAAE,eAAe,GACb,EAAE,QAAQ,OAAO,EAAE,WAAU,EAAK,IAAU,CAAC,IAC5C,EAAK,IAAU,CAAC,KACZ,EAAK,SAAS,EAAE,GAAG,MAC5B,EAAE,eAAe,GACjB,EAAK,IAAU,CAAC;AAAA,IAEpB;AACA,kBAAO,iBAAiB,WAAW,GAAO,EAAI,GAC9C,MAAa,OAAO,oBAAoB,WAAW,GAAO,EAAI;AAAA,EAChE,GAAG;AAAA,IAAC;AAAA,IAAQ;AAAA,IAAS;AAAA,IAAM;AAAA,EAAI,CAAC,GAE5B,CAAC,EAAQ,QAAO;AAEpB,QAAM,IAAM,IAAa,sBAAsB,CAAA,KAAe;AAC9D,SACE,gBAAA,EAAC,OAAD;AAAA,IAAK,KAAK;AAAA,IAAY,WAAW,qBAAqB,CAAA;AAAA,IAAO,SAAS;AAAA,IAAtE,UAAA,CACE,gBAAA,EAAC,OAAD;AAAA,MAAK,WAAU;AAAA,MAA6B,OAAO;AAAA,QAAE,UAAU;AAAA,QAAY,OAAO;AAAA,QAAG,eAAe;AAAA,MAAO;AAAA,IAAI,CAAA,GAC9G,MAAU,IACT,gBAAA,EAAC,OAAD;AAAA,MAAK,WAAU;AAAA,MAA0B,UAAA;AAAA,IAAmB,CAAA,IAE5D,gBAAA,EAAC,OAAD;AAAA,MAAK,WAAU;AAAA,MAAwB,SAAA,CAAU,MAAM,EAAE,gBAAgB;AAAA,MAAzE,UAAA,CACE,gBAAA,EAAC,QAAD;AAAA,QAAM,WAAU;AAAA,QAAhB,UAAA;AAAA,UACG,IAAU;AAAA,UAAE;AAAA,UAAI;AAAA,QACb;AAAA,MACN,CAAA,GAAA,gBAAA,EAAC,OAAD;AAAA,QAAK,WAAU;AAAA,QAAf,UAAA;AAAA,UACE,gBAAA,EAAC,UAAD;AAAA,YAAQ,MAAK;AAAA,YAAS,SAAA,MAAe,EAAK,IAAU,CAAC;AAAA,YAAG,UAAU,KAAW;AAAA,YAAG,UAAA;AAAA,UAAa,CAAA;AAAA,UAC7F,gBAAA,EAAC,UAAD;AAAA,YAAQ,MAAK;AAAA,YAAS,SAAA,MAAe,EAAK,IAAU,CAAC;AAAA,YAAG,UAAU,KAAW,IAAQ;AAAA,YAAG,UAAA;AAAA,UAAa,CAAA;AAAA,UACrG,gBAAA,EAAC,UAAD;AAAA,YACE,MAAK;AAAA,YACL,SAAA,MAAe,EAAgB;AAAA,cAC7B,SAAA;AAAA,cACA,YAAY,EAAE,GAAc,cAAc;AAAA,YAC5C,CAAC;AAAA,YAEA,UAAA,GAAc,aAAa,SAAS;AAAA,UAC/B,CAAA;AAAA,UACR,gBAAA,EAAC,UAAD;AAAA,YAAQ,MAAK;AAAA,YAAS,WAAU;AAAA,YAAyB,SAAS;AAAA,YAAM,UAAA;AAAA,UAAc,CAAA;AAAA,QACnF;AAAA,MACF,CAAA,CAAA;AAAA,IAEJ,CAAA,CAAA;AAAA;AAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectTitle.js","names":[],"sources":["../../../../../src/components/pro/Flow/panels/ProjectTitle.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\n\nconst DEFAULT_TITLE = '未命名流程图';\n\n/**\n * Editable project title shown in the top-left. Renders as plain-looking text\n * (a borderless input) and only becomes editable on double-click, committing on\n * blur or Enter and reverting on Escape. The title is stored on the document\n * and used as the base filename for all exports.\n */\nexport default function ProjectTitle() {\n const { doc, setDoc } = useFlowContext();\n const [editing, setEditing] = useState(false);\n const [draft, setDraft] = useState(doc.title ?? '');\n const inputRef = useRef<HTMLInputElement>(null);\n\n // Keep the draft in sync when the document title changes externally\n // (import / undo / new document) while not actively editing.\n useEffect(() => {\n if (!editing) setDraft(doc.title ?? '');\n }, [doc.title, editing]);\n\n useEffect(() => {\n if (editing) {\n const el = inputRef.current;\n el?.focus();\n el?.select();\n }\n }, [editing]);\n\n const commit = () => {\n const next = draft.trim();\n setEditing(false);\n setDoc((prev) => ({ ...prev, title: next || undefined }));\n };\n\n const cancel = () => {\n setDraft(doc.title ?? '');\n setEditing(false);\n };\n\n return (\n <div className=\"flow-project-title\" title=\"双击编辑标题\">\n <input\n ref={inputRef}\n className={`flow-project-title-input${editing ? ' flow-project-title-input--editing' : ''}`}\n value={editing ? draft : (doc.title ?? '')}\n placeholder={DEFAULT_TITLE}\n readOnly={!editing}\n onDoubleClick={() => setEditing(true)}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={() => { if (editing) commit(); }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') { e.preventDefault(); commit(); }\n else if (e.key === 'Escape') { e.preventDefault(); cancel(); }\n }}\n spellCheck={false}\n />\n </div>\n );\n}\n"],"mappings":";;;AAGA,IAAM,IAAgB;AAQtB,SAAwB,IAAe;AACrC,QAAM,EAAE,KAAA,GAAK,QAAA,EAAA,IAAW,EAAe,GACjC,CAAC,GAAS,CAAA,IAAc,EAAS,EAAK,GACtC,CAAC,GAAO,CAAA,IAAY,EAAS,EAAI,SAAS,EAAE,GAC5C,IAAW,EAAyB,IAAI;AAI9C,EAAA,EAAA,MAAgB;AACd,IAAK,KAAS,EAAS,EAAI,SAAS,EAAE;AAAA,EACxC,GAAG,CAAC,EAAI,OAAO,CAAO,CAAC,GAEvB,EAAA,MAAgB;AACd,QAAI,GAAS;AACX,YAAM,IAAK,EAAS;AACpB,MAAA,GAAI,MAAM,GACV,GAAI,OAAO;AAAA,IACb;AAAA,EACF,GAAG,CAAC,CAAO,CAAC;AAEZ,QAAM,IAAA,MAAe;AACnB,UAAM,IAAO,EAAM,KAAK;AACxB,IAAA,EAAW,EAAK,GAChB,EAAA,CAAQ,OAAU;AAAA,MAAE,GAAG;AAAA,MAAM,OAAO,KAAQ;AAAA,IAAU,EAAE;AAAA,EAC1D,GAEM,IAAA,MAAe;AACnB,IAAA,EAAS,EAAI,SAAS,EAAE,GACxB,EAAW,EAAK;AAAA,EAClB;AAEA,SACE,gBAAA,EAAC,OAAD;AAAA,IAAK,WAAU;AAAA,IAAqB,OAAM;AAAA,
|
|
1
|
+
{"version":3,"file":"ProjectTitle.js","names":[],"sources":["../../../../../src/components/pro/Flow/panels/ProjectTitle.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\n\nconst DEFAULT_TITLE = '未命名流程图';\n\n/**\n * Editable project title shown in the top-left. Renders as plain-looking text\n * (a borderless input) and only becomes editable on double-click, committing on\n * blur or Enter and reverting on Escape. The title is stored on the document\n * and used as the base filename for all exports.\n */\nexport default function ProjectTitle() {\n const { doc, setDoc } = useFlowContext();\n const [editing, setEditing] = useState(false);\n const [draft, setDraft] = useState(doc.title ?? '');\n const inputRef = useRef<HTMLInputElement>(null);\n\n // Keep the draft in sync when the document title changes externally\n // (import / undo / new document) while not actively editing.\n useEffect(() => {\n if (!editing) setDraft(doc.title ?? '');\n }, [doc.title, editing]);\n\n useEffect(() => {\n if (editing) {\n const el = inputRef.current;\n el?.focus();\n el?.select();\n }\n }, [editing]);\n\n const commit = () => {\n const next = draft.trim();\n setEditing(false);\n setDoc((prev) => ({ ...prev, title: next || undefined }));\n };\n\n const cancel = () => {\n setDraft(doc.title ?? '');\n setEditing(false);\n };\n\n return (\n <div className=\"flow-project-title\" title=\"双击编辑标题\">\n <input\n ref={inputRef}\n className={`flow-project-title-input${editing ? ' flow-project-title-input--editing' : ''}`}\n value={editing ? draft : (doc.title ?? '')}\n placeholder={DEFAULT_TITLE}\n readOnly={!editing}\n onDoubleClick={() => setEditing(true)}\n onChange={(e) => setDraft(e.target.value)}\n onBlur={() => { if (editing) commit(); }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') { e.preventDefault(); commit(); }\n else if (e.key === 'Escape') { e.preventDefault(); cancel(); }\n }}\n spellCheck={false}\n />\n </div>\n );\n}\n"],"mappings":";;;AAGA,IAAM,IAAgB;AAQtB,SAAwB,IAAe;AACrC,QAAM,EAAE,KAAA,GAAK,QAAA,EAAA,IAAW,EAAe,GACjC,CAAC,GAAS,CAAA,IAAc,EAAS,EAAK,GACtC,CAAC,GAAO,CAAA,IAAY,EAAS,EAAI,SAAS,EAAE,GAC5C,IAAW,EAAyB,IAAI;AAI9C,EAAA,EAAA,MAAgB;AACd,IAAK,KAAS,EAAS,EAAI,SAAS,EAAE;AAAA,EACxC,GAAG,CAAC,EAAI,OAAO,CAAO,CAAC,GAEvB,EAAA,MAAgB;AACd,QAAI,GAAS;AACX,YAAM,IAAK,EAAS;AACpB,MAAA,GAAI,MAAM,GACV,GAAI,OAAO;AAAA,IACb;AAAA,EACF,GAAG,CAAC,CAAO,CAAC;AAEZ,QAAM,IAAA,MAAe;AACnB,UAAM,IAAO,EAAM,KAAK;AACxB,IAAA,EAAW,EAAK,GAChB,EAAA,CAAQ,OAAU;AAAA,MAAE,GAAG;AAAA,MAAM,OAAO,KAAQ;AAAA,IAAU,EAAE;AAAA,EAC1D,GAEM,IAAA,MAAe;AACnB,IAAA,EAAS,EAAI,SAAS,EAAE,GACxB,EAAW,EAAK;AAAA,EAClB;AAEA,SACE,gBAAA,EAAC,OAAD;AAAA,IAAK,WAAU;AAAA,IAAqB,OAAM;AAAA,IACxC,UAAA,gBAAA,EAAC,SAAD;AAAA,MACE,KAAK;AAAA,MACL,WAAW,2BAA2B,IAAU,uCAAuC,EAAA;AAAA,MACvF,OAAO,IAAU,IAAS,EAAI,SAAS;AAAA,MACvC,aAAa;AAAA,MACb,UAAU,CAAC;AAAA,MACX,eAAA,MAAqB,EAAW,EAAI;AAAA,MACpC,UAAA,CAAW,MAAM,EAAS,EAAE,OAAO,KAAK;AAAA,MACxC,QAAA,MAAc;AAAE,QAAI,KAAS,EAAO;AAAA,MAAG;AAAA,MACvC,WAAA,CAAY,MAAM;AAChB,QAAI,EAAE,QAAQ,WAAW,EAAE,eAAe,GAAG,EAAO,KAC3C,EAAE,QAAQ,aAAY,EAAE,eAAe,GAAG,EAAO;AAAA,MAC5D;AAAA,MACA,YAAY;AAAA,IACb,CAAA;AAAA,EACE,CAAA;AAET"}
|