@principal-ai/principal-view-react 0.6.11 → 0.6.13

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.
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.convertToXYFlowNodes = convertToXYFlowNodes;
4
4
  exports.convertToXYFlowEdges = convertToXYFlowEdges;
5
5
  exports.autoLayoutNodes = autoLayoutNodes;
6
+ exports.hasCycleBetweenNodes = hasCycleBetweenNodes;
7
+ exports.computeOptimalEdgeSides = computeOptimalEdgeSides;
6
8
  const react_1 = require("@xyflow/react");
7
9
  /**
8
10
  * Convert our NodeState to xyflow Node format
@@ -33,6 +35,22 @@ function convertToXYFlowNodes(nodes, configuration, violations = []) {
33
35
  };
34
36
  });
35
37
  }
38
+ /**
39
+ * Map canvas side to source handle ID
40
+ * Source handles use '-out' suffix
41
+ */
42
+ function sideToSourceHandle(side) {
43
+ if (!side)
44
+ return undefined;
45
+ return `${side}-out`;
46
+ }
47
+ /**
48
+ * Map canvas side to target handle ID
49
+ * Target handles use the side name directly
50
+ */
51
+ function sideToTargetHandle(side) {
52
+ return side;
53
+ }
36
54
  /**
37
55
  * Convert our EdgeState to xyflow Edge format
38
56
  */
@@ -45,6 +63,11 @@ function convertToXYFlowEdges(edges, configuration, violations = []) {
45
63
  }
46
64
  const hasViolations = violations.some((v) => v.context?.edgeId === edge.id);
47
65
  const edgeWithHandles = edge;
66
+ // Get handle IDs from edge data (fromSide/toSide) or explicit handles
67
+ const fromSide = edge.data?.fromSide;
68
+ const toSide = edge.data?.toSide;
69
+ const sourceHandle = edgeWithHandles.sourceHandle || sideToSourceHandle(fromSide);
70
+ const targetHandle = edgeWithHandles.targetHandle || sideToTargetHandle(toSide);
48
71
  // Add arrow marker if edge type is directed
49
72
  // Color priority: edge data color > type definition color > default
50
73
  const edgeColor = edge.data?.color;
@@ -60,8 +83,8 @@ function convertToXYFlowEdges(edges, configuration, violations = []) {
60
83
  id: edge.id,
61
84
  source: edge.from,
62
85
  target: edge.to,
63
- sourceHandle: edgeWithHandles.sourceHandle,
64
- targetHandle: edgeWithHandles.targetHandle,
86
+ sourceHandle,
87
+ targetHandle,
65
88
  type: 'custom',
66
89
  animated: typeDefinition?.style === 'animated',
67
90
  markerEnd,
@@ -69,6 +92,7 @@ function convertToXYFlowEdges(edges, configuration, violations = []) {
69
92
  typeDefinition,
70
93
  hasViolations,
71
94
  data: edge.data,
95
+ edgeType: edge.type,
72
96
  },
73
97
  };
74
98
  });
@@ -181,4 +205,63 @@ function applyCircularLayout(nodes) {
181
205
  };
182
206
  });
183
207
  }
208
+ /**
209
+ * Check if there is a cycle between two nodes (i.e., a path from target back to source).
210
+ * Returns true if adding an edge from `from` to `to` would complete a cycle.
211
+ */
212
+ function hasCycleBetweenNodes(from, to, edges) {
213
+ // Check if there's already a path from `to` back to `from`
214
+ // using BFS/DFS from `to` node
215
+ const visited = new Set();
216
+ const queue = [to];
217
+ while (queue.length > 0) {
218
+ const current = queue.shift();
219
+ if (current === from) {
220
+ return true; // Found a path back, there would be a cycle
221
+ }
222
+ if (visited.has(current)) {
223
+ continue;
224
+ }
225
+ visited.add(current);
226
+ // Find all outgoing edges from current node
227
+ for (const edge of edges) {
228
+ if (edge.from === current && !visited.has(edge.to)) {
229
+ queue.push(edge.to);
230
+ }
231
+ }
232
+ }
233
+ return false;
234
+ }
235
+ /**
236
+ * Compute optimal edge sides based on relative node positions.
237
+ * Returns the best fromSide and toSide for connecting two nodes.
238
+ */
239
+ function computeOptimalEdgeSides(fromPosition, toPosition) {
240
+ const dx = toPosition.x - fromPosition.x;
241
+ const dy = toPosition.y - fromPosition.y;
242
+ // Determine primary direction based on which axis has larger delta
243
+ const isHorizontalDominant = Math.abs(dx) > Math.abs(dy);
244
+ if (isHorizontalDominant) {
245
+ // Horizontal connection
246
+ if (dx > 0) {
247
+ // Target is to the right of source
248
+ return { fromSide: 'right', toSide: 'left' };
249
+ }
250
+ else {
251
+ // Target is to the left of source
252
+ return { fromSide: 'left', toSide: 'right' };
253
+ }
254
+ }
255
+ else {
256
+ // Vertical connection
257
+ if (dy > 0) {
258
+ // Target is below source
259
+ return { fromSide: 'bottom', toSide: 'top' };
260
+ }
261
+ else {
262
+ // Target is above source
263
+ return { fromSide: 'top', toSide: 'bottom' };
264
+ }
265
+ }
266
+ }
184
267
  //# sourceMappingURL=graphConverter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"graphConverter.js","sourceRoot":"","sources":["../../src/utils/graphConverter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graphConverter.js","sourceRoot":"","sources":["../../src/utils/graphConverter.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/principal-view-react",
3
- "version": "0.6.11",
3
+ "version": "0.6.13",
4
4
  "description": "React components for graph-based principal view framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -40,6 +40,8 @@ import {
40
40
  convertToXYFlowNodes,
41
41
  convertToXYFlowEdges,
42
42
  autoLayoutNodes,
43
+ hasCycleBetweenNodes,
44
+ computeOptimalEdgeSides,
43
45
  } from '../utils/graphConverter';
44
46
  import { EdgeInfoPanel } from './EdgeInfoPanel';
45
47
  import { NodeInfoPanel } from './NodeInfoPanel';
@@ -90,6 +92,13 @@ interface GraphRendererBaseProps {
90
92
  /** Optional violations to highlight */
91
93
  violations?: Violation[];
92
94
 
95
+ /**
96
+ * Whether to automatically update edge sides (fromSide/toSide) when nodes are moved.
97
+ * Only updates edges where there is no cycle between the connected nodes.
98
+ * Uses position-based logic to determine optimal connection sides.
99
+ */
100
+ autoUpdateEdgeSides?: boolean;
101
+
93
102
  /** Optional configuration name for identification (used with multi-config setups) */
94
103
  configName?: string;
95
104
 
@@ -209,6 +218,7 @@ interface GraphRendererInnerProps {
209
218
  events?: GraphEvent[];
210
219
  onEventProcessed?: (event: GraphEvent) => void;
211
220
  editable?: boolean;
221
+ autoUpdateEdgeSides?: boolean;
212
222
  onPendingChangesChange?: (hasChanges: boolean) => void;
213
223
  onEditStateChange?: (editState: EditState) => void;
214
224
  editStateRef: React.MutableRefObject<EditState>;
@@ -229,6 +239,7 @@ const GraphRendererInner: React.FC<GraphRendererInnerProps> = ({
229
239
  events = [],
230
240
  onEventProcessed,
231
241
  editable = false,
242
+ autoUpdateEdgeSides = false,
232
243
  onPendingChangesChange,
233
244
  onEditStateChange,
234
245
  editStateRef,
@@ -909,9 +920,62 @@ const GraphRendererInner: React.FC<GraphRendererInnerProps> = ({
909
920
  }
910
921
  return { ...prev, positionChanges: newPositions };
911
922
  });
923
+
924
+ // Auto-update edge sides if enabled
925
+ if (autoUpdateEdgeSides) {
926
+ setXyflowLocalNodes((currentNodes) => {
927
+ // Build a position map from current xyflow nodes
928
+ const nodePositions = new Map<string, { x: number; y: number }>();
929
+ for (const node of currentNodes) {
930
+ nodePositions.set(node.id, node.position);
931
+ }
932
+
933
+ // Get moved node IDs
934
+ const movedNodeIds = new Set(positionChanges.map((c) => c.id));
935
+
936
+ // Update edges connected to moved nodes
937
+ setLocalEdges((currentEdges) => {
938
+ return currentEdges.map((edge) => {
939
+ // Only process edges connected to moved nodes
940
+ if (!movedNodeIds.has(edge.from) && !movedNodeIds.has(edge.to)) {
941
+ return edge;
942
+ }
943
+
944
+ // Check for cycles - skip if there's a cycle between these nodes
945
+ const edgesWithoutCurrent = currentEdges.filter((e) => e.id !== edge.id);
946
+ if (hasCycleBetweenNodes(edge.from, edge.to, edgesWithoutCurrent)) {
947
+ return edge; // Don't auto-update edges that are part of a cycle
948
+ }
949
+
950
+ // Get positions of both nodes
951
+ const fromPos = nodePositions.get(edge.from);
952
+ const toPos = nodePositions.get(edge.to);
953
+ if (!fromPos || !toPos) {
954
+ return edge;
955
+ }
956
+
957
+ // Compute optimal sides
958
+ const { fromSide, toSide } = computeOptimalEdgeSides(fromPos, toPos);
959
+
960
+ // Update edge data with new sides
961
+ return {
962
+ ...edge,
963
+ data: {
964
+ ...edge.data,
965
+ fromSide,
966
+ toSide,
967
+ },
968
+ updatedAt: Date.now(),
969
+ };
970
+ });
971
+ });
972
+
973
+ return currentNodes;
974
+ });
975
+ }
912
976
  }
913
977
  },
914
- [editable, updateEditState]
978
+ [editable, autoUpdateEdgeSides, updateEditState]
915
979
  );
916
980
 
917
981
  const xyflowEdges = useMemo(() => {
@@ -1364,6 +1428,7 @@ export const GraphRenderer = forwardRef<GraphRendererHandle, GraphRendererProps>
1364
1428
  events,
1365
1429
  onEventProcessed,
1366
1430
  editable,
1431
+ autoUpdateEdgeSides,
1367
1432
  onPendingChangesChange,
1368
1433
  } = props;
1369
1434
 
@@ -1382,6 +1447,7 @@ export const GraphRenderer = forwardRef<GraphRendererHandle, GraphRendererProps>
1382
1447
  events={events}
1383
1448
  onEventProcessed={onEventProcessed}
1384
1449
  editable={editable}
1450
+ autoUpdateEdgeSides={autoUpdateEdgeSides}
1385
1451
  onPendingChangesChange={onPendingChangesChange}
1386
1452
  editStateRef={editStateRef}
1387
1453
  />
@@ -7,6 +7,7 @@ export interface CustomEdgeData extends Record<string, unknown> {
7
7
  typeDefinition: EdgeTypeDefinition;
8
8
  hasViolations?: boolean;
9
9
  data?: Record<string, unknown>;
10
+ edgeType?: string;
10
11
  // Animation control
11
12
  animationType?: 'flow' | 'particle' | 'pulse' | 'glow' | null;
12
13
  animationDuration?: number;
@@ -34,12 +35,14 @@ export const CustomEdge: React.FC<EdgeProps<any>> = ({
34
35
  typeDefinition,
35
36
  hasViolations,
36
37
  data: edgeData,
38
+ edgeType,
37
39
  animationType,
38
40
  animationDuration = 1000,
39
41
  animationDirection = 'forward',
40
42
  } = edgeProps || ({} as CustomEdgeData);
41
43
 
42
44
  const [particlePosition, setParticlePosition] = useState(0);
45
+ const [isHovered, setIsHovered] = useState(false);
43
46
  const pathRef = useRef<SVGPathElement>(null);
44
47
 
45
48
  // Particle animation effect
@@ -147,7 +150,11 @@ export const CustomEdge: React.FC<EdgeProps<any>> = ({
147
150
  const particlePos = animationType === 'particle' ? getParticleTransform() : null;
148
151
 
149
152
  return (
150
- <>
153
+ <g
154
+ onMouseEnter={() => setIsHovered(true)}
155
+ onMouseLeave={() => setIsHovered(false)}
156
+ style={{ cursor: 'pointer' }}
157
+ >
151
158
  {/* Hidden path for particle position calculation */}
152
159
  <path
153
160
  ref={pathRef}
@@ -164,7 +171,7 @@ export const CustomEdge: React.FC<EdgeProps<any>> = ({
164
171
  fill="none"
165
172
  stroke="transparent"
166
173
  strokeWidth={Math.max(width + 10, 20)}
167
- style={{ cursor: 'pointer' }}
174
+ style={{ pointerEvents: 'stroke' }}
168
175
  />
169
176
 
170
177
  <BaseEdge
@@ -275,6 +282,30 @@ export const CustomEdge: React.FC<EdgeProps<any>> = ({
275
282
  </EdgeLabelRenderer>
276
283
  )}
277
284
 
285
+ {/* Hover tooltip showing edge type */}
286
+ {isHovered && (
287
+ <EdgeLabelRenderer>
288
+ <div
289
+ style={{
290
+ position: 'absolute',
291
+ transform: `translate(-50%, -100%) translate(${labelX}px,${labelY - 12}px)`,
292
+ backgroundColor: 'rgba(0, 0, 0, 0.85)',
293
+ color: 'white',
294
+ padding: '4px 8px',
295
+ borderRadius: '4px',
296
+ fontSize: '11px',
297
+ fontWeight: 500,
298
+ whiteSpace: 'nowrap',
299
+ pointerEvents: 'none',
300
+ zIndex: 1000,
301
+ boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
302
+ }}
303
+ >
304
+ {edgeType || 'edge'}
305
+ </div>
306
+ </EdgeLabelRenderer>
307
+ )}
308
+
278
309
  {/* CSS animations for all edge animation types */}
279
310
  <style>{`
280
311
  /* Flow animation - forward direction */
@@ -343,6 +374,6 @@ export const CustomEdge: React.FC<EdgeProps<any>> = ({
343
374
  }
344
375
  }
345
376
  `}</style>
346
- </>
377
+ </g>
347
378
  );
348
379
  };
package/src/index.ts CHANGED
@@ -70,7 +70,9 @@ export {
70
70
  convertToXYFlowNodes,
71
71
  convertToXYFlowEdges,
72
72
  autoLayoutNodes,
73
+ hasCycleBetweenNodes,
74
+ computeOptimalEdgeSides,
73
75
  } from './utils/graphConverter';
74
- export type { EdgeStateWithHandles } from './utils/graphConverter';
76
+ export type { EdgeStateWithHandles, CanvasSide } from './utils/graphConverter';
75
77
  export { Icon, resolveIcon } from './utils/iconResolver';
76
78
  export type { IconProps } from './utils/iconResolver';
@@ -245,8 +245,9 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
245
245
 
246
246
  return (
247
247
  <>
248
- {/* Input handles - multiple connection points */}
248
+ {/* Input handles - all 4 sides for incoming connections */}
249
249
  <Handle type="target" position={Position.Top} id="top" style={getHandleStyle('top')} />
250
+ <Handle type="target" position={Position.Bottom} id="bottom" style={getHandleStyle('bottom')} />
250
251
  <Handle type="target" position={Position.Left} id="left" style={getHandleStyle('left')} />
251
252
  <Handle type="target" position={Position.Right} id="right" style={getHandleStyle('right')} />
252
253
 
@@ -349,11 +350,12 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
349
350
  </div>
350
351
  )}
351
352
 
352
- {/* Output handles - multiple connection points */}
353
+ {/* Output handles - all 4 sides for outgoing connections */}
354
+ <Handle type="source" position={Position.Top} id="top-out" style={getHandleStyle('top')} />
353
355
  <Handle
354
356
  type="source"
355
357
  position={Position.Bottom}
356
- id="bottom"
358
+ id="bottom-out"
357
359
  style={getHandleStyle('bottom')}
358
360
  />
359
361
  <Handle type="source" position={Position.Left} id="left-out" style={getHandleStyle('left')} />