@flowuent-org/diagramming-core 1.1.4 → 1.1.6
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/package.json +1 -1
- package/packages/diagrams/src/index.ts +1 -0
- package/packages/diagrams/src/lib/contexts/DiagramProvider.tsx +17 -0
- package/packages/diagrams/src/lib/contexts/diagramStoreTypes.tsx +1 -0
- package/packages/diagrams/src/lib/contexts/onReconnect.ts +59 -0
- package/packages/diagrams/src/lib/templates/DiagramContent.tsx +548 -338
- package/packages/diagrams/src/lib/utils/nodeOrderByEdges.ts +437 -0
|
@@ -1,338 +1,548 @@
|
|
|
1
|
-
import React, { useEffect, useState, useMemo } from 'react';
|
|
2
|
-
import { ConnectionMode, ReactFlow, ReactFlowProps } from '@xyflow/react';
|
|
3
|
-
import { useTriggerEvent } from '../utils/event-hooks';
|
|
4
|
-
import { IBlock, ICardNodeData } from '../types/card-node';
|
|
5
|
-
import {
|
|
6
|
-
DndContext,
|
|
7
|
-
DragOverlay,
|
|
8
|
-
PointerSensor,
|
|
9
|
-
useDroppable,
|
|
10
|
-
useSensor,
|
|
11
|
-
useSensors,
|
|
12
|
-
} from '@dnd-kit/core';
|
|
13
|
-
|
|
14
|
-
import { Box, Fab } from '@mui/material';
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
import
|
|
38
|
-
import
|
|
39
|
-
import
|
|
40
|
-
import
|
|
41
|
-
import
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
event.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
</>
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
1
|
+
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
|
2
|
+
import { ConnectionMode, ReactFlow, ReactFlowProps, NodeChange, applyNodeChanges } from '@xyflow/react';
|
|
3
|
+
import { useTriggerEvent } from '../utils/event-hooks';
|
|
4
|
+
import { IBlock, ICardNodeData } from '../types/card-node';
|
|
5
|
+
import {
|
|
6
|
+
DndContext,
|
|
7
|
+
DragOverlay,
|
|
8
|
+
PointerSensor,
|
|
9
|
+
useDroppable,
|
|
10
|
+
useSensor,
|
|
11
|
+
useSensors,
|
|
12
|
+
} from '@dnd-kit/core';
|
|
13
|
+
|
|
14
|
+
import { Box, Fab, IconButton, Tooltip } from '@mui/material';
|
|
15
|
+
import UndoIcon from '@mui/icons-material/Undo';
|
|
16
|
+
import RedoIcon from '@mui/icons-material/Redo';
|
|
17
|
+
import { CardDataProvider } from '../contexts/CardDataProvider';
|
|
18
|
+
import BlockWrapper from '../molecules/BlockWrapper';
|
|
19
|
+
import useDragCallbacks from '../hooks/useDragCallbacks';
|
|
20
|
+
import { useElk } from '../utils/elkLayout';
|
|
21
|
+
import { useModalControls } from '../hooks/useModalControls';
|
|
22
|
+
import { useWorkflowNodeAction } from '../hooks/useWorkflowNodeActiont';
|
|
23
|
+
import {
|
|
24
|
+
useContentHeights,
|
|
25
|
+
useDiagram,
|
|
26
|
+
useDiagramEdges,
|
|
27
|
+
useDiagramNodes,
|
|
28
|
+
useDiagramType,
|
|
29
|
+
useLayoutDirection,
|
|
30
|
+
useSetLayoutDirection,
|
|
31
|
+
usePannable,
|
|
32
|
+
useUndo,
|
|
33
|
+
useRedo,
|
|
34
|
+
useCanUndo,
|
|
35
|
+
useCanRedo,
|
|
36
|
+
} from '../contexts/DiagramProvider';
|
|
37
|
+
import { useAutoRegisterFunctions } from '../hooks/useAutoRegisterFunctions';
|
|
38
|
+
import SequenceDiagram from './sequence/SequenceDiagram';
|
|
39
|
+
import ArchDiagram from './arch/ArchDiagram';
|
|
40
|
+
import StateMachineDiagram from './stateMachine/StateMachineDiagram';
|
|
41
|
+
import SystemFlowDiagram from './systemFlow/SystemFlowDiagram';
|
|
42
|
+
import CloudArchitectureDiagram from './cloud-arch/components/CloudArchitectureDiagram';
|
|
43
|
+
import UseCaseDiagram from './useCaseDiagram/UseCaseDiagram';
|
|
44
|
+
import CollaborationDiagram from './collaborationDiagram/CollaborationDiagram';
|
|
45
|
+
import Header from '../components/Header';
|
|
46
|
+
import { PaneMouseHandler } from '../utils/event-store';
|
|
47
|
+
import { AutomationExecutionPanel } from '../components/automation/AutomationExecutionPanel';
|
|
48
|
+
|
|
49
|
+
interface DiagramContentProps
|
|
50
|
+
extends Omit<ReactFlowProps, 'nodes' | 'edges' | 'onChange'> {
|
|
51
|
+
className?: string;
|
|
52
|
+
showAutomationExecutionPanel?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const DiagramContent: React.FC<DiagramContentProps> = ({
|
|
56
|
+
className = '',
|
|
57
|
+
showAutomationExecutionPanel = true,
|
|
58
|
+
...props
|
|
59
|
+
}) => {
|
|
60
|
+
// Auto-register function nodes when they are created
|
|
61
|
+
useAutoRegisterFunctions();
|
|
62
|
+
|
|
63
|
+
const handleOnPaneClick = useTriggerEvent('onPaneClick');
|
|
64
|
+
|
|
65
|
+
// Use specific hooks where available
|
|
66
|
+
const nodes = useDiagramNodes();
|
|
67
|
+
const edges = useDiagramEdges();
|
|
68
|
+
const onConnect = useDiagram((state) => state.onConnect);
|
|
69
|
+
const onReconnect = useDiagram((state) => state.onReconnect);
|
|
70
|
+
const onEdgesChange = useDiagram((state) => state.onEdgesChange);
|
|
71
|
+
const onNodeDragEnd = useDiagram((state) => state.onNodeDragEnd);
|
|
72
|
+
const onNodeDragStart = useDiagram((state) => state.onNodeDragStart);
|
|
73
|
+
const onNodesChange = useDiagram((state) => state.onNodesChange);
|
|
74
|
+
const setNodes = useDiagram((state) => state.setNodes);
|
|
75
|
+
const diagramType = useDiagramType();
|
|
76
|
+
const contentHeights = useContentHeights();
|
|
77
|
+
const setShowEditEdge = useDiagram((state) => state.setSelectedEdge);
|
|
78
|
+
const selectedNode = useDiagram((state) => state.selectedNode);
|
|
79
|
+
const setSelectedNode = useDiagram((state) => state.setSelectedNode);
|
|
80
|
+
const isPannable = usePannable();
|
|
81
|
+
|
|
82
|
+
// Undo/Redo hooks
|
|
83
|
+
const undo = useUndo();
|
|
84
|
+
const redo = useRedo();
|
|
85
|
+
const canUndo = useCanUndo();
|
|
86
|
+
const canRedo = useCanRedo();
|
|
87
|
+
|
|
88
|
+
// Handle keyboard shortcuts for undo/redo
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
91
|
+
// Don't trigger if user is typing in an input field
|
|
92
|
+
if (
|
|
93
|
+
event.target instanceof HTMLInputElement ||
|
|
94
|
+
event.target instanceof HTMLTextAreaElement ||
|
|
95
|
+
(event.target as any)?.isContentEditable
|
|
96
|
+
) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Ctrl+Z or Cmd+Z for Undo
|
|
101
|
+
if ((event.ctrlKey || event.metaKey) && event.key === 'z' && !event.shiftKey) {
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
if (canUndo) {
|
|
104
|
+
undo();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Ctrl+Y or Cmd+Shift+Z for Redo
|
|
109
|
+
if (
|
|
110
|
+
((event.ctrlKey || event.metaKey) && event.key === 'y') ||
|
|
111
|
+
((event.ctrlKey || event.metaKey) && event.key === 'z' && event.shiftKey)
|
|
112
|
+
) {
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
if (canRedo) {
|
|
115
|
+
redo();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
121
|
+
return () => {
|
|
122
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
123
|
+
};
|
|
124
|
+
}, [undo, redo, canUndo, canRedo]);
|
|
125
|
+
|
|
126
|
+
// Filter out hidden nodes for ReactFlow rendering
|
|
127
|
+
const visibleNodes = useMemo(() => {
|
|
128
|
+
return nodes.filter(node => !node.data?.isHidden);
|
|
129
|
+
}, [nodes]);
|
|
130
|
+
|
|
131
|
+
// Filter out edges connected to hidden nodes
|
|
132
|
+
const visibleEdges = useMemo(() => {
|
|
133
|
+
const visibleNodeIds = new Set(visibleNodes.map(node => node.id));
|
|
134
|
+
return edges.filter(edge =>
|
|
135
|
+
visibleNodeIds.has(edge.source) && visibleNodeIds.has(edge.target)
|
|
136
|
+
);
|
|
137
|
+
}, [edges, visibleNodes]);
|
|
138
|
+
|
|
139
|
+
const { addNode } = useWorkflowNodeAction({
|
|
140
|
+
isReplacing: false,
|
|
141
|
+
variant: 'addParallelCol',
|
|
142
|
+
addInMiddle: false,
|
|
143
|
+
closeModal: () => setOpen(false),
|
|
144
|
+
direction: 'left',
|
|
145
|
+
});
|
|
146
|
+
const [activeItem, setActiveItem] = useState<IBlock | null>(null);
|
|
147
|
+
const [activeCard, setActiveCard] = useState<ICardNodeData | null>(null);
|
|
148
|
+
const { setNodeRef } = useDroppable({ id: 'main' });
|
|
149
|
+
const { open, onClick, setOpen } = useModalControls();
|
|
150
|
+
|
|
151
|
+
const closeModalsOnClickOutside: PaneMouseHandler = () => {
|
|
152
|
+
handleOnPaneClick();
|
|
153
|
+
setShowEditEdge(null);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const sensors = useSensors(
|
|
157
|
+
useSensor(PointerSensor, {
|
|
158
|
+
activationConstraint: {
|
|
159
|
+
delay: 250,
|
|
160
|
+
distance: 5,
|
|
161
|
+
},
|
|
162
|
+
onActivation({ event }) {
|
|
163
|
+
event.stopPropagation();
|
|
164
|
+
},
|
|
165
|
+
}),
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const { getLayoutedElements } = useElk();
|
|
169
|
+
const layoutDirection = useLayoutDirection();
|
|
170
|
+
const setLayoutDirection = useSetLayoutDirection();
|
|
171
|
+
const layoutTimeoutRef = React.useRef<NodeJS.Timeout | null>(null);
|
|
172
|
+
|
|
173
|
+
// Set layout direction to RIGHT for automation diagrams
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
if (diagramType === 'automation' && layoutDirection !== 'RIGHT') {
|
|
176
|
+
setLayoutDirection('RIGHT');
|
|
177
|
+
}
|
|
178
|
+
}, [diagramType, layoutDirection, setLayoutDirection]);
|
|
179
|
+
|
|
180
|
+
// Debounce layout recalculation to prevent continuous propagation
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
// Clear any pending layout recalculation
|
|
183
|
+
if (layoutTimeoutRef.current) {
|
|
184
|
+
clearTimeout(layoutTimeoutRef.current);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Debounce the layout recalculation
|
|
188
|
+
layoutTimeoutRef.current = setTimeout(() => {
|
|
189
|
+
getLayoutedElements();
|
|
190
|
+
}, 300); // 300ms debounce delay
|
|
191
|
+
|
|
192
|
+
return () => {
|
|
193
|
+
if (layoutTimeoutRef.current) {
|
|
194
|
+
clearTimeout(layoutTimeoutRef.current);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}, [nodes.length, edges.length, contentHeights, diagramType, layoutDirection, getLayoutedElements]);
|
|
198
|
+
|
|
199
|
+
// Listen for layout direction changes
|
|
200
|
+
useEffect(() => {
|
|
201
|
+
const handleLayoutDirectionChange = () => {
|
|
202
|
+
getLayoutedElements();
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
window.addEventListener('layoutDirectionChanged', handleLayoutDirectionChange);
|
|
206
|
+
|
|
207
|
+
return () => {
|
|
208
|
+
window.removeEventListener('layoutDirectionChanged', handleLayoutDirectionChange);
|
|
209
|
+
};
|
|
210
|
+
}, [getLayoutedElements]);
|
|
211
|
+
|
|
212
|
+
const { onDragEnd, onDragStart, onDragCancel } = useDragCallbacks({
|
|
213
|
+
setActiveItem,
|
|
214
|
+
setActiveCard,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Handle keyboard deletion of selected nodes (only for automation diagram)
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
if (diagramType !== 'automation') return;
|
|
220
|
+
|
|
221
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
222
|
+
// Check if Delete or Backspace key is pressed
|
|
223
|
+
if (event.key === 'Delete' || event.key === 'Backspace') {
|
|
224
|
+
// Don't trigger deletion if user is typing in an input field
|
|
225
|
+
if (
|
|
226
|
+
event.target instanceof HTMLInputElement ||
|
|
227
|
+
event.target instanceof HTMLTextAreaElement ||
|
|
228
|
+
(event.target as any)?.isContentEditable
|
|
229
|
+
) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Check if a node is selected (either from store or ReactFlow's selection)
|
|
234
|
+
const selectedNodeId = selectedNode || visibleNodes.find(node => node.selected)?.id;
|
|
235
|
+
|
|
236
|
+
if (selectedNodeId) {
|
|
237
|
+
event.preventDefault();
|
|
238
|
+
event.stopPropagation();
|
|
239
|
+
|
|
240
|
+
// Find the node to delete
|
|
241
|
+
const nodeToDelete = nodes.find((node) => node.id === selectedNodeId);
|
|
242
|
+
if (!nodeToDelete) return;
|
|
243
|
+
|
|
244
|
+
// Create a remove change for the node
|
|
245
|
+
const nodeChanges: NodeChange[] = [
|
|
246
|
+
{
|
|
247
|
+
id: selectedNodeId,
|
|
248
|
+
type: 'remove',
|
|
249
|
+
},
|
|
250
|
+
];
|
|
251
|
+
|
|
252
|
+
// Apply the changes through onNodesChange (which handles edge cleanup)
|
|
253
|
+
onNodesChange(nodeChanges);
|
|
254
|
+
|
|
255
|
+
// Clear the selected node
|
|
256
|
+
setSelectedNode(null);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// Add event listener
|
|
262
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
263
|
+
|
|
264
|
+
// Cleanup on unmount
|
|
265
|
+
return () => {
|
|
266
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
267
|
+
};
|
|
268
|
+
}, [diagramType, selectedNode, nodes, visibleNodes, onNodesChange, setSelectedNode]);
|
|
269
|
+
|
|
270
|
+
const renderDynamicDiagram = () => {
|
|
271
|
+
if (diagramType === 'sequence') {
|
|
272
|
+
return (
|
|
273
|
+
<>
|
|
274
|
+
<SequenceDiagram sequenceNodes={visibleNodes} sequenceEdges={visibleEdges} />
|
|
275
|
+
</>
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (diagramType === 'arch') {
|
|
280
|
+
return <ArchDiagram archNodes={visibleNodes as any} archEdges={visibleEdges as any} />;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (diagramType === 'stateMachine') {
|
|
284
|
+
return (
|
|
285
|
+
<>
|
|
286
|
+
<StateMachineDiagram stateNodes={visibleNodes} stateEdges={visibleEdges} />
|
|
287
|
+
</>
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
if (diagramType === 'systemFlow') {
|
|
291
|
+
return (
|
|
292
|
+
<>
|
|
293
|
+
<SystemFlowDiagram systemFlowNodes={visibleNodes} systemFlowEdges={visibleEdges} />
|
|
294
|
+
</>
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
if (diagramType === 'case') {
|
|
298
|
+
return (
|
|
299
|
+
<>
|
|
300
|
+
<UseCaseDiagram caseNodes={visibleNodes} caseEdges={visibleEdges} />
|
|
301
|
+
</>
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
if (diagramType === 'cloud-architecture') {
|
|
305
|
+
return (
|
|
306
|
+
<>
|
|
307
|
+
<CloudArchitectureDiagram initialNodes={visibleNodes as any} initialEdges={visibleEdges as any} />
|
|
308
|
+
</>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
if (diagramType === 'collaboration') {
|
|
312
|
+
return (
|
|
313
|
+
<>
|
|
314
|
+
<CollaborationDiagram collabNodes={visibleNodes} collabEdges={visibleEdges} />
|
|
315
|
+
</>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
if (diagramType === 'automation') {
|
|
319
|
+
return (
|
|
320
|
+
<>
|
|
321
|
+
<Box
|
|
322
|
+
sx={{ display: 'flex', justifyContent: 'space-between' }}
|
|
323
|
+
className={className}
|
|
324
|
+
>
|
|
325
|
+
<Box
|
|
326
|
+
ref={setNodeRef}
|
|
327
|
+
sx={{ height: '100svh', width: '100svw', position: 'relative' }}
|
|
328
|
+
>
|
|
329
|
+
{/* Undo/Redo Toolbar */}
|
|
330
|
+
<Box
|
|
331
|
+
sx={{
|
|
332
|
+
position: 'absolute',
|
|
333
|
+
top: 16,
|
|
334
|
+
left: 16,
|
|
335
|
+
zIndex: 10,
|
|
336
|
+
display: 'flex',
|
|
337
|
+
gap: 0.5,
|
|
338
|
+
backgroundColor: 'background.paper',
|
|
339
|
+
borderRadius: 2,
|
|
340
|
+
boxShadow: 2,
|
|
341
|
+
padding: 0.5,
|
|
342
|
+
}}
|
|
343
|
+
>
|
|
344
|
+
<Tooltip title="Undo (Ctrl+Z)" placement="bottom">
|
|
345
|
+
<span>
|
|
346
|
+
<IconButton
|
|
347
|
+
onClick={undo}
|
|
348
|
+
disabled={!canUndo}
|
|
349
|
+
size="small"
|
|
350
|
+
sx={{
|
|
351
|
+
color: canUndo ? 'primary.main' : 'action.disabled',
|
|
352
|
+
}}
|
|
353
|
+
>
|
|
354
|
+
<UndoIcon fontSize="small" />
|
|
355
|
+
</IconButton>
|
|
356
|
+
</span>
|
|
357
|
+
</Tooltip>
|
|
358
|
+
<Tooltip title="Redo (Ctrl+Y)" placement="bottom">
|
|
359
|
+
<span>
|
|
360
|
+
<IconButton
|
|
361
|
+
onClick={redo}
|
|
362
|
+
disabled={!canRedo}
|
|
363
|
+
size="small"
|
|
364
|
+
sx={{
|
|
365
|
+
color: canRedo ? 'primary.main' : 'action.disabled',
|
|
366
|
+
}}
|
|
367
|
+
>
|
|
368
|
+
<RedoIcon fontSize="small" />
|
|
369
|
+
</IconButton>
|
|
370
|
+
</span>
|
|
371
|
+
</Tooltip>
|
|
372
|
+
</Box>
|
|
373
|
+
<ReactFlow
|
|
374
|
+
panOnDrag={isPannable}
|
|
375
|
+
nodesDraggable={isPannable}
|
|
376
|
+
nodes={visibleNodes}
|
|
377
|
+
onNodesChange={onNodesChange}
|
|
378
|
+
edges={visibleEdges}
|
|
379
|
+
onEdgesChange={onEdgesChange}
|
|
380
|
+
onReconnect={onReconnect}
|
|
381
|
+
onNodeClick={(event, node) => {
|
|
382
|
+
event.stopPropagation();
|
|
383
|
+
setSelectedNode(node.id);
|
|
384
|
+
}}
|
|
385
|
+
onPaneClick={(event) => {
|
|
386
|
+
setSelectedNode(null);
|
|
387
|
+
closeModalsOnClickOutside(event);
|
|
388
|
+
}}
|
|
389
|
+
nodesConnectable={true}
|
|
390
|
+
nodesFocusable={true}
|
|
391
|
+
edgesFocusable={true}
|
|
392
|
+
elementsSelectable={true}
|
|
393
|
+
selectNodesOnDrag={false}
|
|
394
|
+
defaultEdgeOptions={{
|
|
395
|
+
reconnectable: true,
|
|
396
|
+
}}
|
|
397
|
+
fitView
|
|
398
|
+
fitViewOptions={{
|
|
399
|
+
padding: 0.2,
|
|
400
|
+
minZoom: 0.3,
|
|
401
|
+
maxZoom: 2,
|
|
402
|
+
includeHiddenNodes: false,
|
|
403
|
+
}}
|
|
404
|
+
minZoom={0.1}
|
|
405
|
+
maxZoom={2}
|
|
406
|
+
onNodeDragStart={onNodeDragStart}
|
|
407
|
+
onNodeDragStop={onNodeDragEnd}
|
|
408
|
+
onConnect={onConnect}
|
|
409
|
+
connectionMode={ConnectionMode.Strict}
|
|
410
|
+
suppressContentEditableWarning
|
|
411
|
+
suppressHydrationWarning
|
|
412
|
+
{...props}
|
|
413
|
+
/>
|
|
414
|
+
{props.children}
|
|
415
|
+
</Box>
|
|
416
|
+
</Box>
|
|
417
|
+
|
|
418
|
+
<DragOverlay>
|
|
419
|
+
{activeItem ? (
|
|
420
|
+
<CardDataProvider nodeId={activeItem?.id}>
|
|
421
|
+
<BlockWrapper data={activeItem} />
|
|
422
|
+
</CardDataProvider>
|
|
423
|
+
) : null}
|
|
424
|
+
</DragOverlay>
|
|
425
|
+
|
|
426
|
+
{showAutomationExecutionPanel && (
|
|
427
|
+
<AutomationExecutionPanel
|
|
428
|
+
nodes={visibleNodes as any}
|
|
429
|
+
edges={visibleEdges as any}
|
|
430
|
+
workflowId="automation-workflow"
|
|
431
|
+
onNodeUpdate={(nodeId, updatedData) => {
|
|
432
|
+
const updatedNodes = nodes.map(node =>
|
|
433
|
+
node.id === nodeId
|
|
434
|
+
? { ...node, data: updatedData }
|
|
435
|
+
: node
|
|
436
|
+
);
|
|
437
|
+
setNodes(updatedNodes);
|
|
438
|
+
}}
|
|
439
|
+
/>
|
|
440
|
+
)}
|
|
441
|
+
</>
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
return (
|
|
445
|
+
<>
|
|
446
|
+
<Box
|
|
447
|
+
sx={{ display: 'flex', justifyContent: 'space-between' }}
|
|
448
|
+
className={className}
|
|
449
|
+
>
|
|
450
|
+
<Box
|
|
451
|
+
ref={setNodeRef}
|
|
452
|
+
sx={{ height: '100svh', width: '100svw', position: 'relative' }}
|
|
453
|
+
>
|
|
454
|
+
{/* Undo/Redo Toolbar */}
|
|
455
|
+
<Box
|
|
456
|
+
sx={{
|
|
457
|
+
position: 'absolute',
|
|
458
|
+
top: 16,
|
|
459
|
+
left: 16,
|
|
460
|
+
zIndex: 10,
|
|
461
|
+
display: 'flex',
|
|
462
|
+
gap: 0.5,
|
|
463
|
+
backgroundColor: 'background.paper',
|
|
464
|
+
borderRadius: 2,
|
|
465
|
+
boxShadow: 2,
|
|
466
|
+
padding: 0.5,
|
|
467
|
+
}}
|
|
468
|
+
>
|
|
469
|
+
<Tooltip title="Undo (Ctrl+Z)" placement="bottom">
|
|
470
|
+
<span>
|
|
471
|
+
<IconButton
|
|
472
|
+
onClick={undo}
|
|
473
|
+
disabled={!canUndo}
|
|
474
|
+
size="small"
|
|
475
|
+
sx={{
|
|
476
|
+
color: canUndo ? 'primary.main' : 'action.disabled',
|
|
477
|
+
}}
|
|
478
|
+
>
|
|
479
|
+
<UndoIcon fontSize="small" />
|
|
480
|
+
</IconButton>
|
|
481
|
+
</span>
|
|
482
|
+
</Tooltip>
|
|
483
|
+
<Tooltip title="Redo (Ctrl+Y)" placement="bottom">
|
|
484
|
+
<span>
|
|
485
|
+
<IconButton
|
|
486
|
+
onClick={redo}
|
|
487
|
+
disabled={!canRedo}
|
|
488
|
+
size="small"
|
|
489
|
+
sx={{
|
|
490
|
+
color: canRedo ? 'primary.main' : 'action.disabled',
|
|
491
|
+
}}
|
|
492
|
+
>
|
|
493
|
+
<RedoIcon fontSize="small" />
|
|
494
|
+
</IconButton>
|
|
495
|
+
</span>
|
|
496
|
+
</Tooltip>
|
|
497
|
+
</Box>
|
|
498
|
+
<Fab
|
|
499
|
+
onClick={() => addNode('function')}
|
|
500
|
+
sx={{ position: 'fixed', top: 450, left: 0 }}
|
|
501
|
+
>
|
|
502
|
+
Add Function
|
|
503
|
+
</Fab>
|
|
504
|
+
<ReactFlow
|
|
505
|
+
panOnDrag={isPannable}
|
|
506
|
+
nodesDraggable={isPannable}
|
|
507
|
+
nodes={visibleNodes}
|
|
508
|
+
onNodesChange={onNodesChange}
|
|
509
|
+
edges={visibleEdges}
|
|
510
|
+
onEdgesChange={onEdgesChange}
|
|
511
|
+
fitView
|
|
512
|
+
onNodeDragStart={onNodeDragStart}
|
|
513
|
+
onNodeDragStop={onNodeDragEnd}
|
|
514
|
+
onConnect={onConnect}
|
|
515
|
+
connectionMode={ConnectionMode.Strict}
|
|
516
|
+
suppressContentEditableWarning
|
|
517
|
+
suppressHydrationWarning
|
|
518
|
+
onPaneClick={closeModalsOnClickOutside}
|
|
519
|
+
{...props}
|
|
520
|
+
/>
|
|
521
|
+
{props.children}
|
|
522
|
+
</Box>
|
|
523
|
+
</Box>
|
|
524
|
+
|
|
525
|
+
<DragOverlay>
|
|
526
|
+
{activeItem ? (
|
|
527
|
+
<CardDataProvider nodeId={activeItem?.id}>
|
|
528
|
+
<BlockWrapper data={activeItem} />
|
|
529
|
+
</CardDataProvider>
|
|
530
|
+
) : null}
|
|
531
|
+
</DragOverlay>
|
|
532
|
+
</>
|
|
533
|
+
);
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
return (
|
|
537
|
+
<DndContext
|
|
538
|
+
onDragEnd={onDragEnd}
|
|
539
|
+
onDragStart={onDragStart}
|
|
540
|
+
onDragCancel={onDragCancel}
|
|
541
|
+
sensors={sensors}
|
|
542
|
+
>
|
|
543
|
+
{renderDynamicDiagram()}
|
|
544
|
+
</DndContext>
|
|
545
|
+
);
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
export default DiagramContent;
|