@principal-ai/principal-view-react 0.7.14 → 0.7.16
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 +9 -24
- package/dist/components/ConfigurationSelector.d.ts +1 -1
- package/dist/components/ConfigurationSelector.d.ts.map +1 -1
- package/dist/components/ConfigurationSelector.js +6 -10
- package/dist/components/ConfigurationSelector.js.map +1 -1
- package/dist/components/EdgeInfoPanel.d.ts +1 -1
- package/dist/components/EdgeInfoPanel.d.ts.map +1 -1
- package/dist/components/EdgeInfoPanel.js +14 -18
- package/dist/components/EdgeInfoPanel.js.map +1 -1
- package/dist/components/GraphRenderer.d.ts +1 -1
- package/dist/components/GraphRenderer.d.ts.map +1 -1
- package/dist/components/GraphRenderer.js +89 -92
- package/dist/components/GraphRenderer.js.map +1 -1
- package/dist/components/NarrativeRenderer.d.ts +19 -0
- package/dist/components/NarrativeRenderer.d.ts.map +1 -0
- package/dist/components/NarrativeRenderer.js +103 -0
- package/dist/components/NarrativeRenderer.js.map +1 -0
- package/dist/components/NodeInfoPanel.d.ts +1 -1
- package/dist/components/NodeInfoPanel.d.ts.map +1 -1
- package/dist/components/NodeInfoPanel.js +33 -37
- package/dist/components/NodeInfoPanel.js.map +1 -1
- package/dist/components/NodeTooltip.js +15 -19
- package/dist/components/NodeTooltip.js.map +1 -1
- package/dist/components/SelectionSidebar.d.ts +1 -1
- package/dist/components/SelectionSidebar.d.ts.map +1 -1
- package/dist/components/SelectionSidebar.js +26 -30
- package/dist/components/SelectionSidebar.js.map +1 -1
- package/dist/components/TestEventPanel.d.ts +6 -0
- package/dist/components/TestEventPanel.d.ts.map +1 -1
- package/dist/components/TestEventPanel.js +157 -160
- package/dist/components/TestEventPanel.js.map +1 -1
- package/dist/edges/CustomEdge.d.ts +1 -1
- package/dist/edges/CustomEdge.d.ts.map +1 -1
- package/dist/edges/CustomEdge.js +18 -22
- package/dist/edges/CustomEdge.js.map +1 -1
- package/dist/edges/GenericEdge.d.ts +1 -1
- package/dist/edges/GenericEdge.d.ts.map +1 -1
- package/dist/edges/GenericEdge.js +3 -7
- 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 +14 -18
- package/dist/hooks/usePathBasedEvents.js.map +1 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -33
- package/dist/index.js.map +1 -1
- package/dist/nodes/CustomNode.d.ts +1 -1
- package/dist/nodes/CustomNode.d.ts.map +1 -1
- package/dist/nodes/CustomNode.js +22 -26
- package/dist/nodes/CustomNode.js.map +1 -1
- package/dist/nodes/GenericNode.d.ts +1 -1
- package/dist/nodes/GenericNode.d.ts.map +1 -1
- package/dist/nodes/GenericNode.js +4 -8
- package/dist/nodes/GenericNode.js.map +1 -1
- package/dist/utils/animationMapping.d.ts +1 -1
- package/dist/utils/animationMapping.d.ts.map +1 -1
- package/dist/utils/animationMapping.js +6 -13
- package/dist/utils/animationMapping.js.map +1 -1
- package/dist/utils/graphConverter.d.ts +1 -1
- package/dist/utils/graphConverter.d.ts.map +1 -1
- package/dist/utils/graphConverter.js +4 -8
- package/dist/utils/graphConverter.js.map +1 -1
- package/dist/utils/iconResolver.js +7 -45
- package/dist/utils/iconResolver.js.map +1 -1
- package/dist/utils/narrative-converter.d.ts +45 -0
- package/dist/utils/narrative-converter.d.ts.map +1 -0
- package/dist/utils/narrative-converter.js +113 -0
- package/dist/utils/narrative-converter.js.map +1 -0
- package/dist/utils/narrative-loader.d.ts +53 -0
- package/dist/utils/narrative-loader.d.ts.map +1 -0
- package/dist/utils/narrative-loader.js +163 -0
- package/dist/utils/narrative-loader.js.map +1 -0
- package/package.json +22 -25
- package/src/components/ConfigurationSelector.tsx +1 -1
- package/src/components/EdgeInfoPanel.tsx +1 -1
- package/src/components/GraphRenderer.test.tsx +1 -1
- package/src/components/GraphRenderer.tsx +2 -2
- package/src/components/NarrativeRenderer.tsx +171 -0
- package/src/components/NodeInfoPanel.tsx +1 -1
- package/src/components/NodeTooltip.tsx +3 -3
- package/src/components/PendingChanges.test.tsx +1 -1
- package/src/components/SelectionSidebar.tsx +1 -1
- package/src/components/TestEventPanel.tsx +145 -121
- package/src/edges/CustomEdge.tsx +1 -1
- package/src/edges/GenericEdge.tsx +1 -1
- package/src/hooks/usePathBasedEvents.ts +1 -1
- package/src/index.ts +1 -7
- package/src/nodes/CustomNode.tsx +1 -1
- package/src/nodes/GenericNode.tsx +1 -1
- package/src/stories/AnimationWorkshop.stories.tsx +1 -1
- package/src/stories/CanvasEdgeTypes.stories.tsx +1 -1
- package/src/stories/CanvasNodeTypes.stories.tsx +1 -1
- package/src/stories/ColorPriority.stories.tsx +1 -1
- package/src/stories/EventDrivenAnimations.stories.tsx +1 -1
- package/src/stories/GraphRenderer.stories.tsx +1 -1
- package/src/stories/Introduction.mdx +4 -21
- package/src/stories/MultiConfig.stories.tsx +1 -1
- package/src/stories/MultiDirectionalConnections.stories.tsx +1 -1
- package/src/stories/NodeFieldsAudit.stories.tsx +1 -1
- package/src/stories/NodeShapes.stories.tsx +1 -1
- package/src/stories/OtelComponents.stories.tsx +1 -1
- package/src/stories/RealTestExecution.stories.tsx +111 -1
- package/src/stories/TraceViewer.stories.tsx +1 -1
- package/src/stories/ValidatedExecution.stories.tsx +1 -1
- package/src/stories/data/graph-converter-test-execution.json +204 -326
- package/src/stories/data/graph-converter-test.narrative.json +106 -0
- package/src/stories/data/graph-converter-validated-execution.json +6 -6
- package/src/utils/animationMapping.ts +1 -1
- package/src/utils/graphConverter.ts +1 -1
- package/src/utils/narrative-converter.ts +147 -0
- package/src/utils/narrative-loader.ts +172 -0
- package/dist/components/EventLog.d.ts +0 -20
- package/dist/components/EventLog.d.ts.map +0 -1
- package/dist/components/EventLog.js +0 -13
- package/dist/components/EventLog.js.map +0 -1
- package/dist/components/EventLog.test.d.ts +0 -2
- package/dist/components/EventLog.test.d.ts.map +0 -1
- package/dist/components/EventLog.test.js +0 -73
- package/dist/components/EventLog.test.js.map +0 -1
- package/dist/components/GraphRenderer.test.d.ts +0 -2
- package/dist/components/GraphRenderer.test.d.ts.map +0 -1
- package/dist/components/GraphRenderer.test.js +0 -88
- package/dist/components/GraphRenderer.test.js.map +0 -1
- package/dist/components/LayerPanel.d.ts +0 -31
- package/dist/components/LayerPanel.d.ts.map +0 -1
- package/dist/components/LayerPanel.js +0 -207
- package/dist/components/LayerPanel.js.map +0 -1
- package/dist/components/MetricsDashboard.d.ts +0 -14
- package/dist/components/MetricsDashboard.d.ts.map +0 -1
- package/dist/components/MetricsDashboard.js +0 -13
- package/dist/components/MetricsDashboard.js.map +0 -1
- package/src/components/EventLog.test.tsx +0 -85
- package/src/components/EventLog.tsx +0 -51
- package/src/components/MetricsDashboard.tsx +0 -40
- package/src/stories/EventLog.stories.tsx +0 -161
- package/src/stories/IndustryThemes.stories.tsx +0 -483
- package/src/stories/MetricsDashboard.stories.tsx +0 -227
|
@@ -1,25 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const EdgeInfoPanel_1 = require("./EdgeInfoPanel");
|
|
14
|
-
const NodeInfoPanel_1 = require("./NodeInfoPanel");
|
|
15
|
-
const SelectionSidebar_1 = require("./SelectionSidebar");
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState, useEffect, useCallback, useRef, useImperativeHandle, forwardRef, } from 'react';
|
|
3
|
+
import { ReactFlow, Background, BackgroundVariant, Controls, MiniMap, ReactFlowProvider, useReactFlow, useViewport, applyNodeChanges, } from '@xyflow/react';
|
|
4
|
+
import '@xyflow/react/dist/style.css';
|
|
5
|
+
import { CanvasConverter } from '@principal-ai/principal-view-core/browser';
|
|
6
|
+
import { useTheme } from '@principal-ade/industry-theme';
|
|
7
|
+
import { CustomNode } from '../nodes/CustomNode';
|
|
8
|
+
import { CustomEdge } from '../edges/CustomEdge';
|
|
9
|
+
import { convertToXYFlowNodes, convertToXYFlowEdges, } from '../utils/graphConverter';
|
|
10
|
+
import { EdgeInfoPanel } from './EdgeInfoPanel';
|
|
11
|
+
import { NodeInfoPanel } from './NodeInfoPanel';
|
|
12
|
+
import { SelectionSidebar } from './SelectionSidebar';
|
|
16
13
|
// Define custom node types
|
|
17
14
|
const nodeTypes = {
|
|
18
|
-
custom:
|
|
15
|
+
custom: CustomNode,
|
|
19
16
|
};
|
|
20
17
|
// Define custom edge types
|
|
21
18
|
const edgeTypes = {
|
|
22
|
-
custom:
|
|
19
|
+
custom: CustomEdge,
|
|
23
20
|
};
|
|
24
21
|
const createEmptyEditState = () => ({
|
|
25
22
|
positionChanges: new Map(),
|
|
@@ -34,14 +31,14 @@ const createEmptyEditState = () => ({
|
|
|
34
31
|
* Uses viewport transform to position correctly regardless of pan/zoom.
|
|
35
32
|
*/
|
|
36
33
|
const CenterIndicator = ({ color }) => {
|
|
37
|
-
const { x, y } =
|
|
34
|
+
const { x, y } = useViewport();
|
|
38
35
|
// Size of the crosshair in screen pixels (stays constant regardless of zoom)
|
|
39
36
|
const size = 20;
|
|
40
37
|
const strokeWidth = 1.5;
|
|
41
38
|
// The viewport transform places origin (0,0) at screen position (x, y)
|
|
42
39
|
const screenX = x;
|
|
43
40
|
const screenY = y;
|
|
44
|
-
return ((
|
|
41
|
+
return (_jsxs("svg", { style: {
|
|
45
42
|
position: 'absolute',
|
|
46
43
|
top: 0,
|
|
47
44
|
left: 0,
|
|
@@ -49,46 +46,46 @@ const CenterIndicator = ({ color }) => {
|
|
|
49
46
|
height: '100%',
|
|
50
47
|
pointerEvents: 'none',
|
|
51
48
|
zIndex: 5,
|
|
52
|
-
}, children: [(
|
|
49
|
+
}, children: [_jsx("line", { x1: screenX, y1: screenY - size, x2: screenX, y2: screenY + size, stroke: color, strokeWidth: strokeWidth, opacity: 0.7 }), _jsx("line", { x1: screenX - size, y1: screenY, x2: screenX + size, y2: screenY, stroke: color, strokeWidth: strokeWidth, opacity: 0.7 }), _jsx("circle", { cx: screenX, cy: screenY, r: 3, fill: color, opacity: 0.7 })] }));
|
|
53
50
|
};
|
|
54
51
|
/**
|
|
55
52
|
* Inner component that uses ReactFlow hooks
|
|
56
53
|
*/
|
|
57
54
|
const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges, violations = [], configName: _configName, showMinimap = false, showControls = true, showBackground = true, backgroundVariant = 'dots', backgroundGap, showCenterIndicator = false, showTooltips = true, fitViewDuration = 200, highlightedNodeId, events = [], onEventProcessed, editable = false, onPendingChangesChange, onEditStateChange, editStateRef, onSourceClick, }) => {
|
|
58
|
-
const { fitView } =
|
|
59
|
-
const { theme } =
|
|
55
|
+
const { fitView } = useReactFlow();
|
|
56
|
+
const { theme } = useTheme();
|
|
60
57
|
// Track active animations
|
|
61
|
-
const [animationState, setAnimationState] =
|
|
58
|
+
const [animationState, setAnimationState] = useState({
|
|
62
59
|
nodeAnimations: {},
|
|
63
60
|
edgeAnimations: {},
|
|
64
61
|
});
|
|
65
62
|
// Track selected edges for info panel (supports multi-select)
|
|
66
|
-
const [selectedEdgeIds, setSelectedEdgeIds] =
|
|
63
|
+
const [selectedEdgeIds, setSelectedEdgeIds] = useState(new Set());
|
|
67
64
|
// Track selected nodes for info panel (supports multi-select)
|
|
68
|
-
const [selectedNodeIds, setSelectedNodeIds] =
|
|
65
|
+
const [selectedNodeIds, setSelectedNodeIds] = useState(new Set());
|
|
69
66
|
// Track whether panel should be shown (only on explicit clicks, not after dragging)
|
|
70
|
-
const [showNodePanel, setShowNodePanel] =
|
|
71
|
-
const [showEdgePanel, setShowEdgePanel] =
|
|
67
|
+
const [showNodePanel, setShowNodePanel] = useState(false);
|
|
68
|
+
const [showEdgePanel, setShowEdgePanel] = useState(false);
|
|
72
69
|
// Track pending connection for edge type picker
|
|
73
|
-
const [pendingConnection, setPendingConnection] =
|
|
70
|
+
const [pendingConnection, setPendingConnection] = useState(null);
|
|
74
71
|
// ============================================
|
|
75
72
|
// INTERNAL EDIT STATE
|
|
76
73
|
// ============================================
|
|
77
74
|
// Local copies of nodes and edges for editing
|
|
78
|
-
const [localNodes, setLocalNodes] =
|
|
79
|
-
const [localEdges, setLocalEdges] =
|
|
75
|
+
const [localNodes, setLocalNodes] = useState(propNodes);
|
|
76
|
+
const [localEdges, setLocalEdges] = useState(propEdges);
|
|
80
77
|
// Track the prop values to detect external changes
|
|
81
|
-
const propNodesKeyRef =
|
|
78
|
+
const propNodesKeyRef = useRef(propNodes
|
|
82
79
|
.map((n) => n.id)
|
|
83
80
|
.sort()
|
|
84
81
|
.join(','));
|
|
85
|
-
const propEdgesKeyRef =
|
|
82
|
+
const propEdgesKeyRef = useRef(propEdges
|
|
86
83
|
.map((e) => e.id)
|
|
87
84
|
.sort()
|
|
88
85
|
.join(','));
|
|
89
86
|
// Sync local state with props when props change (e.g., config reload)
|
|
90
87
|
// This only happens when the structure changes, not during editing
|
|
91
|
-
|
|
88
|
+
useEffect(() => {
|
|
92
89
|
const newNodesKey = propNodes
|
|
93
90
|
.map((n) => n.id)
|
|
94
91
|
.sort()
|
|
@@ -126,7 +123,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
126
123
|
const nodes = localNodes;
|
|
127
124
|
const edges = editable ? localEdges : propEdges;
|
|
128
125
|
// Helper to check if there are pending changes
|
|
129
|
-
const checkHasChanges =
|
|
126
|
+
const checkHasChanges = useCallback((state) => {
|
|
130
127
|
return (state.positionChanges.size > 0 ||
|
|
131
128
|
state.dimensionChanges.size > 0 ||
|
|
132
129
|
state.nodeUpdates.size > 0 ||
|
|
@@ -135,7 +132,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
135
132
|
state.deletedEdges.length > 0);
|
|
136
133
|
}, []);
|
|
137
134
|
// Helper to update edit state and notify parent
|
|
138
|
-
const updateEditState =
|
|
135
|
+
const updateEditState = useCallback((updater) => {
|
|
139
136
|
const newState = updater(editStateRef.current);
|
|
140
137
|
editStateRef.current = newState;
|
|
141
138
|
onEditStateChange?.(newState);
|
|
@@ -145,7 +142,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
145
142
|
// EVENT HANDLERS
|
|
146
143
|
// ============================================
|
|
147
144
|
// Handle edge click (toggle selection, supports Shift for multi-select)
|
|
148
|
-
const onEdgeClick =
|
|
145
|
+
const onEdgeClick = useCallback((event, edge) => {
|
|
149
146
|
if (event.shiftKey && editable) {
|
|
150
147
|
// Shift+click: toggle edge in selection
|
|
151
148
|
setSelectedEdgeIds((prev) => {
|
|
@@ -176,7 +173,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
176
173
|
}
|
|
177
174
|
}, [editable, selectedEdgeIds]);
|
|
178
175
|
// Handle node click (toggle selection, supports Shift for multi-select)
|
|
179
|
-
const onNodeClick =
|
|
176
|
+
const onNodeClick = useCallback((event, node) => {
|
|
180
177
|
if (event.shiftKey && editable) {
|
|
181
178
|
// Shift+click: toggle node in selection
|
|
182
179
|
setSelectedNodeIds((prev) => {
|
|
@@ -207,12 +204,12 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
207
204
|
}
|
|
208
205
|
}, [editable, selectedNodeIds]);
|
|
209
206
|
// Handle close edge info panel
|
|
210
|
-
const onCloseEdgeInfoPanel =
|
|
207
|
+
const onCloseEdgeInfoPanel = useCallback(() => {
|
|
211
208
|
setSelectedEdgeIds(new Set());
|
|
212
209
|
setShowEdgePanel(false);
|
|
213
210
|
}, []);
|
|
214
211
|
// Handle edge side updates from EdgeInfoPanel
|
|
215
|
-
const handleUpdateEdgeSides =
|
|
212
|
+
const handleUpdateEdgeSides = useCallback((edgeId, fromSide, toSide) => {
|
|
216
213
|
setLocalEdges((currentEdges) => currentEdges.map((edge) => edge.id === edgeId
|
|
217
214
|
? {
|
|
218
215
|
...edge,
|
|
@@ -225,19 +222,19 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
225
222
|
: edge));
|
|
226
223
|
}, []);
|
|
227
224
|
// Handle close node info panel
|
|
228
|
-
const onCloseNodeInfoPanel =
|
|
225
|
+
const onCloseNodeInfoPanel = useCallback(() => {
|
|
229
226
|
setSelectedNodeIds(new Set());
|
|
230
227
|
setShowNodePanel(false);
|
|
231
228
|
}, []);
|
|
232
229
|
// Handle pane click (clear selection when clicking empty space)
|
|
233
|
-
const onPaneClick =
|
|
230
|
+
const onPaneClick = useCallback(() => {
|
|
234
231
|
setSelectedNodeIds(new Set());
|
|
235
232
|
setSelectedEdgeIds(new Set());
|
|
236
233
|
setShowNodePanel(false);
|
|
237
234
|
setShowEdgePanel(false);
|
|
238
235
|
}, []);
|
|
239
236
|
// Handle selection change from ReactFlow (box selection)
|
|
240
|
-
const handleSelectionChange =
|
|
237
|
+
const handleSelectionChange = useCallback(({ nodes: selectedNodes, edges: selectedEdges }) => {
|
|
241
238
|
if (editable) {
|
|
242
239
|
setSelectedNodeIds(new Set(selectedNodes.map((n) => n.id)));
|
|
243
240
|
setSelectedEdgeIds(new Set(selectedEdges.map((e) => e.id)));
|
|
@@ -251,7 +248,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
251
248
|
}
|
|
252
249
|
}, [editable]);
|
|
253
250
|
// Handle node update (internal - updates local state only)
|
|
254
|
-
const handleNodeUpdate =
|
|
251
|
+
const handleNodeUpdate = useCallback((nodeId, updates) => {
|
|
255
252
|
if (!editable)
|
|
256
253
|
return;
|
|
257
254
|
// Update local nodes
|
|
@@ -277,7 +274,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
277
274
|
});
|
|
278
275
|
}, [editable, updateEditState]);
|
|
279
276
|
// Handle node delete (internal)
|
|
280
|
-
const handleNodeDelete =
|
|
277
|
+
const handleNodeDelete = useCallback((nodeId) => {
|
|
281
278
|
if (!editable)
|
|
282
279
|
return;
|
|
283
280
|
// Remove from local state
|
|
@@ -310,7 +307,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
310
307
|
setSelectedNodeIds(new Set());
|
|
311
308
|
}, [editable, updateEditState]);
|
|
312
309
|
// Handle edge delete (internal)
|
|
313
|
-
const handleEdgeDelete =
|
|
310
|
+
const handleEdgeDelete = useCallback((edgeId) => {
|
|
314
311
|
if (!editable)
|
|
315
312
|
return;
|
|
316
313
|
// Find the edge before removing it so we can track its full info
|
|
@@ -345,7 +342,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
345
342
|
setSelectedEdgeIds(new Set());
|
|
346
343
|
}, [editable, updateEditState, localEdges]);
|
|
347
344
|
// Handle new connection from drag
|
|
348
|
-
const handleConnect =
|
|
345
|
+
const handleConnect = useCallback((connection) => {
|
|
349
346
|
if (!editable || !connection.source || !connection.target)
|
|
350
347
|
return;
|
|
351
348
|
// Find source and target node types
|
|
@@ -378,7 +375,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
378
375
|
}
|
|
379
376
|
}, [editable, nodes, configuration.allowedConnections]);
|
|
380
377
|
// Create edge helper
|
|
381
|
-
const createEdge =
|
|
378
|
+
const createEdge = useCallback((from, to, type, sourceHandle, targetHandle) => {
|
|
382
379
|
const edgeId = `${from}-${to}-${type}-${Date.now()}`;
|
|
383
380
|
// Add to local state with handle information
|
|
384
381
|
const newEdge = {
|
|
@@ -403,24 +400,24 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
403
400
|
}));
|
|
404
401
|
}, [updateEditState]);
|
|
405
402
|
// Handle edge type selection from picker
|
|
406
|
-
const handleEdgeTypeSelect =
|
|
403
|
+
const handleEdgeTypeSelect = useCallback((type) => {
|
|
407
404
|
if (!pendingConnection)
|
|
408
405
|
return;
|
|
409
406
|
createEdge(pendingConnection.from, pendingConnection.to, type, pendingConnection.sourceHandle, pendingConnection.targetHandle);
|
|
410
407
|
setPendingConnection(null);
|
|
411
408
|
}, [pendingConnection, createEdge]);
|
|
412
409
|
// Cancel edge type picker
|
|
413
|
-
const handleCancelEdgeTypePicker =
|
|
410
|
+
const handleCancelEdgeTypePicker = useCallback(() => {
|
|
414
411
|
setPendingConnection(null);
|
|
415
412
|
}, []);
|
|
416
413
|
// Track whether reconnection succeeded
|
|
417
|
-
const edgeReconnectSuccessful =
|
|
414
|
+
const edgeReconnectSuccessful = useRef(true);
|
|
418
415
|
// Called when user starts dragging an edge endpoint
|
|
419
|
-
const handleReconnectStart =
|
|
416
|
+
const handleReconnectStart = useCallback(() => {
|
|
420
417
|
edgeReconnectSuccessful.current = false;
|
|
421
418
|
}, []);
|
|
422
419
|
// Handle edge reconnection (dragging edge endpoint to new node)
|
|
423
|
-
const handleReconnect =
|
|
420
|
+
const handleReconnect = useCallback((oldEdge, newConnection) => {
|
|
424
421
|
if (!editable || !newConnection.source || !newConnection.target)
|
|
425
422
|
return;
|
|
426
423
|
// Find the original edge in our local state
|
|
@@ -495,7 +492,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
495
492
|
});
|
|
496
493
|
}, [editable, localEdges, nodes, configuration.allowedConnections, updateEditState]);
|
|
497
494
|
// Called when reconnection ends (whether successful or not)
|
|
498
|
-
const handleReconnectEnd =
|
|
495
|
+
const handleReconnectEnd = useCallback(() => {
|
|
499
496
|
// If reconnection wasn't successful, the edge was dropped in empty space
|
|
500
497
|
// We need to keep the original edge (do nothing, it's still in localEdges)
|
|
501
498
|
// Edge is still in localEdges, no action needed - ReactFlow will re-render with it
|
|
@@ -505,33 +502,33 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
505
502
|
// SELECTED ITEMS
|
|
506
503
|
// ============================================
|
|
507
504
|
// Get first selected edge (for single-selection info panel)
|
|
508
|
-
const selectedEdgeId =
|
|
505
|
+
const selectedEdgeId = useMemo(() => {
|
|
509
506
|
if (selectedEdgeIds.size === 0)
|
|
510
507
|
return null;
|
|
511
508
|
return selectedEdgeIds.values().next().value;
|
|
512
509
|
}, [selectedEdgeIds]);
|
|
513
|
-
const selectedEdge =
|
|
510
|
+
const selectedEdge = useMemo(() => {
|
|
514
511
|
if (!selectedEdgeId)
|
|
515
512
|
return null;
|
|
516
513
|
return edges.find((e) => e.id === selectedEdgeId);
|
|
517
514
|
}, [selectedEdgeId, edges]);
|
|
518
|
-
const selectedEdgeTypeDefinition =
|
|
515
|
+
const selectedEdgeTypeDefinition = useMemo(() => {
|
|
519
516
|
if (!selectedEdge)
|
|
520
517
|
return null;
|
|
521
518
|
return configuration.edgeTypes[selectedEdge.type];
|
|
522
519
|
}, [selectedEdge, configuration.edgeTypes]);
|
|
523
520
|
// Get first selected node (for single-selection info panel)
|
|
524
|
-
const selectedNodeId =
|
|
521
|
+
const selectedNodeId = useMemo(() => {
|
|
525
522
|
if (selectedNodeIds.size === 0)
|
|
526
523
|
return null;
|
|
527
524
|
return selectedNodeIds.values().next().value;
|
|
528
525
|
}, [selectedNodeIds]);
|
|
529
|
-
const selectedNode =
|
|
526
|
+
const selectedNode = useMemo(() => {
|
|
530
527
|
if (!selectedNodeId)
|
|
531
528
|
return null;
|
|
532
529
|
return nodes.find((n) => n.id === selectedNodeId);
|
|
533
530
|
}, [selectedNodeId, nodes]);
|
|
534
|
-
const selectedNodeTypeDefinition =
|
|
531
|
+
const selectedNodeTypeDefinition = useMemo(() => {
|
|
535
532
|
if (!selectedNode)
|
|
536
533
|
return null;
|
|
537
534
|
return configuration.nodeTypes[selectedNode.type];
|
|
@@ -539,7 +536,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
539
536
|
// ============================================
|
|
540
537
|
// ANIMATIONS
|
|
541
538
|
// ============================================
|
|
542
|
-
|
|
539
|
+
useEffect(() => {
|
|
543
540
|
if (events.length === 0)
|
|
544
541
|
return;
|
|
545
542
|
const latestEvent = events[events.length - 1];
|
|
@@ -634,8 +631,8 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
634
631
|
// ============================================
|
|
635
632
|
// XYFLOW CONVERSION
|
|
636
633
|
// ============================================
|
|
637
|
-
const xyflowNodesBase =
|
|
638
|
-
const converted =
|
|
634
|
+
const xyflowNodesBase = useMemo(() => {
|
|
635
|
+
const converted = convertToXYFlowNodes(localNodes, configuration, violations);
|
|
639
636
|
return converted.map((node) => {
|
|
640
637
|
const animation = animationState.nodeAnimations[node.id];
|
|
641
638
|
// Apply any pending position changes
|
|
@@ -658,31 +655,31 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
658
655
|
};
|
|
659
656
|
});
|
|
660
657
|
}, [localNodes, configuration, violations, animationState.nodeAnimations, editable, showTooltips, highlightedNodeId, editStateRef]);
|
|
661
|
-
const baseNodesKey =
|
|
658
|
+
const baseNodesKey = useMemo(() => {
|
|
662
659
|
return nodes
|
|
663
660
|
.map((n) => n.id)
|
|
664
661
|
.sort()
|
|
665
662
|
.join(',');
|
|
666
663
|
}, [nodes]);
|
|
667
|
-
const baseEdgesKey =
|
|
664
|
+
const baseEdgesKey = useMemo(() => {
|
|
668
665
|
return edges
|
|
669
666
|
.map((e) => e.id)
|
|
670
667
|
.sort()
|
|
671
668
|
.join(',');
|
|
672
669
|
}, [edges]);
|
|
673
670
|
// Local xyflow nodes state for dragging
|
|
674
|
-
const [xyflowLocalNodes, setXyflowLocalNodes] =
|
|
671
|
+
const [xyflowLocalNodes, setXyflowLocalNodes] = useState(xyflowNodesBase);
|
|
675
672
|
// Sync when base changes
|
|
676
|
-
const prevBaseNodesKeyRef =
|
|
677
|
-
|
|
673
|
+
const prevBaseNodesKeyRef = useRef(baseNodesKey);
|
|
674
|
+
useEffect(() => {
|
|
678
675
|
if (prevBaseNodesKeyRef.current !== baseNodesKey) {
|
|
679
676
|
prevBaseNodesKeyRef.current = baseNodesKey;
|
|
680
677
|
setXyflowLocalNodes(xyflowNodesBase);
|
|
681
678
|
}
|
|
682
679
|
}, [baseNodesKey, xyflowNodesBase]);
|
|
683
680
|
// Also sync when entering edit mode or when base nodes change content
|
|
684
|
-
const prevEditableRef =
|
|
685
|
-
|
|
681
|
+
const prevEditableRef = useRef(editable);
|
|
682
|
+
useEffect(() => {
|
|
686
683
|
if (editable && !prevEditableRef.current) {
|
|
687
684
|
// Entering edit mode - sync positions
|
|
688
685
|
setXyflowLocalNodes(xyflowNodesBase);
|
|
@@ -691,10 +688,10 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
691
688
|
}, [editable, xyflowNodesBase]);
|
|
692
689
|
const xyflowNodes = editable ? xyflowLocalNodes : xyflowNodesBase;
|
|
693
690
|
// Handle node changes (drag and resize events)
|
|
694
|
-
const handleNodesChange =
|
|
691
|
+
const handleNodesChange = useCallback((changes) => {
|
|
695
692
|
if (!editable)
|
|
696
693
|
return;
|
|
697
|
-
setXyflowLocalNodes((nds) =>
|
|
694
|
+
setXyflowLocalNodes((nds) => applyNodeChanges(changes, nds));
|
|
698
695
|
// Check if dragging started - hide panel when dragging starts
|
|
699
696
|
const hasDragging = changes.some((change) => change.type === 'position' &&
|
|
700
697
|
'dragging' in change &&
|
|
@@ -742,8 +739,8 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
742
739
|
});
|
|
743
740
|
}
|
|
744
741
|
}, [editable, updateEditState]);
|
|
745
|
-
const xyflowEdges =
|
|
746
|
-
const converted =
|
|
742
|
+
const xyflowEdges = useMemo(() => {
|
|
743
|
+
const converted = convertToXYFlowEdges(edges, configuration, violations);
|
|
747
744
|
// Debug: Log edge counts to help diagnose disappearing edges
|
|
748
745
|
if (process.env.NODE_ENV === 'development') {
|
|
749
746
|
console.log('[GraphRenderer] xyflowEdges computed:', {
|
|
@@ -786,7 +783,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
786
783
|
});
|
|
787
784
|
}, [edges, configuration, violations, animationState.edgeAnimations, showTooltips, selectedEdgeIds]);
|
|
788
785
|
// Fit view on mount and structure changes
|
|
789
|
-
|
|
786
|
+
useEffect(() => {
|
|
790
787
|
const timeoutId = setTimeout(() => {
|
|
791
788
|
fitView({
|
|
792
789
|
padding: 0.2,
|
|
@@ -801,14 +798,14 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
801
798
|
// ============================================
|
|
802
799
|
// RENDER
|
|
803
800
|
// ============================================
|
|
804
|
-
return ((
|
|
805
|
-
?
|
|
801
|
+
return (_jsxs(_Fragment, { children: [_jsxs(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: true, nodesConnectable: editable, edgesReconnectable: editable, reconnectRadius: 25, elevateEdgesOnSelect: true, onNodesChange: handleNodesChange, onConnect: handleConnect, onReconnectStart: handleReconnectStart, onReconnect: handleReconnect, onReconnectEnd: handleReconnectEnd, onPaneClick: onPaneClick, onSelectionChange: handleSelectionChange, panOnDrag: true, selectionOnDrag: false, selectionKeyCode: editable ? 'Shift' : null, multiSelectionKeyCode: "Shift", children: [showBackground && (_jsx(Background, { color: backgroundVariant === 'dots' ? theme.colors.border : theme.colors.textMuted, gap: backgroundGap ?? (backgroundVariant === 'dots' ? 16 : 50), size: backgroundVariant === 'dots' ? 1 : 0.5, variant: backgroundVariant === 'dots'
|
|
802
|
+
? BackgroundVariant.Dots
|
|
806
803
|
: backgroundVariant === 'lines'
|
|
807
|
-
?
|
|
808
|
-
:
|
|
804
|
+
? BackgroundVariant.Lines
|
|
805
|
+
: BackgroundVariant.Cross })), showControls && _jsx(Controls, { showZoom: true, showFitView: true, showInteractive: true }), showMinimap && (_jsx(MiniMap, { nodeColor: (node) => {
|
|
809
806
|
const nodeData = node.data;
|
|
810
807
|
return nodeData?.typeDefinition?.color || theme.colors.secondary;
|
|
811
|
-
}, nodeBorderRadius: 2, pannable: true, zoomable: true })), showCenterIndicator && (
|
|
808
|
+
}, nodeBorderRadius: 2, pannable: true, zoomable: true })), showCenterIndicator && _jsx(CenterIndicator, { color: theme.colors.textMuted })] }, `${baseNodesKey}-${baseEdgesKey}`), selectedNodeIds.size >= 2 && showNodePanel && (_jsx(SelectionSidebar, { selectedNodeIds: selectedNodeIds, nodes: nodes, nodeTypeDefinitions: configuration.nodeTypes, onClose: onCloseNodeInfoPanel })), selectedEdgeIds.size === 1 && selectedEdge && selectedEdgeTypeDefinition && showEdgePanel && (_jsx(EdgeInfoPanel, { edge: selectedEdge, typeDefinition: selectedEdgeTypeDefinition, sourceNodeId: selectedEdge.from, targetNodeId: selectedEdge.to, onClose: onCloseEdgeInfoPanel, onDelete: editable ? handleEdgeDelete : undefined, onUpdateSides: editable ? handleUpdateEdgeSides : undefined })), selectedNodeIds.size === 1 && selectedNode && selectedNodeTypeDefinition && showNodePanel && (_jsx(NodeInfoPanel, { node: selectedNode, typeDefinition: selectedNodeTypeDefinition, availableNodeTypes: configuration.nodeTypes, onClose: onCloseNodeInfoPanel, onDelete: editable ? handleNodeDelete : undefined, onUpdate: editable ? handleNodeUpdate : undefined, onSourceClick: onSourceClick })), pendingConnection && (_jsxs("div", { style: {
|
|
812
809
|
position: 'absolute',
|
|
813
810
|
top: '50%',
|
|
814
811
|
left: '50%',
|
|
@@ -821,9 +818,9 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
821
818
|
minWidth: '200px',
|
|
822
819
|
zIndex: 1000,
|
|
823
820
|
border: `1px solid ${theme.colors.border}`,
|
|
824
|
-
}, children: [(
|
|
821
|
+
}, children: [_jsx("div", { style: { fontWeight: 'bold', marginBottom: '12px', fontSize: '14px' }, children: "Select Edge Type" }), _jsxs("div", { style: { fontSize: '12px', color: theme.colors.textSecondary, marginBottom: '12px' }, children: [pendingConnection.from, " \u2192 ", pendingConnection.to] }), _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '8px' }, children: pendingConnection.validTypes.map((type) => {
|
|
825
822
|
const typeDefinition = configuration.edgeTypes[type];
|
|
826
|
-
return ((
|
|
823
|
+
return (_jsx("button", { onClick: () => handleEdgeTypeSelect(type), style: {
|
|
827
824
|
padding: '8px 12px',
|
|
828
825
|
backgroundColor: typeDefinition?.color || theme.colors.secondary,
|
|
829
826
|
color: theme.colors.background,
|
|
@@ -834,7 +831,7 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
834
831
|
fontWeight: 'bold',
|
|
835
832
|
textAlign: 'left',
|
|
836
833
|
}, children: type }, type));
|
|
837
|
-
}) }), (
|
|
834
|
+
}) }), _jsx("button", { onClick: handleCancelEdgeTypePicker, style: {
|
|
838
835
|
marginTop: '12px',
|
|
839
836
|
width: '100%',
|
|
840
837
|
padding: '8px 12px',
|
|
@@ -850,10 +847,10 @@ const GraphRendererInner = ({ configuration, nodes: propNodes, edges: propEdges,
|
|
|
850
847
|
* Convert canvas to legacy configuration format for internal use
|
|
851
848
|
*/
|
|
852
849
|
function useCanvasToLegacy(canvas, library) {
|
|
853
|
-
return
|
|
850
|
+
return useMemo(() => {
|
|
854
851
|
if (!canvas)
|
|
855
852
|
return null;
|
|
856
|
-
const { nodes, edges } =
|
|
853
|
+
const { nodes, edges } = CanvasConverter.canvasToGraph(canvas);
|
|
857
854
|
// Build GraphConfiguration from canvas
|
|
858
855
|
const nodeTypes = {};
|
|
859
856
|
const edgeTypes = {};
|
|
@@ -1007,9 +1004,9 @@ function useCanvasToLegacy(canvas, library) {
|
|
|
1007
1004
|
* />
|
|
1008
1005
|
* ```
|
|
1009
1006
|
*/
|
|
1010
|
-
|
|
1007
|
+
export const GraphRenderer = forwardRef((props, ref) => {
|
|
1011
1008
|
const { canvas, library, className, width = '100%', height = '100%' } = props;
|
|
1012
|
-
const { theme } =
|
|
1009
|
+
const { theme } = useTheme();
|
|
1013
1010
|
// Convert canvas to internal format (merging library types if provided)
|
|
1014
1011
|
const canvasData = useCanvasToLegacy(canvas, library);
|
|
1015
1012
|
// Debug: Log canvas data to help diagnose disappearing edges
|
|
@@ -1023,9 +1020,9 @@ exports.GraphRenderer = (0, react_1.forwardRef)((props, ref) => {
|
|
|
1023
1020
|
});
|
|
1024
1021
|
}
|
|
1025
1022
|
// Internal edit state ref - must be before any conditional returns
|
|
1026
|
-
const editStateRef =
|
|
1023
|
+
const editStateRef = useRef(createEmptyEditState());
|
|
1027
1024
|
// Expose imperative handle - must be before any conditional returns
|
|
1028
|
-
|
|
1025
|
+
useImperativeHandle(ref, () => ({
|
|
1029
1026
|
getPendingChanges: () => {
|
|
1030
1027
|
const state = editStateRef.current;
|
|
1031
1028
|
return {
|
|
@@ -1073,7 +1070,7 @@ exports.GraphRenderer = (0, react_1.forwardRef)((props, ref) => {
|
|
|
1073
1070
|
}), []);
|
|
1074
1071
|
// Validate we have required data
|
|
1075
1072
|
if (!canvasData) {
|
|
1076
|
-
return ((
|
|
1073
|
+
return (_jsx("div", { className: className, style: {
|
|
1077
1074
|
width,
|
|
1078
1075
|
height,
|
|
1079
1076
|
display: 'flex',
|
|
@@ -1081,12 +1078,12 @@ exports.GraphRenderer = (0, react_1.forwardRef)((props, ref) => {
|
|
|
1081
1078
|
justifyContent: 'center',
|
|
1082
1079
|
backgroundColor: theme.colors.background,
|
|
1083
1080
|
color: theme.colors.textSecondary,
|
|
1084
|
-
}, children: (
|
|
1081
|
+
}, children: _jsx("p", { children: "No canvas data provided." }) }));
|
|
1085
1082
|
}
|
|
1086
1083
|
const { configuration, nodes, edges } = canvasData;
|
|
1087
1084
|
// Extract only the props that inner component needs
|
|
1088
1085
|
const { violations, configName, showMinimap, showControls, showBackground, backgroundVariant, backgroundGap, showCenterIndicator, showTooltips, fitViewDuration, highlightedNodeId, events, onEventProcessed, editable, onPendingChangesChange, onSourceClick, } = props;
|
|
1089
|
-
return ((
|
|
1086
|
+
return (_jsx("div", { className: className, style: { width, height, position: 'relative' }, children: _jsx(ReactFlowProvider, { children: _jsx(GraphRendererInner, { configuration: configuration, nodes: nodes, edges: edges, violations: violations, configName: configName, showMinimap: showMinimap, showControls: showControls, showBackground: showBackground, backgroundVariant: backgroundVariant, backgroundGap: backgroundGap, showCenterIndicator: showCenterIndicator, showTooltips: showTooltips, fitViewDuration: fitViewDuration, highlightedNodeId: highlightedNodeId, events: events, onEventProcessed: onEventProcessed, editable: editable, onPendingChangesChange: onPendingChangesChange, editStateRef: editStateRef, onSourceClick: onSourceClick }) }) }));
|
|
1090
1087
|
});
|
|
1091
|
-
|
|
1088
|
+
GraphRenderer.displayName = 'GraphRenderer';
|
|
1092
1089
|
//# sourceMappingURL=GraphRenderer.js.map
|