@principal-ai/principal-view-react 0.6.10 → 0.6.11
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/README.md +2 -5
- package/dist/components/ConfigurationSelector.js +4 -2
- package/dist/components/ConfigurationSelector.js.map +1 -1
- package/dist/components/EdgeInfoPanel.d.ts.map +1 -1
- package/dist/components/EdgeInfoPanel.js +43 -13
- package/dist/components/EdgeInfoPanel.js.map +1 -1
- package/dist/components/GraphRenderer.d.ts.map +1 -1
- package/dist/components/GraphRenderer.js +133 -83
- package/dist/components/GraphRenderer.js.map +1 -1
- package/dist/components/NodeInfoPanel.d.ts.map +1 -1
- package/dist/components/NodeInfoPanel.js +143 -45
- package/dist/components/NodeInfoPanel.js.map +1 -1
- package/dist/edges/CustomEdge.d.ts.map +1 -1
- package/dist/edges/CustomEdge.js +2 -2
- package/dist/edges/CustomEdge.js.map +1 -1
- package/dist/edges/GenericEdge.d.ts.map +1 -1
- package/dist/edges/GenericEdge.js +2 -2
- package/dist/edges/GenericEdge.js.map +1 -1
- package/dist/hooks/usePathBasedEvents.d.ts +1 -1
- package/dist/hooks/usePathBasedEvents.d.ts.map +1 -1
- package/dist/hooks/usePathBasedEvents.js +9 -9
- package/dist/hooks/usePathBasedEvents.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/nodes/CustomNode.d.ts.map +1 -1
- package/dist/nodes/CustomNode.js +61 -44
- package/dist/nodes/CustomNode.js.map +1 -1
- package/dist/nodes/GenericNode.d.ts.map +1 -1
- package/dist/nodes/GenericNode.js.map +1 -1
- package/dist/utils/animationMapping.d.ts.map +1 -1
- package/dist/utils/animationMapping.js +12 -12
- package/dist/utils/animationMapping.js.map +1 -1
- package/dist/utils/graphConverter.d.ts.map +1 -1
- package/dist/utils/graphConverter.js +23 -17
- package/dist/utils/graphConverter.js.map +1 -1
- package/dist/utils/iconResolver.d.ts.map +1 -1
- package/dist/utils/iconResolver.js +1 -1
- package/dist/utils/iconResolver.js.map +1 -1
- package/package.json +2 -1
- package/src/components/ConfigurationSelector.tsx +5 -5
- package/src/components/EdgeInfoPanel.tsx +79 -37
- package/src/components/GraphRenderer.tsx +526 -365
- package/src/components/NodeInfoPanel.tsx +209 -86
- package/src/edges/CustomEdge.tsx +6 -4
- package/src/edges/GenericEdge.tsx +2 -6
- package/src/hooks/usePathBasedEvents.ts +54 -45
- package/src/index.ts +11 -2
- package/src/nodes/CustomNode.tsx +132 -106
- package/src/nodes/GenericNode.tsx +4 -3
- package/src/stories/AnimationWorkshop.stories.tsx +131 -12
- package/src/stories/CanvasNodeTypes.stories.tsx +898 -0
- package/src/stories/ColorPriority.stories.tsx +20 -10
- package/src/stories/EventDrivenAnimations.stories.tsx +8 -0
- package/src/stories/EventLog.stories.tsx +1 -1
- package/src/stories/GraphRenderer.stories.tsx +23 -10
- package/src/stories/IndustryThemes.stories.tsx +481 -0
- package/src/stories/MultiConfig.stories.tsx +8 -0
- package/src/stories/MultiDirectionalConnections.stories.tsx +8 -0
- package/src/stories/NodeFieldsAudit.stories.tsx +124 -37
- package/src/stories/NodeShapes.stories.tsx +73 -59
- package/src/utils/animationMapping.ts +19 -23
- package/src/utils/graphConverter.ts +35 -19
- package/src/utils/iconResolver.tsx +5 -1
|
@@ -6,6 +6,7 @@ const react_1 = require("react");
|
|
|
6
6
|
const react_2 = require("@xyflow/react");
|
|
7
7
|
require("@xyflow/react/dist/style.css");
|
|
8
8
|
const principal_view_core_1 = require("@principal-ai/principal-view-core");
|
|
9
|
+
const industry_theme_1 = require("@principal-ade/industry-theme");
|
|
9
10
|
const CustomNode_1 = require("../nodes/CustomNode");
|
|
10
11
|
const CustomEdge_1 = require("../edges/CustomEdge");
|
|
11
12
|
const graphConverter_1 = require("../utils/graphConverter");
|
|
@@ -31,6 +32,7 @@ const createEmptyEditState = () => ({
|
|
|
31
32
|
*/
|
|
32
33
|
const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges, violations = [], configName: _configName, showMinimap = true, showControls = true, showBackground = true, events = [], onEventProcessed, editable = false, onPendingChangesChange, onEditStateChange, editStateRef, }) => {
|
|
33
34
|
const { fitView } = (0, react_2.useReactFlow)();
|
|
35
|
+
const { theme } = (0, industry_theme_1.useTheme)();
|
|
34
36
|
// Track active animations
|
|
35
37
|
const [animationState, setAnimationState] = (0, react_1.useState)({
|
|
36
38
|
nodeAnimations: {},
|
|
@@ -49,13 +51,25 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
49
51
|
const [localNodes, setLocalNodes] = (0, react_1.useState)(propNodes);
|
|
50
52
|
const [localEdges, setLocalEdges] = (0, react_1.useState)(propEdges);
|
|
51
53
|
// Track the prop values to detect external changes
|
|
52
|
-
const propNodesKeyRef = (0, react_1.useRef)(propNodes
|
|
53
|
-
|
|
54
|
+
const propNodesKeyRef = (0, react_1.useRef)(propNodes
|
|
55
|
+
.map((n) => n.id)
|
|
56
|
+
.sort()
|
|
57
|
+
.join(','));
|
|
58
|
+
const propEdgesKeyRef = (0, react_1.useRef)(propEdges
|
|
59
|
+
.map((e) => e.id)
|
|
60
|
+
.sort()
|
|
61
|
+
.join(','));
|
|
54
62
|
// Sync local state with props when props change (e.g., config reload)
|
|
55
63
|
// This only happens when the structure changes, not during editing
|
|
56
64
|
(0, react_1.useEffect)(() => {
|
|
57
|
-
const newNodesKey = propNodes
|
|
58
|
-
|
|
65
|
+
const newNodesKey = propNodes
|
|
66
|
+
.map((n) => n.id)
|
|
67
|
+
.sort()
|
|
68
|
+
.join(',');
|
|
69
|
+
const newEdgesKey = propEdges
|
|
70
|
+
.map((e) => e.id)
|
|
71
|
+
.sort()
|
|
72
|
+
.join(',');
|
|
59
73
|
if (newNodesKey !== propNodesKeyRef.current || newEdgesKey !== propEdgesKeyRef.current) {
|
|
60
74
|
propNodesKeyRef.current = newNodesKey;
|
|
61
75
|
propEdgesKeyRef.current = newEdgesKey;
|
|
@@ -73,11 +87,11 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
73
87
|
const edges = editable ? localEdges : propEdges;
|
|
74
88
|
// Helper to check if there are pending changes
|
|
75
89
|
const checkHasChanges = (0, react_1.useCallback)((state) => {
|
|
76
|
-
return state.positionChanges.size > 0 ||
|
|
90
|
+
return (state.positionChanges.size > 0 ||
|
|
77
91
|
state.nodeUpdates.size > 0 ||
|
|
78
92
|
state.deletedNodeIds.size > 0 ||
|
|
79
93
|
state.createdEdges.length > 0 ||
|
|
80
|
-
state.deletedEdges.length > 0;
|
|
94
|
+
state.deletedEdges.length > 0);
|
|
81
95
|
}, []);
|
|
82
96
|
// Helper to update edit state and notify parent
|
|
83
97
|
const updateEditState = (0, react_1.useCallback)((updater) => {
|
|
@@ -112,7 +126,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
112
126
|
if (!editable)
|
|
113
127
|
return;
|
|
114
128
|
// Update local nodes
|
|
115
|
-
setLocalNodes(prev => prev.map(node => {
|
|
129
|
+
setLocalNodes((prev) => prev.map((node) => {
|
|
116
130
|
if (node.id === nodeId) {
|
|
117
131
|
return {
|
|
118
132
|
...node,
|
|
@@ -123,7 +137,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
123
137
|
return node;
|
|
124
138
|
}));
|
|
125
139
|
// Track the change
|
|
126
|
-
updateEditState(prev => {
|
|
140
|
+
updateEditState((prev) => {
|
|
127
141
|
const newUpdates = new Map(prev.nodeUpdates);
|
|
128
142
|
const existing = newUpdates.get(nodeId) || {};
|
|
129
143
|
newUpdates.set(nodeId, {
|
|
@@ -138,10 +152,10 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
138
152
|
if (!editable)
|
|
139
153
|
return;
|
|
140
154
|
// Remove from local state
|
|
141
|
-
setLocalNodes(prev => prev.filter(n => n.id !== nodeId));
|
|
142
|
-
setLocalEdges(prev => prev.filter(e => e.from !== nodeId && e.to !== nodeId));
|
|
155
|
+
setLocalNodes((prev) => prev.filter((n) => n.id !== nodeId));
|
|
156
|
+
setLocalEdges((prev) => prev.filter((e) => e.from !== nodeId && e.to !== nodeId));
|
|
143
157
|
// Track the change
|
|
144
|
-
updateEditState(prev => {
|
|
158
|
+
updateEditState((prev) => {
|
|
145
159
|
const newDeletedNodes = new Set(prev.deletedNodeIds);
|
|
146
160
|
newDeletedNodes.add(nodeId);
|
|
147
161
|
// Remove any pending updates for this node
|
|
@@ -151,7 +165,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
151
165
|
const newPositions = new Map(prev.positionChanges);
|
|
152
166
|
newPositions.delete(nodeId);
|
|
153
167
|
// Remove created edges that involve this node
|
|
154
|
-
const newCreatedEdges = prev.createdEdges.filter(e => e.from !== nodeId && e.to !== nodeId);
|
|
168
|
+
const newCreatedEdges = prev.createdEdges.filter((e) => e.from !== nodeId && e.to !== nodeId);
|
|
155
169
|
return {
|
|
156
170
|
...prev,
|
|
157
171
|
deletedNodeIds: newDeletedNodes,
|
|
@@ -167,13 +181,13 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
167
181
|
if (!editable)
|
|
168
182
|
return;
|
|
169
183
|
// Find the edge before removing it so we can track its full info
|
|
170
|
-
const edgeToDelete = localEdges.find(e => e.id === edgeId);
|
|
184
|
+
const edgeToDelete = localEdges.find((e) => e.id === edgeId);
|
|
171
185
|
// Remove from local state
|
|
172
|
-
setLocalEdges(prev => prev.filter(e => e.id !== edgeId));
|
|
186
|
+
setLocalEdges((prev) => prev.filter((e) => e.id !== edgeId));
|
|
173
187
|
// Track the change
|
|
174
|
-
updateEditState(prev => {
|
|
188
|
+
updateEditState((prev) => {
|
|
175
189
|
// Check if this was a newly created edge
|
|
176
|
-
const createdEdgeIndex = prev.createdEdges.findIndex(e => e.id === edgeId);
|
|
190
|
+
const createdEdgeIndex = prev.createdEdges.findIndex((e) => e.id === edgeId);
|
|
177
191
|
if (createdEdgeIndex >= 0) {
|
|
178
192
|
// Just remove it from created edges
|
|
179
193
|
const newCreatedEdges = [...prev.createdEdges];
|
|
@@ -182,12 +196,15 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
182
196
|
}
|
|
183
197
|
// Otherwise mark as deleted with full edge info
|
|
184
198
|
if (edgeToDelete) {
|
|
185
|
-
const newDeletedEdges = [
|
|
199
|
+
const newDeletedEdges = [
|
|
200
|
+
...prev.deletedEdges,
|
|
201
|
+
{
|
|
186
202
|
id: edgeId,
|
|
187
203
|
from: edgeToDelete.from,
|
|
188
204
|
to: edgeToDelete.to,
|
|
189
|
-
type: edgeToDelete.type
|
|
190
|
-
}
|
|
205
|
+
type: edgeToDelete.type,
|
|
206
|
+
},
|
|
207
|
+
];
|
|
191
208
|
return { ...prev, deletedEdges: newDeletedEdges };
|
|
192
209
|
}
|
|
193
210
|
return prev;
|
|
@@ -199,14 +216,14 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
199
216
|
if (!editable || !connection.source || !connection.target)
|
|
200
217
|
return;
|
|
201
218
|
// Find source and target node types
|
|
202
|
-
const sourceNode = nodes.find(n => n.id === connection.source);
|
|
203
|
-
const targetNode = nodes.find(n => n.id === connection.target);
|
|
219
|
+
const sourceNode = nodes.find((n) => n.id === connection.source);
|
|
220
|
+
const targetNode = nodes.find((n) => n.id === connection.target);
|
|
204
221
|
if (!sourceNode || !targetNode)
|
|
205
222
|
return;
|
|
206
223
|
// Find valid edge types for this connection
|
|
207
224
|
const validTypes = configuration.allowedConnections
|
|
208
|
-
.filter(ac => ac.from === sourceNode.type && ac.to === targetNode.type)
|
|
209
|
-
.map(ac => ac.via);
|
|
225
|
+
.filter((ac) => ac.from === sourceNode.type && ac.to === targetNode.type)
|
|
226
|
+
.map((ac) => ac.via);
|
|
210
227
|
const uniqueTypes = [...new Set(validTypes)];
|
|
211
228
|
if (uniqueTypes.length === 0) {
|
|
212
229
|
console.warn(`No valid edge types for connection from ${sourceNode.type} to ${targetNode.type}`);
|
|
@@ -242,11 +259,14 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
242
259
|
sourceHandle,
|
|
243
260
|
targetHandle,
|
|
244
261
|
};
|
|
245
|
-
setLocalEdges(prev => [...prev, newEdge]);
|
|
262
|
+
setLocalEdges((prev) => [...prev, newEdge]);
|
|
246
263
|
// Track the change
|
|
247
|
-
updateEditState(prev => ({
|
|
264
|
+
updateEditState((prev) => ({
|
|
248
265
|
...prev,
|
|
249
|
-
createdEdges: [
|
|
266
|
+
createdEdges: [
|
|
267
|
+
...prev.createdEdges,
|
|
268
|
+
{ id: edgeId, from, to, type, sourceHandle, targetHandle },
|
|
269
|
+
],
|
|
250
270
|
}));
|
|
251
271
|
}, [updateEditState]);
|
|
252
272
|
// Handle edge type selection from picker
|
|
@@ -271,16 +291,16 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
271
291
|
if (!editable || !newConnection.source || !newConnection.target)
|
|
272
292
|
return;
|
|
273
293
|
// Find the original edge in our local state
|
|
274
|
-
const originalEdge = localEdges.find(e => e.id === oldEdge.id);
|
|
294
|
+
const originalEdge = localEdges.find((e) => e.id === oldEdge.id);
|
|
275
295
|
if (!originalEdge)
|
|
276
296
|
return;
|
|
277
297
|
// Find source and target node types for validation
|
|
278
|
-
const sourceNode = nodes.find(n => n.id === newConnection.source);
|
|
279
|
-
const targetNode = nodes.find(n => n.id === newConnection.target);
|
|
298
|
+
const sourceNode = nodes.find((n) => n.id === newConnection.source);
|
|
299
|
+
const targetNode = nodes.find((n) => n.id === newConnection.target);
|
|
280
300
|
if (!sourceNode || !targetNode)
|
|
281
301
|
return;
|
|
282
302
|
// Check if the new connection is valid for this edge type
|
|
283
|
-
const isValidConnection = configuration.allowedConnections.some(ac => ac.from === sourceNode.type && ac.to === targetNode.type && ac.via === originalEdge.type);
|
|
303
|
+
const isValidConnection = configuration.allowedConnections.some((ac) => ac.from === sourceNode.type && ac.to === targetNode.type && ac.via === originalEdge.type);
|
|
284
304
|
if (!isValidConnection) {
|
|
285
305
|
console.warn(`Cannot reconnect: ${originalEdge.type} edge not allowed from ${sourceNode.type} to ${targetNode.type}`);
|
|
286
306
|
return;
|
|
@@ -288,7 +308,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
288
308
|
// Mark as successful before updating
|
|
289
309
|
edgeReconnectSuccessful.current = true;
|
|
290
310
|
// Update local edges - manually update the edge to preserve its type and id
|
|
291
|
-
setLocalEdges(prev => prev.map(edge => {
|
|
311
|
+
setLocalEdges((prev) => prev.map((edge) => {
|
|
292
312
|
if (edge.id === oldEdge.id) {
|
|
293
313
|
return {
|
|
294
314
|
...edge,
|
|
@@ -302,9 +322,9 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
302
322
|
return edge;
|
|
303
323
|
}));
|
|
304
324
|
// Track the change - remove old edge and add new one
|
|
305
|
-
updateEditState(prev => {
|
|
325
|
+
updateEditState((prev) => {
|
|
306
326
|
// Check if this was a newly created edge
|
|
307
|
-
const createdEdgeIndex = prev.createdEdges.findIndex(e => e.id === oldEdge.id);
|
|
327
|
+
const createdEdgeIndex = prev.createdEdges.findIndex((e) => e.id === oldEdge.id);
|
|
308
328
|
if (createdEdgeIndex >= 0) {
|
|
309
329
|
// Update the created edge entry
|
|
310
330
|
const newCreatedEdges = [...prev.createdEdges];
|
|
@@ -318,20 +338,26 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
318
338
|
return { ...prev, createdEdges: newCreatedEdges };
|
|
319
339
|
}
|
|
320
340
|
// For existing edges, track as delete + create
|
|
321
|
-
const newDeletedEdges = [
|
|
341
|
+
const newDeletedEdges = [
|
|
342
|
+
...prev.deletedEdges,
|
|
343
|
+
{
|
|
322
344
|
id: oldEdge.id,
|
|
323
345
|
from: originalEdge.from,
|
|
324
346
|
to: originalEdge.to,
|
|
325
347
|
type: originalEdge.type,
|
|
326
|
-
}
|
|
327
|
-
|
|
348
|
+
},
|
|
349
|
+
];
|
|
350
|
+
const newCreatedEdges = [
|
|
351
|
+
...prev.createdEdges,
|
|
352
|
+
{
|
|
328
353
|
id: oldEdge.id,
|
|
329
354
|
from: newConnection.source,
|
|
330
355
|
to: newConnection.target,
|
|
331
356
|
type: originalEdge.type,
|
|
332
357
|
sourceHandle: newConnection.sourceHandle ?? undefined,
|
|
333
358
|
targetHandle: newConnection.targetHandle ?? undefined,
|
|
334
|
-
}
|
|
359
|
+
},
|
|
360
|
+
];
|
|
335
361
|
return { ...prev, deletedEdges: newDeletedEdges, createdEdges: newCreatedEdges };
|
|
336
362
|
});
|
|
337
363
|
}, [editable, localEdges, nodes, configuration.allowedConnections, updateEditState]);
|
|
@@ -348,7 +374,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
348
374
|
const selectedEdge = (0, react_1.useMemo)(() => {
|
|
349
375
|
if (!selectedEdgeId)
|
|
350
376
|
return null;
|
|
351
|
-
return edges.find(e => e.id === selectedEdgeId);
|
|
377
|
+
return edges.find((e) => e.id === selectedEdgeId);
|
|
352
378
|
}, [selectedEdgeId, edges]);
|
|
353
379
|
const selectedEdgeTypeDefinition = (0, react_1.useMemo)(() => {
|
|
354
380
|
if (!selectedEdge)
|
|
@@ -358,7 +384,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
358
384
|
const selectedNode = (0, react_1.useMemo)(() => {
|
|
359
385
|
if (!selectedNodeId)
|
|
360
386
|
return null;
|
|
361
|
-
return nodes.find(n => n.id === selectedNodeId);
|
|
387
|
+
return nodes.find((n) => n.id === selectedNodeId);
|
|
362
388
|
}, [selectedNodeId, nodes]);
|
|
363
389
|
const selectedNodeTypeDefinition = (0, react_1.useMemo)(() => {
|
|
364
390
|
if (!selectedNode)
|
|
@@ -377,7 +403,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
377
403
|
const edgeId = edgeEvent.edgeId;
|
|
378
404
|
const animation = edgeEvent.animation;
|
|
379
405
|
if (animation && edgeId) {
|
|
380
|
-
setAnimationState(prev => ({
|
|
406
|
+
setAnimationState((prev) => ({
|
|
381
407
|
...prev,
|
|
382
408
|
edgeAnimations: {
|
|
383
409
|
...prev.edgeAnimations,
|
|
@@ -391,7 +417,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
391
417
|
}));
|
|
392
418
|
const duration = animation.duration || 1000;
|
|
393
419
|
setTimeout(() => {
|
|
394
|
-
setAnimationState(prev => {
|
|
420
|
+
setAnimationState((prev) => {
|
|
395
421
|
const newEdgeAnimations = { ...prev.edgeAnimations };
|
|
396
422
|
delete newEdgeAnimations[edgeId];
|
|
397
423
|
return { ...prev, edgeAnimations: newEdgeAnimations };
|
|
@@ -406,7 +432,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
406
432
|
const newState = stateEvent.newState;
|
|
407
433
|
if (nodeId && newState) {
|
|
408
434
|
// Update the node's state
|
|
409
|
-
setLocalNodes(prev => prev.map(node => node.id === nodeId ? { ...node, state: newState } : node));
|
|
435
|
+
setLocalNodes((prev) => prev.map((node) => (node.id === nodeId ? { ...node, state: newState } : node)));
|
|
410
436
|
// Trigger animation based on state
|
|
411
437
|
const stateToAnimation = {
|
|
412
438
|
processing: 'pulse',
|
|
@@ -416,7 +442,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
416
442
|
const animationType = stateToAnimation[newState];
|
|
417
443
|
if (animationType) {
|
|
418
444
|
const duration = animationType === 'pulse' ? 1500 : animationType === 'flash' ? 1000 : 500;
|
|
419
|
-
setAnimationState(prev => ({
|
|
445
|
+
setAnimationState((prev) => ({
|
|
420
446
|
...prev,
|
|
421
447
|
nodeAnimations: {
|
|
422
448
|
...prev.nodeAnimations,
|
|
@@ -425,7 +451,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
425
451
|
}));
|
|
426
452
|
if (animationType !== 'pulse') {
|
|
427
453
|
setTimeout(() => {
|
|
428
|
-
setAnimationState(prev => {
|
|
454
|
+
setAnimationState((prev) => {
|
|
429
455
|
const newNodeAnimations = { ...prev.nodeAnimations };
|
|
430
456
|
delete newNodeAnimations[nodeId];
|
|
431
457
|
return { ...prev, nodeAnimations: newNodeAnimations };
|
|
@@ -440,7 +466,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
440
466
|
const nodeEvent = latestEvent.payload;
|
|
441
467
|
const nodeId = nodeEvent.nodeId;
|
|
442
468
|
if (nodeId) {
|
|
443
|
-
setAnimationState(prev => ({
|
|
469
|
+
setAnimationState((prev) => ({
|
|
444
470
|
...prev,
|
|
445
471
|
nodeAnimations: {
|
|
446
472
|
...prev.nodeAnimations,
|
|
@@ -448,7 +474,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
448
474
|
},
|
|
449
475
|
}));
|
|
450
476
|
setTimeout(() => {
|
|
451
|
-
setAnimationState(prev => {
|
|
477
|
+
setAnimationState((prev) => {
|
|
452
478
|
const newNodeAnimations = { ...prev.nodeAnimations };
|
|
453
479
|
delete newNodeAnimations[nodeId];
|
|
454
480
|
return { ...prev, nodeAnimations: newNodeAnimations };
|
|
@@ -465,7 +491,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
465
491
|
const converted = (0, graphConverter_1.convertToXYFlowNodes)(nodes, configuration, violations);
|
|
466
492
|
const layoutType = configuration.display?.layout || 'hierarchical';
|
|
467
493
|
const positioned = (0, graphConverter_1.autoLayoutNodes)(converted, [], layoutType);
|
|
468
|
-
return positioned.map(node => {
|
|
494
|
+
return positioned.map((node) => {
|
|
469
495
|
const animation = animationState.nodeAnimations[node.id];
|
|
470
496
|
// Apply any pending position changes
|
|
471
497
|
const pendingPosition = editStateRef.current.positionChanges.get(node.id);
|
|
@@ -475,16 +501,21 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
475
501
|
data: {
|
|
476
502
|
...node.data,
|
|
477
503
|
editable,
|
|
478
|
-
...(animation
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
504
|
+
...(animation
|
|
505
|
+
? {
|
|
506
|
+
animationType: animation.type,
|
|
507
|
+
animationDuration: animation.duration,
|
|
508
|
+
}
|
|
509
|
+
: {}),
|
|
482
510
|
},
|
|
483
511
|
};
|
|
484
512
|
});
|
|
485
513
|
}, [nodes, configuration, violations, animationState.nodeAnimations, editable, editStateRef]);
|
|
486
514
|
const baseNodesKey = (0, react_1.useMemo)(() => {
|
|
487
|
-
return nodes
|
|
515
|
+
return nodes
|
|
516
|
+
.map((n) => n.id)
|
|
517
|
+
.sort()
|
|
518
|
+
.join(',');
|
|
488
519
|
}, [nodes]);
|
|
489
520
|
// Local xyflow nodes state for dragging
|
|
490
521
|
const [xyflowLocalNodes, setXyflowLocalNodes] = (0, react_1.useState)(xyflowNodesBase);
|
|
@@ -510,16 +541,15 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
510
541
|
const handleNodesChange = (0, react_1.useCallback)((changes) => {
|
|
511
542
|
if (!editable)
|
|
512
543
|
return;
|
|
513
|
-
setXyflowLocalNodes(nds => (0, react_2.applyNodeChanges)(changes, nds));
|
|
544
|
+
setXyflowLocalNodes((nds) => (0, react_2.applyNodeChanges)(changes, nds));
|
|
514
545
|
// Track position changes on drag end
|
|
515
|
-
const positionChanges = changes
|
|
516
|
-
.filter((change) => change.type === 'position' &&
|
|
546
|
+
const positionChanges = changes.filter((change) => change.type === 'position' &&
|
|
517
547
|
'position' in change &&
|
|
518
548
|
change.position !== undefined &&
|
|
519
549
|
'dragging' in change &&
|
|
520
550
|
change.dragging === false);
|
|
521
551
|
if (positionChanges.length > 0) {
|
|
522
|
-
updateEditState(prev => {
|
|
552
|
+
updateEditState((prev) => {
|
|
523
553
|
const newPositions = new Map(prev.positionChanges);
|
|
524
554
|
for (const change of positionChanges) {
|
|
525
555
|
newPositions.set(change.id, {
|
|
@@ -533,7 +563,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
533
563
|
}, [editable, updateEditState]);
|
|
534
564
|
const xyflowEdges = (0, react_1.useMemo)(() => {
|
|
535
565
|
const converted = (0, graphConverter_1.convertToXYFlowEdges)(edges, configuration, violations);
|
|
536
|
-
return converted.map(edge => {
|
|
566
|
+
return converted.map((edge) => {
|
|
537
567
|
const animation = animationState.edgeAnimations[edge.id];
|
|
538
568
|
if (animation) {
|
|
539
569
|
return {
|
|
@@ -565,26 +595,28 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
565
595
|
// ============================================
|
|
566
596
|
// RENDER
|
|
567
597
|
// ============================================
|
|
568
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_2.ReactFlow, { nodes: xyflowNodes, edges: xyflowEdges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, minZoom: 0.1, maxZoom: 4, defaultEdgeOptions: { type: 'custom' }, onEdgeClick: onEdgeClick, onNodeClick: onNodeClick, proOptions: { hideAttribution: true }, nodesDraggable: editable, elementsSelectable: editable, nodesConnectable: editable, edgesReconnectable: editable, onNodesChange: handleNodesChange, onConnect: handleConnect, onReconnectStart: handleReconnectStart, onReconnect: handleReconnect, onReconnectEnd: handleReconnectEnd, panOnDrag: true, selectionOnDrag: false, children: [showBackground && (0, jsx_runtime_1.jsx)(react_2.Background, { color:
|
|
598
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_2.ReactFlow, { nodes: xyflowNodes, edges: xyflowEdges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, minZoom: 0.1, maxZoom: 4, defaultEdgeOptions: { type: 'custom' }, onEdgeClick: onEdgeClick, onNodeClick: onNodeClick, proOptions: { hideAttribution: true }, nodesDraggable: editable, elementsSelectable: editable, nodesConnectable: editable, edgesReconnectable: editable, onNodesChange: handleNodesChange, onConnect: handleConnect, onReconnectStart: handleReconnectStart, onReconnect: handleReconnect, onReconnectEnd: handleReconnectEnd, panOnDrag: true, selectionOnDrag: false, children: [showBackground && (0, jsx_runtime_1.jsx)(react_2.Background, { color: theme.colors.border, gap: 16, size: 1 }), showControls && (0, jsx_runtime_1.jsx)(react_2.Controls, { showZoom: true, showFitView: true, showInteractive: true }), showMinimap && ((0, jsx_runtime_1.jsx)(react_2.MiniMap, { nodeColor: (node) => {
|
|
569
599
|
const nodeData = node.data;
|
|
570
|
-
return nodeData?.typeDefinition?.color ||
|
|
600
|
+
return nodeData?.typeDefinition?.color || theme.colors.secondary;
|
|
571
601
|
}, nodeBorderRadius: 2, pannable: true, zoomable: true }))] }, baseNodesKey), selectedEdge && selectedEdgeTypeDefinition && ((0, jsx_runtime_1.jsx)(EdgeInfoPanel_1.EdgeInfoPanel, { edge: selectedEdge, typeDefinition: selectedEdgeTypeDefinition, sourceNodeId: selectedEdge.from, targetNodeId: selectedEdge.to, onClose: onCloseEdgeInfoPanel, onDelete: editable ? handleEdgeDelete : undefined })), selectedNode && selectedNodeTypeDefinition && ((0, jsx_runtime_1.jsx)(NodeInfoPanel_1.NodeInfoPanel, { node: selectedNode, typeDefinition: selectedNodeTypeDefinition, availableNodeTypes: configuration.nodeTypes, onClose: onCloseNodeInfoPanel, onDelete: editable ? handleNodeDelete : undefined, onUpdate: editable ? handleNodeUpdate : undefined })), pendingConnection && ((0, jsx_runtime_1.jsxs)("div", { style: {
|
|
572
602
|
position: 'absolute',
|
|
573
603
|
top: '50%',
|
|
574
604
|
left: '50%',
|
|
575
605
|
transform: 'translate(-50%, -50%)',
|
|
576
|
-
backgroundColor:
|
|
606
|
+
backgroundColor: theme.colors.background,
|
|
607
|
+
color: theme.colors.text,
|
|
577
608
|
borderRadius: '8px',
|
|
578
609
|
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
|
|
579
610
|
padding: '16px',
|
|
580
611
|
minWidth: '200px',
|
|
581
612
|
zIndex: 1000,
|
|
582
|
-
|
|
613
|
+
border: `1px solid ${theme.colors.border}`,
|
|
614
|
+
}, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontWeight: 'bold', marginBottom: '12px', fontSize: '14px' }, children: "Select Edge Type" }), (0, jsx_runtime_1.jsxs)("div", { style: { fontSize: '12px', color: theme.colors.textSecondary, marginBottom: '12px' }, children: [pendingConnection.from, " \u2192 ", pendingConnection.to] }), (0, jsx_runtime_1.jsx)("div", { style: { display: 'flex', flexDirection: 'column', gap: '8px' }, children: pendingConnection.validTypes.map((type) => {
|
|
583
615
|
const typeDefinition = configuration.edgeTypes[type];
|
|
584
616
|
return ((0, jsx_runtime_1.jsx)("button", { onClick: () => handleEdgeTypeSelect(type), style: {
|
|
585
617
|
padding: '8px 12px',
|
|
586
|
-
backgroundColor: typeDefinition?.color ||
|
|
587
|
-
color:
|
|
618
|
+
backgroundColor: typeDefinition?.color || theme.colors.secondary,
|
|
619
|
+
color: theme.colors.background,
|
|
588
620
|
border: 'none',
|
|
589
621
|
borderRadius: '4px',
|
|
590
622
|
cursor: 'pointer',
|
|
@@ -596,9 +628,9 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
596
628
|
marginTop: '12px',
|
|
597
629
|
width: '100%',
|
|
598
630
|
padding: '8px 12px',
|
|
599
|
-
backgroundColor:
|
|
600
|
-
color:
|
|
601
|
-
border:
|
|
631
|
+
backgroundColor: theme.colors.surface,
|
|
632
|
+
color: theme.colors.textSecondary,
|
|
633
|
+
border: `1px solid ${theme.colors.border}`,
|
|
602
634
|
borderRadius: '4px',
|
|
603
635
|
cursor: 'pointer',
|
|
604
636
|
fontSize: '12px',
|
|
@@ -660,11 +692,20 @@ function useCanvasToLegacy(canvas, library) {
|
|
|
660
692
|
const nodeType = vv?.nodeType || node.type;
|
|
661
693
|
if (!nodeTypes[nodeType]) {
|
|
662
694
|
// Color priority: vv.fill > node.color > vv.states.idle.color
|
|
663
|
-
const fillColor = vv?.fill
|
|
664
|
-
|
|
665
|
-
|
|
695
|
+
const fillColor = vv?.fill ||
|
|
696
|
+
(typeof node.color === 'string' ? node.color : undefined) ||
|
|
697
|
+
vv?.states?.idle?.color;
|
|
698
|
+
// Derive description from text content (everything after line 1) for text nodes
|
|
699
|
+
let nodeDescription = `${nodeType} node`;
|
|
700
|
+
if (node.type === 'text' && 'text' in node) {
|
|
701
|
+
const lines = node.text.split('\n');
|
|
702
|
+
const descFromText = lines.slice(1).join('\n').trim();
|
|
703
|
+
if (descFromText) {
|
|
704
|
+
nodeDescription = descFromText;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
666
707
|
nodeTypes[nodeType] = {
|
|
667
|
-
description:
|
|
708
|
+
description: nodeDescription,
|
|
668
709
|
shape: vv?.shape || 'rectangle',
|
|
669
710
|
icon: vv?.icon,
|
|
670
711
|
color: fillColor,
|
|
@@ -703,8 +744,8 @@ function useCanvasToLegacy(canvas, library) {
|
|
|
703
744
|
};
|
|
704
745
|
}
|
|
705
746
|
// Find node types for from/to
|
|
706
|
-
const fromNode = canvas.nodes?.find(n => n.id === edge.fromNode);
|
|
707
|
-
const toNode = canvas.nodes?.find(n => n.id === edge.toNode);
|
|
747
|
+
const fromNode = canvas.nodes?.find((n) => n.id === edge.fromNode);
|
|
748
|
+
const toNode = canvas.nodes?.find((n) => n.id === edge.toNode);
|
|
708
749
|
const fromType = fromNode?.pv?.nodeType || edge.fromNode;
|
|
709
750
|
const toType = toNode?.pv?.nodeType || edge.toNode;
|
|
710
751
|
allowedConnections.push({
|
|
@@ -758,16 +799,12 @@ function useCanvasToLegacy(canvas, library) {
|
|
|
758
799
|
*/
|
|
759
800
|
exports.GraphRenderer = (0, react_1.forwardRef)((props, ref) => {
|
|
760
801
|
const { canvas, library, className, width = '100%', height = '100%' } = props;
|
|
802
|
+
const { theme } = (0, industry_theme_1.useTheme)();
|
|
761
803
|
// Convert canvas to internal format (merging library types if provided)
|
|
762
804
|
const canvasData = useCanvasToLegacy(canvas, library);
|
|
763
|
-
//
|
|
764
|
-
if (!canvasData) {
|
|
765
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: className, style: { width, height, display: 'flex', alignItems: 'center', justifyContent: 'center' }, children: (0, jsx_runtime_1.jsx)("p", { style: { color: '#666' }, children: "No canvas data provided." }) }));
|
|
766
|
-
}
|
|
767
|
-
const { configuration, nodes, edges } = canvasData;
|
|
768
|
-
// Internal edit state ref
|
|
805
|
+
// Internal edit state ref - must be before any conditional returns
|
|
769
806
|
const editStateRef = (0, react_1.useRef)(createEmptyEditState());
|
|
770
|
-
// Expose imperative handle
|
|
807
|
+
// Expose imperative handle - must be before any conditional returns
|
|
771
808
|
(0, react_1.useImperativeHandle)(ref, () => ({
|
|
772
809
|
getPendingChanges: () => {
|
|
773
810
|
const state = editStateRef.current;
|
|
@@ -781,14 +818,14 @@ exports.GraphRenderer = (0, react_1.forwardRef)((props, ref) => {
|
|
|
781
818
|
updates,
|
|
782
819
|
})),
|
|
783
820
|
deletedNodeIds: Array.from(state.deletedNodeIds),
|
|
784
|
-
createdEdges: state.createdEdges.map(e => ({
|
|
821
|
+
createdEdges: state.createdEdges.map((e) => ({
|
|
785
822
|
from: e.from,
|
|
786
823
|
to: e.to,
|
|
787
824
|
type: e.type,
|
|
788
825
|
sourceHandle: e.sourceHandle,
|
|
789
826
|
targetHandle: e.targetHandle,
|
|
790
827
|
})),
|
|
791
|
-
deletedEdges: state.deletedEdges.map(e => ({ from: e.from, to: e.to, type: e.type })),
|
|
828
|
+
deletedEdges: state.deletedEdges.map((e) => ({ from: e.from, to: e.to, type: e.type })),
|
|
792
829
|
hasChanges: state.positionChanges.size > 0 ||
|
|
793
830
|
state.nodeUpdates.size > 0 ||
|
|
794
831
|
state.deletedNodeIds.size > 0 ||
|
|
@@ -801,13 +838,26 @@ exports.GraphRenderer = (0, react_1.forwardRef)((props, ref) => {
|
|
|
801
838
|
},
|
|
802
839
|
hasUnsavedChanges: () => {
|
|
803
840
|
const state = editStateRef.current;
|
|
804
|
-
return state.positionChanges.size > 0 ||
|
|
841
|
+
return (state.positionChanges.size > 0 ||
|
|
805
842
|
state.nodeUpdates.size > 0 ||
|
|
806
843
|
state.deletedNodeIds.size > 0 ||
|
|
807
844
|
state.createdEdges.length > 0 ||
|
|
808
|
-
state.deletedEdges.length > 0;
|
|
845
|
+
state.deletedEdges.length > 0);
|
|
809
846
|
},
|
|
810
847
|
}), []);
|
|
848
|
+
// Validate we have required data
|
|
849
|
+
if (!canvasData) {
|
|
850
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: className, style: {
|
|
851
|
+
width,
|
|
852
|
+
height,
|
|
853
|
+
display: 'flex',
|
|
854
|
+
alignItems: 'center',
|
|
855
|
+
justifyContent: 'center',
|
|
856
|
+
backgroundColor: theme.colors.background,
|
|
857
|
+
color: theme.colors.textSecondary,
|
|
858
|
+
}, children: (0, jsx_runtime_1.jsx)("p", { children: "No canvas data provided." }) }));
|
|
859
|
+
}
|
|
860
|
+
const { configuration, nodes, edges } = canvasData;
|
|
811
861
|
// Extract only the props that inner component needs
|
|
812
862
|
const { violations, configName, showMinimap, showControls, showBackground, events, onEventProcessed, editable, onPendingChangesChange, } = props;
|
|
813
863
|
return ((0, jsx_runtime_1.jsx)("div", { className: className, style: { width, height, position: 'relative' }, children: (0, jsx_runtime_1.jsx)(react_2.ReactFlowProvider, { children: (0, jsx_runtime_1.jsx)(GraphRendererInner, { configuration: configuration, nodes: nodes, edges: edges, violations: violations, configName: configName, showMinimap: showMinimap, showControls: showControls, showBackground: showBackground, events: events, onEventProcessed: onEventProcessed, editable: editable, onPendingChangesChange: onPendingChangesChange, editStateRef: editStateRef }) }) }));
|