@principal-ai/principal-view-react 0.6.10 → 0.6.12

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.
Files changed (66) hide show
  1. package/README.md +2 -5
  2. package/dist/components/ConfigurationSelector.js +4 -2
  3. package/dist/components/ConfigurationSelector.js.map +1 -1
  4. package/dist/components/EdgeInfoPanel.d.ts.map +1 -1
  5. package/dist/components/EdgeInfoPanel.js +43 -13
  6. package/dist/components/EdgeInfoPanel.js.map +1 -1
  7. package/dist/components/GraphRenderer.d.ts.map +1 -1
  8. package/dist/components/GraphRenderer.js +133 -83
  9. package/dist/components/GraphRenderer.js.map +1 -1
  10. package/dist/components/NodeInfoPanel.d.ts.map +1 -1
  11. package/dist/components/NodeInfoPanel.js +143 -45
  12. package/dist/components/NodeInfoPanel.js.map +1 -1
  13. package/dist/edges/CustomEdge.d.ts +1 -0
  14. package/dist/edges/CustomEdge.d.ts.map +1 -1
  15. package/dist/edges/CustomEdge.js +18 -4
  16. package/dist/edges/CustomEdge.js.map +1 -1
  17. package/dist/edges/GenericEdge.d.ts.map +1 -1
  18. package/dist/edges/GenericEdge.js +2 -2
  19. package/dist/edges/GenericEdge.js.map +1 -1
  20. package/dist/hooks/usePathBasedEvents.d.ts +1 -1
  21. package/dist/hooks/usePathBasedEvents.d.ts.map +1 -1
  22. package/dist/hooks/usePathBasedEvents.js +9 -9
  23. package/dist/hooks/usePathBasedEvents.js.map +1 -1
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/nodes/CustomNode.d.ts.map +1 -1
  28. package/dist/nodes/CustomNode.js +62 -45
  29. package/dist/nodes/CustomNode.js.map +1 -1
  30. package/dist/nodes/GenericNode.d.ts.map +1 -1
  31. package/dist/nodes/GenericNode.js.map +1 -1
  32. package/dist/utils/animationMapping.d.ts.map +1 -1
  33. package/dist/utils/animationMapping.js +12 -12
  34. package/dist/utils/animationMapping.js.map +1 -1
  35. package/dist/utils/graphConverter.d.ts.map +1 -1
  36. package/dist/utils/graphConverter.js +47 -19
  37. package/dist/utils/graphConverter.js.map +1 -1
  38. package/dist/utils/iconResolver.d.ts.map +1 -1
  39. package/dist/utils/iconResolver.js +1 -1
  40. package/dist/utils/iconResolver.js.map +1 -1
  41. package/package.json +2 -1
  42. package/src/components/ConfigurationSelector.tsx +5 -5
  43. package/src/components/EdgeInfoPanel.tsx +79 -37
  44. package/src/components/GraphRenderer.tsx +526 -365
  45. package/src/components/NodeInfoPanel.tsx +209 -86
  46. package/src/edges/CustomEdge.tsx +40 -7
  47. package/src/edges/GenericEdge.tsx +2 -6
  48. package/src/hooks/usePathBasedEvents.ts +54 -45
  49. package/src/index.ts +11 -2
  50. package/src/nodes/CustomNode.tsx +137 -109
  51. package/src/nodes/GenericNode.tsx +4 -3
  52. package/src/stories/AnimationWorkshop.stories.tsx +131 -12
  53. package/src/stories/CanvasEdgeTypes.stories.tsx +980 -0
  54. package/src/stories/CanvasNodeTypes.stories.tsx +898 -0
  55. package/src/stories/ColorPriority.stories.tsx +20 -10
  56. package/src/stories/EventDrivenAnimations.stories.tsx +8 -0
  57. package/src/stories/EventLog.stories.tsx +1 -1
  58. package/src/stories/GraphRenderer.stories.tsx +23 -10
  59. package/src/stories/IndustryThemes.stories.tsx +481 -0
  60. package/src/stories/MultiConfig.stories.tsx +8 -0
  61. package/src/stories/MultiDirectionalConnections.stories.tsx +8 -0
  62. package/src/stories/NodeFieldsAudit.stories.tsx +124 -37
  63. package/src/stories/NodeShapes.stories.tsx +73 -59
  64. package/src/utils/animationMapping.ts +19 -23
  65. package/src/utils/graphConverter.ts +61 -21
  66. package/src/utils/iconResolver.tsx +5 -1
@@ -13,7 +13,7 @@ import type { LogLevel } from '@principal-ai/principal-view-core';
13
13
  export interface NodeAnimation {
14
14
  type: 'pulse' | 'flash' | 'shake' | 'entry';
15
15
  duration: number;
16
- intensity?: number; // 0-1 scale
16
+ intensity?: number; // 0-1 scale
17
17
  color?: string;
18
18
  }
19
19
 
@@ -43,7 +43,7 @@ export function logLevelToNodeAnimation(level: LogLevel): NodeAnimation {
43
43
  type: 'pulse',
44
44
  duration: 800,
45
45
  intensity: 0.3,
46
- color: '#94a3b8' // slate-400
46
+ color: '#94a3b8', // slate-400
47
47
  };
48
48
 
49
49
  case 'info':
@@ -51,7 +51,7 @@ export function logLevelToNodeAnimation(level: LogLevel): NodeAnimation {
51
51
  type: 'pulse',
52
52
  duration: 1000,
53
53
  intensity: 0.5,
54
- color: '#3b82f6' // blue-500
54
+ color: '#3b82f6', // blue-500
55
55
  };
56
56
 
57
57
  case 'warn':
@@ -59,7 +59,7 @@ export function logLevelToNodeAnimation(level: LogLevel): NodeAnimation {
59
59
  type: 'pulse',
60
60
  duration: 1200,
61
61
  intensity: 1.0,
62
- color: '#f59e0b' // amber-500
62
+ color: '#f59e0b', // amber-500
63
63
  };
64
64
 
65
65
  case 'error':
@@ -67,7 +67,7 @@ export function logLevelToNodeAnimation(level: LogLevel): NodeAnimation {
67
67
  type: 'flash', // More dramatic animation for errors
68
68
  duration: 1500,
69
69
  intensity: 1.0,
70
- color: '#ef4444' // red-500
70
+ color: '#ef4444', // red-500
71
71
  };
72
72
  }
73
73
  }
@@ -75,10 +75,7 @@ export function logLevelToNodeAnimation(level: LogLevel): NodeAnimation {
75
75
  /**
76
76
  * Map component action to node animation (Milestone 2)
77
77
  */
78
- export function actionToNodeAnimation(
79
- _action: string,
80
- state?: string
81
- ): NodeAnimation {
78
+ export function actionToNodeAnimation(_action: string, state?: string): NodeAnimation {
82
79
  // Default mapping - can be overridden by configuration
83
80
  switch (state) {
84
81
  case 'acquired':
@@ -88,7 +85,7 @@ export function actionToNodeAnimation(
88
85
  type: 'pulse',
89
86
  duration: 1000,
90
87
  intensity: 0.8,
91
- color: '#22c55e' // green-500
88
+ color: '#22c55e', // green-500
92
89
  };
93
90
 
94
91
  case 'waiting':
@@ -97,7 +94,7 @@ export function actionToNodeAnimation(
97
94
  type: 'pulse',
98
95
  duration: 1500,
99
96
  intensity: 0.5,
100
- color: '#eab308' // yellow-500
97
+ color: '#eab308', // yellow-500
101
98
  };
102
99
 
103
100
  case 'error':
@@ -106,7 +103,7 @@ export function actionToNodeAnimation(
106
103
  type: 'shake',
107
104
  duration: 600,
108
105
  intensity: 1.0,
109
- color: '#ef4444' // red-500
106
+ color: '#ef4444', // red-500
110
107
  };
111
108
 
112
109
  case 'completed':
@@ -115,7 +112,7 @@ export function actionToNodeAnimation(
115
112
  type: 'flash',
116
113
  duration: 800,
117
114
  intensity: 0.7,
118
- color: '#22c55e' // green-500
115
+ color: '#22c55e', // green-500
119
116
  };
120
117
 
121
118
  default:
@@ -124,7 +121,7 @@ export function actionToNodeAnimation(
124
121
  type: 'pulse',
125
122
  duration: 1000,
126
123
  intensity: 0.6,
127
- color: '#3b82f6' // blue-500
124
+ color: '#3b82f6', // blue-500
128
125
  };
129
126
  }
130
127
  }
@@ -132,16 +129,15 @@ export function actionToNodeAnimation(
132
129
  /**
133
130
  * Get animation config for edge based on action (Milestone 2)
134
131
  */
135
- export function actionToEdgeAnimation(
136
- _action: string,
137
- edgeConfig?: EdgeAnimation
138
- ): EdgeAnimation {
132
+ export function actionToEdgeAnimation(_action: string, edgeConfig?: EdgeAnimation): EdgeAnimation {
139
133
  // Use edge config if provided, otherwise default
140
- return edgeConfig || {
141
- type: 'flow',
142
- duration: 2000,
143
- direction: 'forward'
144
- };
134
+ return (
135
+ edgeConfig || {
136
+ type: 'flow',
137
+ duration: 2000,
138
+ direction: 'forward',
139
+ }
140
+ );
145
141
  }
146
142
 
147
143
  /**
@@ -1,5 +1,10 @@
1
1
  import { MarkerType, type Node, type Edge } from '@xyflow/react';
2
- import type { NodeState, EdgeState, GraphConfiguration, Violation } from '@principal-ai/principal-view-core';
2
+ import type {
3
+ NodeState,
4
+ EdgeState,
5
+ GraphConfiguration,
6
+ Violation,
7
+ } from '@principal-ai/principal-view-core';
3
8
  import type { CustomNodeData } from '../nodes/CustomNode';
4
9
  import type { CustomEdgeData } from '../edges/CustomEdge';
5
10
 
@@ -19,14 +24,19 @@ export function convertToXYFlowNodes(
19
24
  console.warn(`Node type "${node.type}" not found in configuration for node "${node.id}"`);
20
25
  }
21
26
 
22
- const hasViolations = violations.some(v => v.context?.nodeId === node.id);
27
+ const hasViolations = violations.some((v) => v.context?.nodeId === node.id);
28
+
29
+ // Groups should render behind other nodes and edges
30
+ const isGroup = node.data?.canvasType === 'group';
23
31
 
24
32
  return {
25
33
  id: node.id,
26
34
  type: 'custom',
27
35
  position: node.position || { x: 0, y: 0 },
36
+ // Groups have lower zIndex to render behind edges
37
+ zIndex: isGroup ? -1 : undefined,
28
38
  data: {
29
- name: (node.data.name as string) || node.id,
39
+ name: node.name || node.id,
30
40
  typeDefinition,
31
41
  state: node.state,
32
42
  hasViolations,
@@ -42,6 +52,23 @@ export interface EdgeStateWithHandles extends EdgeState {
42
52
  targetHandle?: string;
43
53
  }
44
54
 
55
+ /**
56
+ * Map canvas side to source handle ID
57
+ * Source handles use '-out' suffix
58
+ */
59
+ function sideToSourceHandle(side?: string): string | undefined {
60
+ if (!side) return undefined;
61
+ return `${side}-out`;
62
+ }
63
+
64
+ /**
65
+ * Map canvas side to target handle ID
66
+ * Target handles use the side name directly
67
+ */
68
+ function sideToTargetHandle(side?: string): string | undefined {
69
+ return side;
70
+ }
71
+
45
72
  /**
46
73
  * Convert our EdgeState to xyflow Edge format
47
74
  */
@@ -58,25 +85,34 @@ export function convertToXYFlowEdges(
58
85
  console.warn(`Edge type "${edge.type}" not found in configuration for edge "${edge.id}"`);
59
86
  }
60
87
 
61
- const hasViolations = violations.some(v => v.context?.edgeId === edge.id);
88
+ const hasViolations = violations.some((v) => v.context?.edgeId === edge.id);
62
89
  const edgeWithHandles = edge as EdgeStateWithHandles;
63
90
 
91
+ // Get handle IDs from edge data (fromSide/toSide) or explicit handles
92
+ const fromSide = edge.data?.fromSide as string | undefined;
93
+ const toSide = edge.data?.toSide as string | undefined;
94
+ const sourceHandle = edgeWithHandles.sourceHandle || sideToSourceHandle(fromSide);
95
+ const targetHandle = edgeWithHandles.targetHandle || sideToTargetHandle(toSide);
96
+
64
97
  // Add arrow marker if edge type is directed
65
98
  // Color priority: edge data color > type definition color > default
66
99
  const edgeColor = edge.data?.color as string | undefined;
67
- const markerEnd = typeDefinition?.directed !== false ? {
68
- type: MarkerType.ArrowClosed,
69
- color: edgeColor || typeDefinition?.color || '#888',
70
- width: 20,
71
- height: 20,
72
- } : undefined;
100
+ const markerEnd =
101
+ typeDefinition?.directed !== false
102
+ ? {
103
+ type: MarkerType.ArrowClosed,
104
+ color: edgeColor || typeDefinition?.color || '#888',
105
+ width: 20,
106
+ height: 20,
107
+ }
108
+ : undefined;
73
109
 
74
110
  return {
75
111
  id: edge.id,
76
112
  source: edge.from,
77
113
  target: edge.to,
78
- sourceHandle: edgeWithHandles.sourceHandle,
79
- targetHandle: edgeWithHandles.targetHandle,
114
+ sourceHandle,
115
+ targetHandle,
80
116
  type: 'custom',
81
117
  animated: typeDefinition?.style === 'animated',
82
118
  markerEnd,
@@ -84,6 +120,7 @@ export function convertToXYFlowEdges(
84
120
  typeDefinition,
85
121
  hasViolations,
86
122
  data: edge.data,
123
+ edgeType: edge.type,
87
124
  },
88
125
  };
89
126
  });
@@ -98,7 +135,7 @@ export function autoLayoutNodes<T extends Record<string, unknown>>(
98
135
  layoutType: 'hierarchical' | 'force-directed' | 'circular' | 'manual' = 'hierarchical'
99
136
  ): Node<T>[] {
100
137
  // Skip if all nodes have positions
101
- const hasPositions = nodes.every(n => n.position.x !== 0 || n.position.y !== 0);
138
+ const hasPositions = nodes.every((n) => n.position.x !== 0 || n.position.y !== 0);
102
139
  if (hasPositions || layoutType === 'manual') {
103
140
  return nodes;
104
141
  }
@@ -120,17 +157,20 @@ export function autoLayoutNodes<T extends Record<string, unknown>>(
120
157
  /**
121
158
  * Simple hierarchical layout algorithm
122
159
  */
123
- function applyHierarchicalLayout<T extends Record<string, unknown>>(nodes: Node<T>[], edges: Edge[]): Node<T>[] {
160
+ function applyHierarchicalLayout<T extends Record<string, unknown>>(
161
+ nodes: Node<T>[],
162
+ edges: Edge[]
163
+ ): Node<T>[] {
124
164
  // Build adjacency list
125
165
  const adjacency = new Map<string, string[]>();
126
166
  const inDegree = new Map<string, number>();
127
167
 
128
- nodes.forEach(node => {
168
+ nodes.forEach((node) => {
129
169
  adjacency.set(node.id, []);
130
170
  inDegree.set(node.id, 0);
131
171
  });
132
172
 
133
- edges.forEach(edge => {
173
+ edges.forEach((edge) => {
134
174
  const targets = adjacency.get(edge.source) || [];
135
175
  targets.push(edge.target);
136
176
  adjacency.set(edge.source, targets);
@@ -160,7 +200,7 @@ function applyHierarchicalLayout<T extends Record<string, unknown>>(nodes: Node<
160
200
  visited.add(nodeId);
161
201
 
162
202
  const neighbors = adjacency.get(nodeId) || [];
163
- neighbors.forEach(neighbor => {
203
+ neighbors.forEach((neighbor) => {
164
204
  const degree = inDegree.get(neighbor)! - 1;
165
205
  inDegree.set(neighbor, degree);
166
206
  if (degree === 0 && !visited.has(neighbor)) {
@@ -175,7 +215,7 @@ function applyHierarchicalLayout<T extends Record<string, unknown>>(nodes: Node<
175
215
  }
176
216
 
177
217
  // Handle any remaining nodes (cycles or disconnected)
178
- const remainingNodes = nodes.filter(n => !visited.has(n.id)).map(n => n.id);
218
+ const remainingNodes = nodes.filter((n) => !visited.has(n.id)).map((n) => n.id);
179
219
  if (remainingNodes.length > 0) {
180
220
  layers.push(remainingNodes);
181
221
  }
@@ -184,8 +224,8 @@ function applyHierarchicalLayout<T extends Record<string, unknown>>(nodes: Node<
184
224
  const LAYER_HEIGHT = 150;
185
225
  const NODE_WIDTH = 200;
186
226
 
187
- return nodes.map(node => {
188
- const layerIndex = layers.findIndex(layer => layer.includes(node.id));
227
+ return nodes.map((node) => {
228
+ const layerIndex = layers.findIndex((layer) => layer.includes(node.id));
189
229
  const layer = layers[layerIndex] || [];
190
230
  const positionInLayer = layer.indexOf(node.id);
191
231
  const layerWidth = layer.length * NODE_WIDTH;
@@ -193,7 +233,7 @@ function applyHierarchicalLayout<T extends Record<string, unknown>>(nodes: Node<
193
233
  return {
194
234
  ...node,
195
235
  position: {
196
- x: (positionInLayer * NODE_WIDTH) + (NODE_WIDTH / 2) - (layerWidth / 2),
236
+ x: positionInLayer * NODE_WIDTH + NODE_WIDTH / 2 - layerWidth / 2,
197
237
  y: layerIndex * LAYER_HEIGHT,
198
238
  },
199
239
  };
@@ -38,7 +38,11 @@ export function resolveIcon(icon?: string, size: number = 20, className?: string
38
38
  }
39
39
 
40
40
  // Fall back to rendering as text (emoji or unicode)
41
- return <span className={className} style={{ fontSize: `${size}px` }}>{icon}</span>;
41
+ return (
42
+ <span className={className} style={{ fontSize: `${size}px` }}>
43
+ {icon}
44
+ </span>
45
+ );
42
46
  }
43
47
 
44
48
  /**