dishui 0.0.55 → 0.0.57
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/Select/index.js +90 -72
- package/dist/components/Select/index.js.map +1 -1
- package/dist/components/pro/Flow/Flow.js +656 -568
- package/dist/components/pro/Flow/Flow.js.map +1 -1
- package/dist/components/pro/Flow/FlowCanvas.js +1027 -970
- package/dist/components/pro/Flow/FlowCanvas.js.map +1 -1
- package/dist/components/pro/Flow/canvas/ViewportContainer.d.ts +6 -0
- package/dist/components/pro/Flow/canvas/ViewportContainer.js +20 -15
- package/dist/components/pro/Flow/canvas/ViewportContainer.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowUIContext.d.ts +12 -3
- package/dist/components/pro/Flow/context/FlowUIContext.js +89 -81
- package/dist/components/pro/Flow/context/FlowUIContext.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useCompactMode.d.ts +13 -0
- package/dist/components/pro/Flow/hooks/useCompactMode.js +20 -0
- package/dist/components/pro/Flow/hooks/useCompactMode.js.map +1 -0
- package/dist/components/pro/Flow/hooks/useDraggablePanel.d.ts +19 -4
- package/dist/components/pro/Flow/hooks/useDraggablePanel.js +31 -24
- package/dist/components/pro/Flow/hooks/useDraggablePanel.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useMindMap.js +16 -22
- package/dist/components/pro/Flow/hooks/useMindMap.js.map +1 -1
- package/dist/components/pro/Flow/hooks/usePixiApp.js +16 -16
- package/dist/components/pro/Flow/hooks/usePixiApp.js.map +1 -1
- package/dist/components/pro/Flow/panels/FlowPanelGroup.d.ts +10 -5
- package/dist/components/pro/Flow/panels/FlowPanelGroup.js +95 -67
- package/dist/components/pro/Flow/panels/FlowPanelGroup.js.map +1 -1
- package/dist/dishui.css +1 -1
- package/dist/utils/i18n.js +20 -0
- package/dist/utils/i18n.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,8 +3,8 @@ import { useFlowContext as F } from "../context/FlowContext.js";
|
|
|
3
3
|
import { useFlowUI as O } from "../context/FlowUIContext.js";
|
|
4
4
|
import { generateId as R } from "../nodes/nodeUtils.js";
|
|
5
5
|
import { useCallback as L, useEffect as C } from "react";
|
|
6
|
-
var S = 150, z = 52,
|
|
7
|
-
function
|
|
6
|
+
var S = 150, z = 52, G = 100, H = 26;
|
|
7
|
+
function W(D, P, g) {
|
|
8
8
|
const a = D.nodes.filter((i) => i.data.isMindMapNode), m = new Map(a.map((i) => [i.id, i])), c = /* @__PURE__ */ new Map();
|
|
9
9
|
for (const i of a) {
|
|
10
10
|
const p = i.data.mindMapParentId;
|
|
@@ -15,12 +15,12 @@ function H(D, P, g) {
|
|
|
15
15
|
const u = m.get(P);
|
|
16
16
|
if (!u) return /* @__PURE__ */ new Map();
|
|
17
17
|
const I = /* @__PURE__ */ new Map();
|
|
18
|
-
let
|
|
18
|
+
let k = u.position.y;
|
|
19
19
|
const x = (i, p) => {
|
|
20
20
|
const t = c.get(i.id) ?? [];
|
|
21
21
|
let e;
|
|
22
22
|
if (t.length === 0)
|
|
23
|
-
e =
|
|
23
|
+
e = k, k += 78;
|
|
24
24
|
else {
|
|
25
25
|
const n = t.map((d) => x(d, p + 1));
|
|
26
26
|
e = (n[0] + n[n.length - 1]) / 2;
|
|
@@ -43,7 +43,7 @@ function H(D, P, g) {
|
|
|
43
43
|
return I;
|
|
44
44
|
}
|
|
45
45
|
function q() {
|
|
46
|
-
const { setEditingNodeId: D, focusOnRect: P, autoFocus: g } = O(), { currentCanvas: a, currentCanvasId: m, selection: c, setSelection: u, setDoc: I } = F(),
|
|
46
|
+
const { setEditingNodeId: D, focusOnRect: P, autoFocus: g } = O(), { currentCanvas: a, currentCanvasId: m, selection: c, setSelection: u, setDoc: I } = F(), k = L((t) => {
|
|
47
47
|
t && window.setTimeout(() => {
|
|
48
48
|
P(t.position.x, t.position.y, t.width, t.height, {
|
|
49
49
|
padding: 80,
|
|
@@ -60,8 +60,8 @@ function q() {
|
|
|
60
60
|
id: s,
|
|
61
61
|
type: "shapeRoundRect",
|
|
62
62
|
position: {
|
|
63
|
-
x: n.position.x + S +
|
|
64
|
-
y: n.position.y + z +
|
|
63
|
+
x: n.position.x + S + G,
|
|
64
|
+
y: n.position.y + z + H
|
|
65
65
|
},
|
|
66
66
|
width: S,
|
|
67
67
|
height: z,
|
|
@@ -69,9 +69,6 @@ function q() {
|
|
|
69
69
|
data: {
|
|
70
70
|
..._,
|
|
71
71
|
label: t === "child" ? "子主题" : "主题",
|
|
72
|
-
fill: "#ffffff",
|
|
73
|
-
stroke: "#6366f1",
|
|
74
|
-
strokeWidth: 2,
|
|
75
72
|
isMindMapNode: !0,
|
|
76
73
|
mindMapParentId: d
|
|
77
74
|
}
|
|
@@ -92,7 +89,7 @@ function q() {
|
|
|
92
89
|
}
|
|
93
90
|
} : o), h = E.findIndex((o) => o.id === n.id), l = t === "sibling-before" ? h : h + 1;
|
|
94
91
|
E.splice(Math.max(0, l), 0, A);
|
|
95
|
-
const
|
|
92
|
+
const b = {
|
|
96
93
|
...w,
|
|
97
94
|
nodes: E,
|
|
98
95
|
edges: [...w.edges, {
|
|
@@ -103,15 +100,14 @@ function q() {
|
|
|
103
100
|
data: {
|
|
104
101
|
...U,
|
|
105
102
|
markerEnd: "none",
|
|
106
|
-
strokeColor: "#94a3b8",
|
|
107
103
|
strokeWidth: 1.5
|
|
108
104
|
}
|
|
109
105
|
}]
|
|
110
|
-
}, r = t === "child" ? n : w.nodes.find((o) => o.id === d) ?? n, M =
|
|
106
|
+
}, r = t === "child" ? n : w.nodes.find((o) => o.id === d) ?? n, M = W(b, y, {
|
|
111
107
|
id: r.id,
|
|
112
108
|
position: r.position
|
|
113
109
|
});
|
|
114
|
-
return
|
|
110
|
+
return b.nodes = b.nodes.map((o) => {
|
|
115
111
|
const N = M.get(o.id);
|
|
116
112
|
return N ? {
|
|
117
113
|
...o,
|
|
@@ -121,7 +117,7 @@ function q() {
|
|
|
121
117
|
...f,
|
|
122
118
|
canvases: {
|
|
123
119
|
...f.canvases,
|
|
124
|
-
[m]:
|
|
120
|
+
[m]: b
|
|
125
121
|
}
|
|
126
122
|
};
|
|
127
123
|
}), u({
|
|
@@ -150,9 +146,7 @@ function q() {
|
|
|
150
146
|
data: {
|
|
151
147
|
..._,
|
|
152
148
|
label: "中心主题",
|
|
153
|
-
|
|
154
|
-
stroke: "#4f46e5",
|
|
155
|
-
strokeWidth: 2,
|
|
149
|
+
stroke: "theme:primary",
|
|
156
150
|
isMindMapNode: !0
|
|
157
151
|
}
|
|
158
152
|
};
|
|
@@ -184,12 +178,12 @@ function q() {
|
|
|
184
178
|
if (!g || !p) return;
|
|
185
179
|
const t = a.nodes.find((n) => n.id === p);
|
|
186
180
|
if (!t) return;
|
|
187
|
-
const e = window.setTimeout(() =>
|
|
181
|
+
const e = window.setTimeout(() => k(t), 20);
|
|
188
182
|
return () => window.clearTimeout(e);
|
|
189
183
|
}, [
|
|
190
184
|
g,
|
|
191
185
|
p,
|
|
192
|
-
|
|
186
|
+
k
|
|
193
187
|
]), C(() => {
|
|
194
188
|
const t = (e) => {
|
|
195
189
|
const n = e.target;
|
|
@@ -212,9 +206,9 @@ function q() {
|
|
|
212
206
|
const f = new Map(a.nodes.map((l) => [l.id, l])), w = a.nodes.filter((l) => l.data.isMindMapNode && l.data.mindMapParentId === s.data.mindMapParentId), y = w.findIndex((l) => l.id === s.id), E = a.nodes.filter((l) => l.data.mindMapParentId === s.id);
|
|
213
207
|
let h;
|
|
214
208
|
if (e.key === "ArrowLeft" && s.data.mindMapParentId ? h = f.get(s.data.mindMapParentId) : e.key === "ArrowRight" ? h = E[0] : e.key === "ArrowUp" && y > 0 ? h = w[y - 1] : e.key === "ArrowDown" && y >= 0 && y < w.length - 1 && (h = w[y + 1]), !h) {
|
|
215
|
-
const l = s.position.x + s.width / 2,
|
|
209
|
+
const l = s.position.x + s.width / 2, b = s.position.y + s.height / 2;
|
|
216
210
|
h = a.nodes.filter((r) => r.id !== s.id && r.type !== "animAnchor").map((r) => {
|
|
217
|
-
const M = r.position.x + r.width / 2 - l, o = r.position.y + r.height / 2 -
|
|
211
|
+
const M = r.position.x + r.width / 2 - l, o = r.position.y + r.height / 2 - b;
|
|
218
212
|
return {
|
|
219
213
|
node: r,
|
|
220
214
|
inDirection: e.key === "ArrowLeft" ? M < 0 : e.key === "ArrowRight" ? M > 0 : e.key === "ArrowUp" ? o < 0 : o > 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMindMap.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useMindMap.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\nimport { DEFAULT_EDGE_DATA, DEFAULT_NODE_DATA } from '../constants';\nimport { generateId } from '../nodes/nodeUtils';\nimport type { FlowCanvasData, FlowNode } from '../types';\n\nconst NODE_WIDTH = 150;\nconst NODE_HEIGHT = 52;\nconst LEVEL_GAP = 100;\nconst ROW_GAP = 26;\n\nfunction layoutMindMap(\n canvas: FlowCanvasData,\n rootId: string,\n fixed?: { id: string; position: { x: number; y: number } },\n): Map<string, { x: number; y: number }> {\n const nodes = canvas.nodes.filter(node => node.data.isMindMapNode);\n const nodeMap = new Map(nodes.map(node => [node.id, node]));\n const children = new Map<string, FlowNode[]>();\n for (const node of nodes) {\n const parentId = node.data.mindMapParentId;\n if (!parentId || !nodeMap.has(parentId)) continue;\n const list = children.get(parentId) ?? [];\n list.push(node);\n children.set(parentId, list);\n }\n\n const root = nodeMap.get(rootId);\n if (!root) return new Map();\n const positions = new Map<string, { x: number; y: number }>();\n let nextY = root.position.y;\n\n const place = (node: FlowNode, depth: number): number => {\n const kids = children.get(node.id) ?? [];\n let centerY: number;\n if (kids.length === 0) {\n centerY = nextY;\n nextY += NODE_HEIGHT + ROW_GAP;\n } else {\n const childYs = kids.map(child => place(child, depth + 1));\n centerY = (childYs[0] + childYs[childYs.length - 1]) / 2;\n }\n positions.set(node.id, {\n x: root.position.x + depth * (NODE_WIDTH + LEVEL_GAP),\n y: centerY,\n });\n return centerY;\n };\n\n place(root, 0);\n if (fixed) {\n const computed = positions.get(fixed.id);\n if (computed) {\n const dx = fixed.position.x - computed.x;\n const dy = fixed.position.y - computed.y;\n for (const [id, position] of positions) {\n positions.set(id, { x: position.x + dx, y: position.y + dy });\n }\n }\n }\n return positions;\n}\n\nexport function useMindMap() {\n const { setEditingNodeId, focusOnRect, autoFocus } = useFlowUI();\n const { currentCanvas, currentCanvasId, selection, setSelection, setDoc } = useFlowContext();\n\n const focusNode = useCallback((node: FlowNode | undefined) => {\n if (!node) return;\n window.setTimeout(() => {\n focusOnRect(node.position.x, node.position.y, node.width, node.height, {\n padding: 80,\n preserveZoom: true,\n duration: 280,\n });\n }, 0);\n }, [focusOnRect]);\n\n const createNode = useCallback((kind: 'child' | 'sibling-after' | 'sibling-before') => {\n const selectedId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n const selected = selectedId ? currentCanvas.nodes.find(node => node.id === selectedId) : undefined;\n if (!selected) return;\n\n // A regular Flow node can become the root of a mind map on first use.\n const parentId = kind === 'child' ? selected.id : selected.data.mindMapParentId;\n if (kind !== 'child' && !parentId) return;\n const id = generateId('mind');\n const node: FlowNode = {\n id,\n type: 'shapeRoundRect',\n position: { x: selected.position.x + NODE_WIDTH + LEVEL_GAP, y: selected.position.y + NODE_HEIGHT + ROW_GAP },\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n zIndex: Math.max(0, ...currentCanvas.nodes.map(item => item.zIndex ?? 0)) + 1,\n data: {\n ...DEFAULT_NODE_DATA,\n label: kind === 'child' ? '子主题' : '主题',\n fill: '#ffffff',\n stroke: '#6366f1',\n strokeWidth: 2,\n isMindMapNode: true,\n mindMapParentId: parentId,\n },\n };\n\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n const rootId = (() => {\n let cursor = selected;\n const map = new Map(canvas.nodes.map(item => [item.id, item]));\n while (cursor.data.mindMapParentId && map.has(cursor.data.mindMapParentId)) {\n cursor = map.get(cursor.data.mindMapParentId)!;\n }\n return cursor.id;\n })();\n const normalizedNodes = canvas.nodes.map(item => item.id === selected.id && !item.data.isMindMapNode\n ? { ...item, data: { ...item.data, isMindMapNode: true } }\n : item);\n const selectedIndex = normalizedNodes.findIndex(item => item.id === selected.id);\n const insertIndex = kind === 'sibling-before'\n ? selectedIndex\n : selectedIndex + 1;\n normalizedNodes.splice(Math.max(0, insertIndex), 0, node);\n const nextCanvas: FlowCanvasData = {\n ...canvas,\n nodes: normalizedNodes,\n edges: [...canvas.edges, {\n id: generateId('edge'),\n source: parentId!,\n target: id,\n type: 'simplebezier',\n data: { ...DEFAULT_EDGE_DATA, markerEnd: 'none', strokeColor: '#94a3b8', strokeWidth: 1.5 },\n }],\n };\n // Child insertion keeps the current node fixed. Sibling insertion keeps\n // their shared parent fixed, so adding nodes never makes the parent jump.\n const fixedNode = kind === 'child'\n ? selected\n : canvas.nodes.find(item => item.id === parentId) ?? selected;\n const positions = layoutMindMap(nextCanvas, rootId, {\n id: fixedNode.id,\n position: fixedNode.position,\n });\n nextCanvas.nodes = nextCanvas.nodes.map(item => {\n const pos = positions.get(item.id);\n return pos ? { ...item, position: pos } : item;\n });\n return { ...prev, canvases: { ...prev.canvases, [currentCanvasId]: nextCanvas } };\n });\n setSelection({ nodeIds: new Set([id]), edgeIds: new Set(), lineIds: new Set() });\n }, [selection, currentCanvas, currentCanvasId, setDoc, setSelection]);\n\n const createRoot = useCallback(() => {\n if (selection.nodeIds.size !== 0) return false;\n const id = generateId('mind');\n const highestZ = Math.max(0, ...currentCanvas.nodes.map(node => node.zIndex ?? 0));\n const node: FlowNode = {\n id,\n type: 'shapeRoundRect',\n position: { x: 120, y: 120 },\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n zIndex: highestZ + 1,\n data: { ...DEFAULT_NODE_DATA, label: '中心主题', fill: '#eef2ff', stroke: '#4f46e5', strokeWidth: 2, isMindMapNode: true },\n };\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return { ...prev, canvases: { ...prev.canvases, [currentCanvasId]: { ...canvas, nodes: [...canvas.nodes, node] } } };\n });\n setSelection({ nodeIds: new Set([id]), edgeIds: new Set(), lineIds: new Set() });\n return true;\n }, [selection, currentCanvas, currentCanvasId, setDoc, setSelection]);\n\n const focusedNodeId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n\n // Center only when keyboard focus/selection moves to another node. Node drag\n // updates currentCanvas continuously; depending on currentCanvas here would\n // make the viewport chase the mouse while an object is being repositioned.\n useEffect(() => {\n if (!autoFocus || !focusedNodeId) return;\n const node = currentCanvas.nodes.find(item => item.id === focusedNodeId);\n if (!node) return;\n const timer = window.setTimeout(() => focusNode(node), 20);\n return () => window.clearTimeout(timer);\n // currentCanvas is intentionally excluded: position-only updates must not\n // retrigger viewport focus while dragging.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoFocus, focusedNodeId, focusNode]);\n\n useEffect(() => {\n const onKey = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement;\n // Never intercept native editing keys from form controls. In particular,\n // inspector textareas must keep Enter/Tab for multiline media source.\n if (target.matches('input, textarea, select, .flow-label-editor') || target.isContentEditable) return;\n const selectedId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n const selected = selectedId ? currentCanvas.nodes.find(node => node.id === selectedId) : undefined;\n\n const consume = () => {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n };\n\n if (event.key === 'Tab') {\n consume();\n if (!selected) createRoot();\n else createNode('child');\n } else if (event.key === 'Enter' && selected) {\n consume();\n createNode(event.shiftKey ? 'sibling-before' : 'sibling-after');\n } else if ((event.key === ' ' || event.code === 'Space') && selected) {\n consume();\n setEditingNodeId(selected.id);\n } else if (selected && ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {\n const nodeMap = new Map(currentCanvas.nodes.map(node => [node.id, node]));\n const siblings = currentCanvas.nodes.filter(node =>\n node.data.isMindMapNode && node.data.mindMapParentId === selected.data.mindMapParentId,\n );\n const siblingIndex = siblings.findIndex(node => node.id === selected.id);\n const children = currentCanvas.nodes.filter(node => node.data.mindMapParentId === selected.id);\n let targetNode: FlowNode | undefined;\n if (event.key === 'ArrowLeft' && selected.data.mindMapParentId) {\n targetNode = nodeMap.get(selected.data.mindMapParentId);\n } else if (event.key === 'ArrowRight') {\n targetNode = children[0];\n } else if (event.key === 'ArrowUp' && siblingIndex > 0) {\n targetNode = siblings[siblingIndex - 1];\n } else if (event.key === 'ArrowDown' && siblingIndex >= 0 && siblingIndex < siblings.length - 1) {\n targetNode = siblings[siblingIndex + 1];\n }\n\n // Regular Flow nodes have no mind-map relationship metadata. Fall back\n // to spatial navigation, choosing the nearest node in the requested\n // direction. The perpendicular penalty favors visually aligned nodes.\n if (!targetNode) {\n const sx = selected.position.x + selected.width / 2;\n const sy = selected.position.y + selected.height / 2;\n const candidates = currentCanvas.nodes\n .filter(node => node.id !== selected.id && node.type !== 'animAnchor')\n .map(node => {\n const dx = node.position.x + node.width / 2 - sx;\n const dy = node.position.y + node.height / 2 - sy;\n const inDirection = event.key === 'ArrowLeft' ? dx < 0\n : event.key === 'ArrowRight' ? dx > 0\n : event.key === 'ArrowUp' ? dy < 0\n : dy > 0;\n const primary = event.key === 'ArrowLeft' || event.key === 'ArrowRight'\n ? Math.abs(dx)\n : Math.abs(dy);\n const perpendicular = event.key === 'ArrowLeft' || event.key === 'ArrowRight'\n ? Math.abs(dy)\n : Math.abs(dx);\n return { node, inDirection, score: primary + perpendicular * 1.75 };\n })\n .filter(candidate => candidate.inDirection)\n .sort((a, b) => a.score - b.score);\n targetNode = candidates[0]?.node;\n }\n\n if (targetNode) {\n consume();\n setSelection({ nodeIds: new Set([targetNode.id]), edgeIds: new Set(), lineIds: new Set() });\n }\n }\n };\n window.addEventListener('keydown', onKey, true);\n return () => window.removeEventListener('keydown', onKey, true);\n }, [selection, currentCanvas, createNode, createRoot, setEditingNodeId, setSelection]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useMindMap.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useMindMap.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\nimport { useFlowContext } from '../context/FlowContext';\nimport { useFlowUI } from '../context/FlowUIContext';\nimport { DEFAULT_EDGE_DATA, DEFAULT_NODE_DATA } from '../constants';\nimport { generateId } from '../nodes/nodeUtils';\nimport type { FlowCanvasData, FlowNode } from '../types';\n\nconst NODE_WIDTH = 150;\nconst NODE_HEIGHT = 52;\nconst LEVEL_GAP = 100;\nconst ROW_GAP = 26;\n\nfunction layoutMindMap(\n canvas: FlowCanvasData,\n rootId: string,\n fixed?: { id: string; position: { x: number; y: number } },\n): Map<string, { x: number; y: number }> {\n const nodes = canvas.nodes.filter(node => node.data.isMindMapNode);\n const nodeMap = new Map(nodes.map(node => [node.id, node]));\n const children = new Map<string, FlowNode[]>();\n for (const node of nodes) {\n const parentId = node.data.mindMapParentId;\n if (!parentId || !nodeMap.has(parentId)) continue;\n const list = children.get(parentId) ?? [];\n list.push(node);\n children.set(parentId, list);\n }\n\n const root = nodeMap.get(rootId);\n if (!root) return new Map();\n const positions = new Map<string, { x: number; y: number }>();\n let nextY = root.position.y;\n\n const place = (node: FlowNode, depth: number): number => {\n const kids = children.get(node.id) ?? [];\n let centerY: number;\n if (kids.length === 0) {\n centerY = nextY;\n nextY += NODE_HEIGHT + ROW_GAP;\n } else {\n const childYs = kids.map(child => place(child, depth + 1));\n centerY = (childYs[0] + childYs[childYs.length - 1]) / 2;\n }\n positions.set(node.id, {\n x: root.position.x + depth * (NODE_WIDTH + LEVEL_GAP),\n y: centerY,\n });\n return centerY;\n };\n\n place(root, 0);\n if (fixed) {\n const computed = positions.get(fixed.id);\n if (computed) {\n const dx = fixed.position.x - computed.x;\n const dy = fixed.position.y - computed.y;\n for (const [id, position] of positions) {\n positions.set(id, { x: position.x + dx, y: position.y + dy });\n }\n }\n }\n return positions;\n}\n\nexport function useMindMap() {\n const { setEditingNodeId, focusOnRect, autoFocus } = useFlowUI();\n const { currentCanvas, currentCanvasId, selection, setSelection, setDoc } = useFlowContext();\n\n const focusNode = useCallback((node: FlowNode | undefined) => {\n if (!node) return;\n window.setTimeout(() => {\n focusOnRect(node.position.x, node.position.y, node.width, node.height, {\n padding: 80,\n preserveZoom: true,\n duration: 280,\n });\n }, 0);\n }, [focusOnRect]);\n\n const createNode = useCallback((kind: 'child' | 'sibling-after' | 'sibling-before') => {\n const selectedId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n const selected = selectedId ? currentCanvas.nodes.find(node => node.id === selectedId) : undefined;\n if (!selected) return;\n\n // A regular Flow node can become the root of a mind map on first use.\n const parentId = kind === 'child' ? selected.id : selected.data.mindMapParentId;\n if (kind !== 'child' && !parentId) return;\n const id = generateId('mind');\n const node: FlowNode = {\n id,\n type: 'shapeRoundRect',\n position: { x: selected.position.x + NODE_WIDTH + LEVEL_GAP, y: selected.position.y + NODE_HEIGHT + ROW_GAP },\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n zIndex: Math.max(0, ...currentCanvas.nodes.map(item => item.zIndex ?? 0)) + 1,\n data: {\n // Colors follow the theme (`theme:*` tokens from DEFAULT_NODE_DATA) so\n // mind maps re-color automatically when the DishUI theme changes.\n ...DEFAULT_NODE_DATA,\n label: kind === 'child' ? '子主题' : '主题',\n isMindMapNode: true,\n mindMapParentId: parentId,\n },\n };\n\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n const rootId = (() => {\n let cursor = selected;\n const map = new Map(canvas.nodes.map(item => [item.id, item]));\n while (cursor.data.mindMapParentId && map.has(cursor.data.mindMapParentId)) {\n cursor = map.get(cursor.data.mindMapParentId)!;\n }\n return cursor.id;\n })();\n const normalizedNodes = canvas.nodes.map(item => item.id === selected.id && !item.data.isMindMapNode\n ? { ...item, data: { ...item.data, isMindMapNode: true } }\n : item);\n const selectedIndex = normalizedNodes.findIndex(item => item.id === selected.id);\n const insertIndex = kind === 'sibling-before'\n ? selectedIndex\n : selectedIndex + 1;\n normalizedNodes.splice(Math.max(0, insertIndex), 0, node);\n const nextCanvas: FlowCanvasData = {\n ...canvas,\n nodes: normalizedNodes,\n edges: [...canvas.edges, {\n id: generateId('edge'),\n source: parentId!,\n target: id,\n type: 'simplebezier',\n data: { ...DEFAULT_EDGE_DATA, markerEnd: 'none', strokeWidth: 1.5 },\n }],\n };\n // Child insertion keeps the current node fixed. Sibling insertion keeps\n // their shared parent fixed, so adding nodes never makes the parent jump.\n const fixedNode = kind === 'child'\n ? selected\n : canvas.nodes.find(item => item.id === parentId) ?? selected;\n const positions = layoutMindMap(nextCanvas, rootId, {\n id: fixedNode.id,\n position: fixedNode.position,\n });\n nextCanvas.nodes = nextCanvas.nodes.map(item => {\n const pos = positions.get(item.id);\n return pos ? { ...item, position: pos } : item;\n });\n return { ...prev, canvases: { ...prev.canvases, [currentCanvasId]: nextCanvas } };\n });\n setSelection({ nodeIds: new Set([id]), edgeIds: new Set(), lineIds: new Set() });\n }, [selection, currentCanvas, currentCanvasId, setDoc, setSelection]);\n\n const createRoot = useCallback(() => {\n if (selection.nodeIds.size !== 0) return false;\n const id = generateId('mind');\n const highestZ = Math.max(0, ...currentCanvas.nodes.map(node => node.zIndex ?? 0));\n const node: FlowNode = {\n id,\n type: 'shapeRoundRect',\n position: { x: 120, y: 120 },\n width: NODE_WIDTH,\n height: NODE_HEIGHT,\n zIndex: highestZ + 1,\n // Root keeps a primary-colored outline for hierarchy emphasis; every\n // color is still a theme token, so it follows the active theme.\n data: { ...DEFAULT_NODE_DATA, label: '中心主题', stroke: 'theme:primary', isMindMapNode: true },\n };\n setDoc(prev => {\n const canvas = prev.canvases[currentCanvasId];\n if (!canvas) return prev;\n return { ...prev, canvases: { ...prev.canvases, [currentCanvasId]: { ...canvas, nodes: [...canvas.nodes, node] } } };\n });\n setSelection({ nodeIds: new Set([id]), edgeIds: new Set(), lineIds: new Set() });\n return true;\n }, [selection, currentCanvas, currentCanvasId, setDoc, setSelection]);\n\n const focusedNodeId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n\n // Center only when keyboard focus/selection moves to another node. Node drag\n // updates currentCanvas continuously; depending on currentCanvas here would\n // make the viewport chase the mouse while an object is being repositioned.\n useEffect(() => {\n if (!autoFocus || !focusedNodeId) return;\n const node = currentCanvas.nodes.find(item => item.id === focusedNodeId);\n if (!node) return;\n const timer = window.setTimeout(() => focusNode(node), 20);\n return () => window.clearTimeout(timer);\n // currentCanvas is intentionally excluded: position-only updates must not\n // retrigger viewport focus while dragging.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoFocus, focusedNodeId, focusNode]);\n\n useEffect(() => {\n const onKey = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement;\n // Never intercept native editing keys from form controls. In particular,\n // inspector textareas must keep Enter/Tab for multiline media source.\n if (target.matches('input, textarea, select, .flow-label-editor') || target.isContentEditable) return;\n const selectedId = selection.nodeIds.size === 1 ? [...selection.nodeIds][0] : null;\n const selected = selectedId ? currentCanvas.nodes.find(node => node.id === selectedId) : undefined;\n\n const consume = () => {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n };\n\n if (event.key === 'Tab') {\n consume();\n if (!selected) createRoot();\n else createNode('child');\n } else if (event.key === 'Enter' && selected) {\n consume();\n createNode(event.shiftKey ? 'sibling-before' : 'sibling-after');\n } else if ((event.key === ' ' || event.code === 'Space') && selected) {\n consume();\n setEditingNodeId(selected.id);\n } else if (selected && ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) {\n const nodeMap = new Map(currentCanvas.nodes.map(node => [node.id, node]));\n const siblings = currentCanvas.nodes.filter(node =>\n node.data.isMindMapNode && node.data.mindMapParentId === selected.data.mindMapParentId,\n );\n const siblingIndex = siblings.findIndex(node => node.id === selected.id);\n const children = currentCanvas.nodes.filter(node => node.data.mindMapParentId === selected.id);\n let targetNode: FlowNode | undefined;\n if (event.key === 'ArrowLeft' && selected.data.mindMapParentId) {\n targetNode = nodeMap.get(selected.data.mindMapParentId);\n } else if (event.key === 'ArrowRight') {\n targetNode = children[0];\n } else if (event.key === 'ArrowUp' && siblingIndex > 0) {\n targetNode = siblings[siblingIndex - 1];\n } else if (event.key === 'ArrowDown' && siblingIndex >= 0 && siblingIndex < siblings.length - 1) {\n targetNode = siblings[siblingIndex + 1];\n }\n\n // Regular Flow nodes have no mind-map relationship metadata. Fall back\n // to spatial navigation, choosing the nearest node in the requested\n // direction. The perpendicular penalty favors visually aligned nodes.\n if (!targetNode) {\n const sx = selected.position.x + selected.width / 2;\n const sy = selected.position.y + selected.height / 2;\n const candidates = currentCanvas.nodes\n .filter(node => node.id !== selected.id && node.type !== 'animAnchor')\n .map(node => {\n const dx = node.position.x + node.width / 2 - sx;\n const dy = node.position.y + node.height / 2 - sy;\n const inDirection = event.key === 'ArrowLeft' ? dx < 0\n : event.key === 'ArrowRight' ? dx > 0\n : event.key === 'ArrowUp' ? dy < 0\n : dy > 0;\n const primary = event.key === 'ArrowLeft' || event.key === 'ArrowRight'\n ? Math.abs(dx)\n : Math.abs(dy);\n const perpendicular = event.key === 'ArrowLeft' || event.key === 'ArrowRight'\n ? Math.abs(dy)\n : Math.abs(dx);\n return { node, inDirection, score: primary + perpendicular * 1.75 };\n })\n .filter(candidate => candidate.inDirection)\n .sort((a, b) => a.score - b.score);\n targetNode = candidates[0]?.node;\n }\n\n if (targetNode) {\n consume();\n setSelection({ nodeIds: new Set([targetNode.id]), edgeIds: new Set(), lineIds: new Set() });\n }\n }\n };\n window.addEventListener('keydown', onKey, true);\n return () => window.removeEventListener('keydown', onKey, true);\n }, [selection, currentCanvas, createNode, createRoot, setEditingNodeId, setSelection]);\n}\n"],"mappings":"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"}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { PixiApp as k } from "../canvas/PixiApp.js";
|
|
2
|
-
import { refreshThemePalette as
|
|
2
|
+
import { refreshThemePalette as g, resolveFlowThemeColors as w } from "../utils/themeColor.js";
|
|
3
3
|
import { useEffect as x, useRef as o } from "react";
|
|
4
4
|
function O(l, a) {
|
|
5
5
|
const s = o(null), d = o(null), c = o(!1), i = o(a);
|
|
6
6
|
return i.current = a, x(() => {
|
|
7
|
-
const
|
|
8
|
-
if (!
|
|
7
|
+
const t = l.current;
|
|
8
|
+
if (!t) return;
|
|
9
9
|
let h = !1;
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const e = document.createElement("canvas");
|
|
11
|
+
e.style.display = "block", e.style.width = "100%", e.style.height = "100%", e.style.touchAction = "none", t.appendChild(e), d.current = e;
|
|
12
12
|
const r = new k();
|
|
13
13
|
s.current = r;
|
|
14
|
-
const f =
|
|
15
|
-
|
|
16
|
-
const m = w(
|
|
14
|
+
const f = t.getBoundingClientRect();
|
|
15
|
+
g(t);
|
|
16
|
+
const m = w(t);
|
|
17
17
|
r.init({
|
|
18
|
-
canvas:
|
|
18
|
+
canvas: e,
|
|
19
19
|
width: f.width,
|
|
20
20
|
height: f.height,
|
|
21
21
|
backgroundColor: m.background
|
|
@@ -25,27 +25,27 @@ function O(l, a) {
|
|
|
25
25
|
const p = new ResizeObserver((n) => {
|
|
26
26
|
if (!r.destroyed)
|
|
27
27
|
for (const R of n) {
|
|
28
|
-
const { width: v, height:
|
|
29
|
-
v > 0 &&
|
|
28
|
+
const { width: v, height: y } = R.contentRect;
|
|
29
|
+
v > 0 && y > 0 && r.resize(v, y);
|
|
30
30
|
}
|
|
31
31
|
});
|
|
32
|
-
p.observe(
|
|
32
|
+
p.observe(t);
|
|
33
33
|
const C = () => {
|
|
34
34
|
if (r.destroyed) return;
|
|
35
|
-
|
|
36
|
-
const n = w(
|
|
35
|
+
g(t);
|
|
36
|
+
const n = w(t);
|
|
37
37
|
r.setBackgroundColor(n.background), i.current?.(n);
|
|
38
38
|
}, u = new MutationObserver(C);
|
|
39
39
|
u.observe(document.documentElement, {
|
|
40
40
|
attributes: !0,
|
|
41
41
|
attributeFilter: ["data-theme", "class"]
|
|
42
42
|
});
|
|
43
|
-
const b =
|
|
43
|
+
const b = t.closest("[data-theme]");
|
|
44
44
|
return b && u.observe(b, {
|
|
45
45
|
attributes: !0,
|
|
46
46
|
attributeFilter: ["data-theme", "class"]
|
|
47
47
|
}), () => {
|
|
48
|
-
h = !0, p.disconnect(), u.disconnect(), r.destroy(), s.current = null, c.current = !1,
|
|
48
|
+
h = !0, p.disconnect(), u.disconnect(), r.destroy(), s.current = null, c.current = !1, e.parentNode && e.parentNode.removeChild(e);
|
|
49
49
|
};
|
|
50
50
|
}, [l]), {
|
|
51
51
|
pixiRef: s,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePixiApp.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/usePixiApp.ts"],"sourcesContent":["import { useRef, useEffect, useCallback } from 'react';\nimport { PixiApp } from '../canvas/PixiApp';\nimport { resolveFlowThemeColors, refreshThemePalette, type FlowThemeColors } from '../utils/themeColor';\n\n/**\n * Manages PixiJS Application lifecycle: creates on mount, destroys on unmount,\n * handles container resize.\n *\n * The canvas backdrop follows the active DishUI theme (`--color-base-200`). We\n * resolve the theme color from the container's inherited CSS scope at init and\n * again whenever the ancestor `data-theme` attribute changes.\n */\nexport function usePixiApp(\n containerRef: React.RefObject<HTMLDivElement | null>,\n onThemeColors?: (colors: FlowThemeColors) => void,\n) {\n const pixiRef = useRef<PixiApp | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const readyRef = useRef(false);\n const onThemeColorsRef = useRef(onThemeColors);\n onThemeColorsRef.current = onThemeColors;\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n\n const canvas = document.createElement('canvas');\n canvas.style.display = 'block';\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n container.appendChild(canvas);\n canvasRef.current = canvas;\n\n const pixi = new PixiApp();\n pixiRef.current = pixi;\n\n const rect = container.getBoundingClientRect();\n refreshThemePalette(container);\n const themeColors = resolveFlowThemeColors(container);\n pixi.init({\n canvas,\n width: rect.width,\n height: rect.height,\n backgroundColor: themeColors.background,\n }).then(() => {\n if (!cancelled) {\n readyRef.current = true;\n onThemeColorsRef.current?.(themeColors);\n }\n });\n\n const ro = new ResizeObserver((entries) => {\n if (pixi.destroyed) return;\n for (const entry of entries) {\n const { width, height } = entry.contentRect;\n if (width > 0 && height > 0) {\n pixi.resize(width, height);\n }\n }\n });\n ro.observe(container);\n\n // React to DishUI theme switches (the theme is applied via a `data-theme`\n // attribute on an ancestor element). Re-resolve the themed colors and push\n // them to the canvas + subscriber.\n const applyTheme = () => {\n if (pixi.destroyed) return;\n refreshThemePalette(container);\n const colors = resolveFlowThemeColors(container);\n pixi.setBackgroundColor(colors.background);\n onThemeColorsRef.current?.(colors);\n };\n const themeObserver = new MutationObserver(applyTheme);\n themeObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-theme', 'class'],\n });\n // Also watch the closest themed ancestor, if any.\n const themedAncestor = container.closest('[data-theme]');\n if (themedAncestor) {\n themeObserver.observe(themedAncestor, {\n attributes: true,\n attributeFilter: ['data-theme', 'class'],\n });\n }\n\n return () => {\n cancelled = true;\n ro.disconnect();\n themeObserver.disconnect();\n pixi.destroy();\n pixiRef.current = null;\n readyRef.current = false;\n if (canvas.parentNode) canvas.parentNode.removeChild(canvas);\n };\n }, [containerRef]);\n\n return { pixiRef, canvasRef, readyRef };\n}\n"],"mappings":";;;AAYA,SAAgB,EACd,GACA,GACA;AACA,QAAM,IAAU,EAAuB,IAAI,GACrC,IAAY,EAAiC,IAAI,GACjD,IAAW,EAAO,EAAK,GACvB,IAAmB,EAAO,CAAa;AAC7C,SAAA,EAAiB,UAAU,GAE3B,EAAA,MAAgB;AACd,UAAM,IAAY,EAAa;AAC/B,QAAI,CAAC,EAAW;AAEhB,QAAI,IAAY;AAEhB,UAAM,IAAS,SAAS,cAAc,QAAQ;AAC9C,IAAA,EAAO,MAAM,UAAU,SACvB,EAAO,MAAM,QAAQ,QACrB,EAAO,MAAM,SAAS,
|
|
1
|
+
{"version":3,"file":"usePixiApp.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/usePixiApp.ts"],"sourcesContent":["import { useRef, useEffect, useCallback } from 'react';\nimport { PixiApp } from '../canvas/PixiApp';\nimport { resolveFlowThemeColors, refreshThemePalette, type FlowThemeColors } from '../utils/themeColor';\n\n/**\n * Manages PixiJS Application lifecycle: creates on mount, destroys on unmount,\n * handles container resize.\n *\n * The canvas backdrop follows the active DishUI theme (`--color-base-200`). We\n * resolve the theme color from the container's inherited CSS scope at init and\n * again whenever the ancestor `data-theme` attribute changes.\n */\nexport function usePixiApp(\n containerRef: React.RefObject<HTMLDivElement | null>,\n onThemeColors?: (colors: FlowThemeColors) => void,\n) {\n const pixiRef = useRef<PixiApp | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const readyRef = useRef(false);\n const onThemeColorsRef = useRef(onThemeColors);\n onThemeColorsRef.current = onThemeColors;\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n\n const canvas = document.createElement('canvas');\n canvas.style.display = 'block';\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n // Touch devices: the canvas owns all touch gestures (drag/pinch/pan); the\n // browser must not scroll or fire pointercancel mid-gesture.\n canvas.style.touchAction = 'none';\n container.appendChild(canvas);\n canvasRef.current = canvas;\n\n const pixi = new PixiApp();\n pixiRef.current = pixi;\n\n const rect = container.getBoundingClientRect();\n refreshThemePalette(container);\n const themeColors = resolveFlowThemeColors(container);\n pixi.init({\n canvas,\n width: rect.width,\n height: rect.height,\n backgroundColor: themeColors.background,\n }).then(() => {\n if (!cancelled) {\n readyRef.current = true;\n onThemeColorsRef.current?.(themeColors);\n }\n });\n\n const ro = new ResizeObserver((entries) => {\n if (pixi.destroyed) return;\n for (const entry of entries) {\n const { width, height } = entry.contentRect;\n if (width > 0 && height > 0) {\n pixi.resize(width, height);\n }\n }\n });\n ro.observe(container);\n\n // React to DishUI theme switches (the theme is applied via a `data-theme`\n // attribute on an ancestor element). Re-resolve the themed colors and push\n // them to the canvas + subscriber.\n const applyTheme = () => {\n if (pixi.destroyed) return;\n refreshThemePalette(container);\n const colors = resolveFlowThemeColors(container);\n pixi.setBackgroundColor(colors.background);\n onThemeColorsRef.current?.(colors);\n };\n const themeObserver = new MutationObserver(applyTheme);\n themeObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-theme', 'class'],\n });\n // Also watch the closest themed ancestor, if any.\n const themedAncestor = container.closest('[data-theme]');\n if (themedAncestor) {\n themeObserver.observe(themedAncestor, {\n attributes: true,\n attributeFilter: ['data-theme', 'class'],\n });\n }\n\n return () => {\n cancelled = true;\n ro.disconnect();\n themeObserver.disconnect();\n pixi.destroy();\n pixiRef.current = null;\n readyRef.current = false;\n if (canvas.parentNode) canvas.parentNode.removeChild(canvas);\n };\n }, [containerRef]);\n\n return { pixiRef, canvasRef, readyRef };\n}\n"],"mappings":";;;AAYA,SAAgB,EACd,GACA,GACA;AACA,QAAM,IAAU,EAAuB,IAAI,GACrC,IAAY,EAAiC,IAAI,GACjD,IAAW,EAAO,EAAK,GACvB,IAAmB,EAAO,CAAa;AAC7C,SAAA,EAAiB,UAAU,GAE3B,EAAA,MAAgB;AACd,UAAM,IAAY,EAAa;AAC/B,QAAI,CAAC,EAAW;AAEhB,QAAI,IAAY;AAEhB,UAAM,IAAS,SAAS,cAAc,QAAQ;AAC9C,IAAA,EAAO,MAAM,UAAU,SACvB,EAAO,MAAM,QAAQ,QACrB,EAAO,MAAM,SAAS,QAGtB,EAAO,MAAM,cAAc,QAC3B,EAAU,YAAY,CAAM,GAC5B,EAAU,UAAU;AAEpB,UAAM,IAAO,IAAI,EAAQ;AACzB,IAAA,EAAQ,UAAU;AAElB,UAAM,IAAO,EAAU,sBAAsB;AAC7C,IAAA,EAAoB,CAAS;AAC7B,UAAM,IAAc,EAAuB,CAAS;AACpD,IAAA,EAAK,KAAK;AAAA,MACR,QAAA;AAAA,MACA,OAAO,EAAK;AAAA,MACZ,QAAQ,EAAK;AAAA,MACb,iBAAiB,EAAY;AAAA,IAC/B,CAAC,EAAE,KAAA,MAAW;AACZ,MAAK,MACH,EAAS,UAAU,IACnB,EAAiB,UAAU,CAAW;AAAA,IAE1C,CAAC;AAED,UAAM,IAAK,IAAI,eAAA,CAAgB,MAAY;AACzC,UAAI,CAAA,EAAK;AACT,mBAAW,KAAS,GAAS;AAC3B,gBAAM,EAAE,OAAA,GAAO,QAAA,EAAA,IAAW,EAAM;AAChC,UAAI,IAAQ,KAAK,IAAS,KACxB,EAAK,OAAO,GAAO,CAAM;AAAA,QAE7B;AAAA,IACF,CAAC;AACD,IAAA,EAAG,QAAQ,CAAS;AAKpB,UAAM,IAAA,MAAmB;AACvB,UAAI,EAAK,UAAW;AACpB,MAAA,EAAoB,CAAS;AAC7B,YAAM,IAAS,EAAuB,CAAS;AAC/C,MAAA,EAAK,mBAAmB,EAAO,UAAU,GACzC,EAAiB,UAAU,CAAM;AAAA,IACnC,GACM,IAAgB,IAAI,iBAAiB,CAAU;AACrD,IAAA,EAAc,QAAQ,SAAS,iBAAiB;AAAA,MAC9C,YAAY;AAAA,MACZ,iBAAiB,CAAC,cAAc,OAAO;AAAA,IACzC,CAAC;AAED,UAAM,IAAiB,EAAU,QAAQ,cAAc;AACvD,WAAI,KACF,EAAc,QAAQ,GAAgB;AAAA,MACpC,YAAY;AAAA,MACZ,iBAAiB,CAAC,cAAc,OAAO;AAAA,IACzC,CAAC,GAGH,MAAa;AACX,MAAA,IAAY,IACZ,EAAG,WAAW,GACd,EAAc,WAAW,GACzB,EAAK,QAAQ,GACb,EAAQ,UAAU,MAClB,EAAS,UAAU,IACf,EAAO,cAAY,EAAO,WAAW,YAAY,CAAM;AAAA,IAC7D;AAAA,EACF,GAAG,CAAC,CAAY,CAAC,GAEV;AAAA,IAAE,SAAA;AAAA,IAAS,WAAA;AAAA,IAAW,UAAA;AAAA,EAAS;AACxC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type ResizeEdge } from '../hooks/useResizablePanel';
|
|
3
|
+
import type { FlowPanelDock } from '../context/FlowUIContext';
|
|
3
4
|
export interface PanelTab {
|
|
4
5
|
id: string;
|
|
5
6
|
label: string;
|
|
@@ -13,10 +14,14 @@ interface FlowPanelGroupProps {
|
|
|
13
14
|
resizeEdge: ResizeEdge;
|
|
14
15
|
defaultWidth: number;
|
|
15
16
|
style?: React.CSSProperties;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
|
|
17
|
+
/** Which dock this group occupies ('float' = draggable overlay). */
|
|
18
|
+
dock?: FlowPanelDock;
|
|
19
|
+
/** Request moving the current tab to another dock (left/right/float). */
|
|
20
|
+
onDock?: (tabId: string, dock: FlowPanelDock) => void;
|
|
21
|
+
/** Floating panels report the dock edge they hover while dragging. */
|
|
22
|
+
onDragHintChange?: (side: 'left' | 'right' | null) => void;
|
|
23
|
+
/** Close (hide) the current panel; it can be reopened via the toolbar. */
|
|
24
|
+
onClose?: (tabId: string) => void;
|
|
20
25
|
}
|
|
21
|
-
export default function FlowPanelGroup({ tabs, defaultTab, className, resizeEdge, defaultWidth, style,
|
|
26
|
+
export default function FlowPanelGroup({ tabs, defaultTab, className, resizeEdge, defaultWidth, style, dock, onDock, onDragHintChange, onClose, }: FlowPanelGroupProps): React.JSX.Element | null;
|
|
22
27
|
export {};
|
|
@@ -1,90 +1,118 @@
|
|
|
1
|
-
import P from "../../../
|
|
2
|
-
import { useDraggablePanel as
|
|
1
|
+
import P from "../../../Select/index.js";
|
|
2
|
+
import { useDraggablePanel as $ } from "../hooks/useDraggablePanel.js";
|
|
3
3
|
import { useResizablePanel as x } from "../hooks/useResizablePanel.js";
|
|
4
|
-
import {
|
|
5
|
-
import { jsx as
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
4
|
+
import { useCallback as z, useEffect as D, useState as T } from "react";
|
|
5
|
+
import { jsx as n, jsxs as c } from "react/jsx-runtime";
|
|
6
|
+
var j = {
|
|
7
|
+
left: [{
|
|
8
|
+
target: "float",
|
|
9
|
+
glyph: "⬈",
|
|
10
|
+
title: "浮动面板"
|
|
11
|
+
}, {
|
|
12
|
+
target: "right",
|
|
13
|
+
glyph: "⇥",
|
|
14
|
+
title: "停靠到右侧"
|
|
15
|
+
}],
|
|
16
|
+
right: [{
|
|
17
|
+
target: "left",
|
|
18
|
+
glyph: "⇤",
|
|
19
|
+
title: "停靠到左侧"
|
|
20
|
+
}, {
|
|
21
|
+
target: "float",
|
|
22
|
+
glyph: "⬈",
|
|
23
|
+
title: "浮动面板"
|
|
24
|
+
}],
|
|
25
|
+
float: [{
|
|
26
|
+
target: "left",
|
|
27
|
+
glyph: "⇤",
|
|
28
|
+
title: "停靠到左侧"
|
|
29
|
+
}, {
|
|
30
|
+
target: "right",
|
|
31
|
+
glyph: "⇥",
|
|
32
|
+
title: "停靠到右侧"
|
|
33
|
+
}]
|
|
34
|
+
};
|
|
35
|
+
function H({ tabs: l, defaultTab: h, className: g, resizeEdge: s, defaultWidth: m, style: u, dock: p, onDock: r, onDragHintChange: w, onClose: d }) {
|
|
36
|
+
const [i, f] = T(h ?? l[0]?.id), o = p === "float", t = l.find((e) => e.id === i)?.id ?? l[0]?.id, { panelStyle: y, handleProps: N } = $(o ? {
|
|
37
|
+
onDropDock: (e) => {
|
|
38
|
+
t && r?.(t, e);
|
|
39
|
+
},
|
|
40
|
+
onDragHintChange: w
|
|
41
|
+
} : void 0), { panelWidth: v, resizeHandleProps: b } = x(s, m);
|
|
42
|
+
D(() => {
|
|
43
|
+
l.length > 0 && !l.find((e) => e.id === i) && f(l[0].id);
|
|
44
|
+
}, [l, i]);
|
|
45
|
+
const S = z((e) => {
|
|
46
|
+
t && r?.(t, e);
|
|
47
|
+
}, [t, r]);
|
|
48
|
+
if (l.length === 0) return null;
|
|
49
|
+
const a = l.find((e) => e.id === i) ?? l[0], k = l.length > 1, C = j[p ?? "left"];
|
|
50
|
+
return /* @__PURE__ */ c("div", {
|
|
51
|
+
className: `${g ?? "flow-palette"}${o ? " flow-panel--detached" : " flow-panel--docked"}`,
|
|
21
52
|
"data-flow-panel": !0,
|
|
22
53
|
style: {
|
|
23
|
-
...
|
|
24
|
-
...
|
|
25
|
-
width:
|
|
54
|
+
...u,
|
|
55
|
+
...o ? y : void 0,
|
|
56
|
+
width: v
|
|
26
57
|
},
|
|
27
58
|
children: [
|
|
28
|
-
/* @__PURE__ */
|
|
59
|
+
/* @__PURE__ */ n("div", {
|
|
29
60
|
className: `flow-panel-resize-handle flow-panel-resize-handle--${s}`,
|
|
30
|
-
...
|
|
61
|
+
...b
|
|
31
62
|
}),
|
|
32
|
-
|
|
33
|
-
className: "flow-panel-
|
|
34
|
-
...t ? p : {},
|
|
35
|
-
children: [/* @__PURE__ */ a("div", {
|
|
36
|
-
className: "flow-panel-tabs",
|
|
37
|
-
ref: h,
|
|
38
|
-
children: l.map((e) => /* @__PURE__ */ i(P, {
|
|
39
|
-
variant: "ghost",
|
|
40
|
-
size: "xs",
|
|
41
|
-
className: `flow-panel-tab${e.id === n.id ? " flow-panel-tab--active" : ""}`,
|
|
42
|
-
onClick: () => f(e.id),
|
|
43
|
-
children: [e.icon && /* @__PURE__ */ a("span", {
|
|
44
|
-
className: "flow-panel-tab-icon",
|
|
45
|
-
children: e.icon
|
|
46
|
-
}), e.label]
|
|
47
|
-
}, e.id))
|
|
48
|
-
}), o && /* @__PURE__ */ a("button", {
|
|
49
|
-
className: "flow-panel-detach-btn",
|
|
50
|
-
title: "Detach tab",
|
|
51
|
-
onClick: () => o(n.id),
|
|
52
|
-
children: "⬈"
|
|
53
|
-
})]
|
|
54
|
-
}) : /* @__PURE__ */ i("div", {
|
|
55
|
-
className: "flow-panel-header-single",
|
|
56
|
-
...t ? p : {},
|
|
63
|
+
/* @__PURE__ */ c("div", {
|
|
64
|
+
className: "flow-panel-header",
|
|
57
65
|
children: [
|
|
58
|
-
/* @__PURE__ */
|
|
66
|
+
o && /* @__PURE__ */ n("span", {
|
|
67
|
+
className: "flow-panel-grip",
|
|
68
|
+
title: "拖动移动面板",
|
|
69
|
+
...N,
|
|
70
|
+
children: "⠿"
|
|
71
|
+
}),
|
|
72
|
+
k ? /* @__PURE__ */ n(P, {
|
|
73
|
+
className: "flow-panel-select",
|
|
74
|
+
options: l.map((e) => ({
|
|
75
|
+
value: e.id,
|
|
76
|
+
label: typeof e.icon == "string" && e.icon ? `${e.icon} ${e.label}` : e.label
|
|
77
|
+
})),
|
|
78
|
+
value: a.id,
|
|
79
|
+
onChange: (e) => f(String(e))
|
|
80
|
+
}) : /* @__PURE__ */ c("span", {
|
|
59
81
|
className: "flow-panel-header-label",
|
|
60
|
-
children: [
|
|
82
|
+
children: [a.icon && /* @__PURE__ */ n("span", {
|
|
61
83
|
className: "flow-panel-tab-icon",
|
|
62
|
-
children:
|
|
63
|
-
}),
|
|
64
|
-
}),
|
|
65
|
-
t && d && /* @__PURE__ */ a("button", {
|
|
66
|
-
className: "flow-panel-detach-btn",
|
|
67
|
-
title: "Attach back",
|
|
68
|
-
onClick: () => d(n.id),
|
|
69
|
-
children: "⮋"
|
|
84
|
+
children: a.icon
|
|
85
|
+
}), a.label]
|
|
70
86
|
}),
|
|
71
|
-
|
|
72
|
-
className: "flow-panel-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
87
|
+
/* @__PURE__ */ c("span", {
|
|
88
|
+
className: "flow-panel-header-actions",
|
|
89
|
+
children: [C.map((e) => /* @__PURE__ */ n("button", {
|
|
90
|
+
type: "button",
|
|
91
|
+
className: "flow-panel-dock-btn",
|
|
92
|
+
title: e.title,
|
|
93
|
+
onClick: () => S(e.target),
|
|
94
|
+
children: e.glyph
|
|
95
|
+
}, e.target)), d && /* @__PURE__ */ n("button", {
|
|
96
|
+
type: "button",
|
|
97
|
+
className: "flow-panel-dock-btn flow-panel-close-btn",
|
|
98
|
+
title: "关闭面板(可从工具栏重新打开)",
|
|
99
|
+
onClick: () => {
|
|
100
|
+
t && d(t);
|
|
101
|
+
},
|
|
102
|
+
children: "✕"
|
|
103
|
+
})]
|
|
76
104
|
})
|
|
77
105
|
]
|
|
78
106
|
}),
|
|
79
|
-
/* @__PURE__ */
|
|
107
|
+
/* @__PURE__ */ n("div", {
|
|
80
108
|
className: "flow-palette-content",
|
|
81
|
-
children:
|
|
109
|
+
children: a.content
|
|
82
110
|
})
|
|
83
111
|
]
|
|
84
112
|
});
|
|
85
113
|
}
|
|
86
114
|
export {
|
|
87
|
-
|
|
115
|
+
H as default
|
|
88
116
|
};
|
|
89
117
|
|
|
90
118
|
//# sourceMappingURL=FlowPanelGroup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FlowPanelGroup.js","names":[],"sources":["../../../../../src/components/pro/Flow/panels/FlowPanelGroup.tsx"],"sourcesContent":["import React, { useState, useEffect,
|
|
1
|
+
{"version":3,"file":"FlowPanelGroup.js","names":[],"sources":["../../../../../src/components/pro/Flow/panels/FlowPanelGroup.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback } from 'react';\nimport Select from '../../../Select';\nimport { useDraggablePanel } from '../hooks/useDraggablePanel';\nimport { useResizablePanel, type ResizeEdge } from '../hooks/useResizablePanel';\nimport type { FlowPanelDock } from '../context/FlowUIContext';\n\nexport interface PanelTab {\n id: string;\n label: string;\n icon?: React.ReactNode;\n content: React.ReactNode;\n}\n\ninterface FlowPanelGroupProps {\n tabs: PanelTab[];\n defaultTab?: string;\n className?: string;\n resizeEdge: ResizeEdge;\n defaultWidth: number;\n style?: React.CSSProperties;\n /** Which dock this group occupies ('float' = draggable overlay). */\n dock?: FlowPanelDock;\n /** Request moving the current tab to another dock (left/right/float). */\n onDock?: (tabId: string, dock: FlowPanelDock) => void;\n /** Floating panels report the dock edge they hover while dragging. */\n onDragHintChange?: (side: 'left' | 'right' | null) => void;\n /** Close (hide) the current panel; it can be reopened via the toolbar. */\n onClose?: (tabId: string) => void;\n}\n\n/** Header buttons offered per current dock, in display order. */\nconst DOCK_ACTIONS: Record<FlowPanelDock, Array<{ target: FlowPanelDock; glyph: string; title: string }>> = {\n left: [\n { target: 'float', glyph: '⬈', title: '浮动面板' },\n { target: 'right', glyph: '⇥', title: '停靠到右侧' },\n ],\n right: [\n { target: 'left', glyph: '⇤', title: '停靠到左侧' },\n { target: 'float', glyph: '⬈', title: '浮动面板' },\n ],\n float: [\n { target: 'left', glyph: '⇤', title: '停靠到左侧' },\n { target: 'right', glyph: '⇥', title: '停靠到右侧' },\n ],\n};\n\nexport default function FlowPanelGroup({\n tabs,\n defaultTab,\n className,\n resizeEdge,\n defaultWidth,\n style,\n dock,\n onDock,\n onDragHintChange,\n onClose,\n}: FlowPanelGroupProps) {\n const [activeTab, setActiveTab] = useState(defaultTab ?? tabs[0]?.id);\n const isFloating = dock === 'float';\n const currentId = tabs.find(t => t.id === activeTab)?.id ?? tabs[0]?.id;\n const { panelStyle, handleProps } = useDraggablePanel(\n isFloating\n ? {\n onDropDock: (side) => { if (currentId) onDock?.(currentId, side); },\n onDragHintChange,\n }\n : undefined,\n );\n const { panelWidth, resizeHandleProps } = useResizablePanel(resizeEdge, defaultWidth);\n\n useEffect(() => {\n if (tabs.length > 0 && !tabs.find(t => t.id === activeTab)) {\n setActiveTab(tabs[0].id);\n }\n }, [tabs, activeTab]);\n\n const handleDock = useCallback((target: FlowPanelDock) => {\n if (currentId) onDock?.(currentId, target);\n }, [currentId, onDock]);\n\n if (tabs.length === 0) return null;\n\n const current = tabs.find(t => t.id === activeTab) ?? tabs[0];\n const showSwitcher = tabs.length > 1;\n const actions = DOCK_ACTIONS[dock ?? 'left'];\n\n return (\n <div\n className={`${className ?? 'flow-palette'}${isFloating ? ' flow-panel--detached' : ' flow-panel--docked'}`}\n data-flow-panel\n style={{ ...style, ...(isFloating ? panelStyle : undefined), width: panelWidth }}\n >\n <div\n className={`flow-panel-resize-handle flow-panel-resize-handle--${resizeEdge}`}\n {...resizeHandleProps}\n />\n <div className=\"flow-panel-header\">\n {isFloating && (\n <span className=\"flow-panel-grip\" title=\"拖动移动面板\" {...handleProps}>⠿</span>\n )}\n {showSwitcher ? (\n <Select\n className=\"flow-panel-select\"\n options={tabs.map(t => ({\n value: t.id,\n label: typeof t.icon === 'string' && t.icon ? `${t.icon} ${t.label}` : t.label,\n }))}\n value={current.id}\n onChange={(v) => setActiveTab(String(v))}\n />\n ) : (\n <span className=\"flow-panel-header-label\">\n {current.icon && <span className=\"flow-panel-tab-icon\">{current.icon}</span>}\n {current.label}\n </span>\n )}\n <span className=\"flow-panel-header-actions\">\n {actions.map(a => (\n <button\n key={a.target}\n type=\"button\"\n className=\"flow-panel-dock-btn\"\n title={a.title}\n onClick={() => handleDock(a.target)}\n >{a.glyph}</button>\n ))}\n {onClose && (\n <button\n type=\"button\"\n className=\"flow-panel-dock-btn flow-panel-close-btn\"\n title=\"关闭面板(可从工具栏重新打开)\"\n onClick={() => { if (currentId) onClose(currentId); }}\n >✕</button>\n )}\n </span>\n </div>\n <div className=\"flow-palette-content\">\n {current.content}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|