@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.
- 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 +1 -0
- package/dist/edges/CustomEdge.d.ts.map +1 -1
- package/dist/edges/CustomEdge.js +18 -4
- 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 +62 -45
- 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 +47 -19
- 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 +40 -7
- 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 +137 -109
- package/src/nodes/GenericNode.tsx +4 -3
- package/src/stories/AnimationWorkshop.stories.tsx +131 -12
- package/src/stories/CanvasEdgeTypes.stories.tsx +980 -0
- 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 +61 -21
- package/src/utils/iconResolver.tsx +5 -1
|
@@ -10,14 +10,14 @@ import type {
|
|
|
10
10
|
ComponentActivityEvent,
|
|
11
11
|
ComponentActionEvent,
|
|
12
12
|
EdgeAnimationEvent,
|
|
13
|
-
PathBasedEvent
|
|
13
|
+
PathBasedEvent,
|
|
14
14
|
} from '@principal-ai/principal-view-core';
|
|
15
15
|
import {
|
|
16
16
|
logLevelToNodeAnimation,
|
|
17
17
|
actionToNodeAnimation,
|
|
18
18
|
actionToEdgeAnimation,
|
|
19
19
|
type NodeAnimation,
|
|
20
|
-
type EdgeAnimation
|
|
20
|
+
type EdgeAnimation,
|
|
21
21
|
} from '../utils/animationMapping';
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -52,53 +52,62 @@ export function usePathBasedEvents({
|
|
|
52
52
|
events,
|
|
53
53
|
callbacks,
|
|
54
54
|
onEventProcessed,
|
|
55
|
-
minLogLevel = 'info'
|
|
55
|
+
minLogLevel = 'info',
|
|
56
56
|
}: UsePathBasedEventsOptions): void {
|
|
57
57
|
const { onNodeAnimation, onEdgeAnimation } = callbacks;
|
|
58
58
|
|
|
59
59
|
// Process component activity events (Milestone 1)
|
|
60
|
-
const processActivityEvent = useCallback(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
const processActivityEvent = useCallback(
|
|
61
|
+
(event: ComponentActivityEvent) => {
|
|
62
|
+
// Map log level to animation
|
|
63
|
+
const animation = logLevelToNodeAnimation(event.level);
|
|
64
|
+
|
|
65
|
+
// Trigger node animation
|
|
66
|
+
onNodeAnimation(event.componentId, {
|
|
67
|
+
...animation,
|
|
68
|
+
timestamp: event.timestamp,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
onEventProcessed?.(event);
|
|
72
|
+
},
|
|
73
|
+
[onNodeAnimation, onEventProcessed]
|
|
74
|
+
);
|
|
72
75
|
|
|
73
76
|
// Process component action events (Milestone 2)
|
|
74
|
-
const processActionEvent = useCallback(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
77
|
+
const processActionEvent = useCallback(
|
|
78
|
+
(event: ComponentActionEvent) => {
|
|
79
|
+
// Map action/state to animation
|
|
80
|
+
const animation = actionToNodeAnimation(event.action, event.state);
|
|
81
|
+
|
|
82
|
+
// Trigger node animation
|
|
83
|
+
onNodeAnimation(event.componentId, {
|
|
84
|
+
...animation,
|
|
85
|
+
timestamp: event.timestamp,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
onEventProcessed?.(event);
|
|
89
|
+
},
|
|
90
|
+
[onNodeAnimation, onEventProcessed]
|
|
91
|
+
);
|
|
86
92
|
|
|
87
93
|
// Process edge animation events (Milestone 2)
|
|
88
|
-
const processEdgeAnimationEvent = useCallback(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
const processEdgeAnimationEvent = useCallback(
|
|
95
|
+
(event: EdgeAnimationEvent) => {
|
|
96
|
+
const animation = actionToEdgeAnimation(event.triggeredBy?.action || 'unknown', {
|
|
97
|
+
type: event.animation,
|
|
98
|
+
duration: event.duration,
|
|
99
|
+
direction: event.direction,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
onEdgeAnimation(event.edgeId, {
|
|
103
|
+
...animation,
|
|
104
|
+
timestamp: event.timestamp,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
onEventProcessed?.(event);
|
|
108
|
+
},
|
|
109
|
+
[onEdgeAnimation, onEventProcessed]
|
|
110
|
+
);
|
|
102
111
|
|
|
103
112
|
// Process events when they change
|
|
104
113
|
useEffect(() => {
|
|
@@ -149,7 +158,7 @@ export function processBatchEvents(
|
|
|
149
158
|
const animation = logLevelToNodeAnimation(event.level);
|
|
150
159
|
callbacks.onNodeAnimation(event.componentId, {
|
|
151
160
|
...animation,
|
|
152
|
-
timestamp: event.timestamp
|
|
161
|
+
timestamp: event.timestamp,
|
|
153
162
|
});
|
|
154
163
|
}
|
|
155
164
|
break;
|
|
@@ -159,7 +168,7 @@ export function processBatchEvents(
|
|
|
159
168
|
const animation = actionToNodeAnimation(event.action, event.state);
|
|
160
169
|
callbacks.onNodeAnimation(event.componentId, {
|
|
161
170
|
...animation,
|
|
162
|
-
timestamp: event.timestamp
|
|
171
|
+
timestamp: event.timestamp,
|
|
163
172
|
});
|
|
164
173
|
}
|
|
165
174
|
break;
|
|
@@ -169,11 +178,11 @@ export function processBatchEvents(
|
|
|
169
178
|
const animation = actionToEdgeAnimation(event.triggeredBy?.action || 'unknown', {
|
|
170
179
|
type: event.animation,
|
|
171
180
|
duration: event.duration,
|
|
172
|
-
direction: event.direction
|
|
181
|
+
direction: event.direction,
|
|
173
182
|
});
|
|
174
183
|
callbacks.onEdgeAnimation(event.edgeId, {
|
|
175
184
|
...animation,
|
|
176
|
-
timestamp: event.timestamp
|
|
185
|
+
timestamp: event.timestamp,
|
|
177
186
|
});
|
|
178
187
|
}
|
|
179
188
|
break;
|
package/src/index.ts
CHANGED
|
@@ -30,7 +30,12 @@ export type {
|
|
|
30
30
|
|
|
31
31
|
// Export components
|
|
32
32
|
export { GraphRenderer } from './components/GraphRenderer';
|
|
33
|
-
export type {
|
|
33
|
+
export type {
|
|
34
|
+
GraphRendererProps,
|
|
35
|
+
GraphRendererHandle,
|
|
36
|
+
NodePositionChange,
|
|
37
|
+
PendingChanges,
|
|
38
|
+
} from './components/GraphRenderer';
|
|
34
39
|
|
|
35
40
|
export { EventLog } from './components/EventLog';
|
|
36
41
|
export type { EventLogProps } from './components/EventLog';
|
|
@@ -61,7 +66,11 @@ export { CustomEdge } from './edges/CustomEdge';
|
|
|
61
66
|
export type { CustomEdgeData } from './edges/CustomEdge';
|
|
62
67
|
|
|
63
68
|
// Export utilities
|
|
64
|
-
export {
|
|
69
|
+
export {
|
|
70
|
+
convertToXYFlowNodes,
|
|
71
|
+
convertToXYFlowEdges,
|
|
72
|
+
autoLayoutNodes,
|
|
73
|
+
} from './utils/graphConverter';
|
|
65
74
|
export type { EdgeStateWithHandles } from './utils/graphConverter';
|
|
66
75
|
export { Icon, resolveIcon } from './utils/iconResolver';
|
|
67
76
|
export type { IconProps } from './utils/iconResolver';
|
package/src/nodes/CustomNode.tsx
CHANGED
|
@@ -37,9 +37,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
|
|
|
37
37
|
if (!typeDefinition) {
|
|
38
38
|
return (
|
|
39
39
|
<div style={{ padding: '10px', border: '2px solid red', borderRadius: '4px' }}>
|
|
40
|
-
<div style={{ fontSize: '12px', color: 'red' }}>
|
|
41
|
-
Error: Missing node type definition
|
|
42
|
-
</div>
|
|
40
|
+
<div style={{ fontSize: '12px', color: 'red' }}>Error: Missing node type definition</div>
|
|
43
41
|
</div>
|
|
44
42
|
);
|
|
45
43
|
}
|
|
@@ -49,8 +47,11 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
|
|
|
49
47
|
const nodeDataColor = nodeData.color as string | undefined;
|
|
50
48
|
const baseColor = nodeDataColor || typeDefinition.color || '#888';
|
|
51
49
|
// Check node's own states first (from pv.states), then fall back to type definition states
|
|
52
|
-
const nodeDataStates = nodeData.states as
|
|
53
|
-
|
|
50
|
+
const nodeDataStates = nodeData.states as
|
|
51
|
+
| Record<string, { color?: string; label?: string; icon?: string }>
|
|
52
|
+
| undefined;
|
|
53
|
+
const stateColor =
|
|
54
|
+
state && (nodeDataStates?.[state]?.color || typeDefinition.states?.[state]?.color);
|
|
54
55
|
const fillColor = stateColor || baseColor;
|
|
55
56
|
|
|
56
57
|
// Get stroke color - priority: node data stroke > type definition stroke > fill color
|
|
@@ -64,9 +65,10 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
|
|
|
64
65
|
const displayName = nodeProps.name;
|
|
65
66
|
|
|
66
67
|
// Icon priority: node data override > state icon (node data states first) > type definition icon
|
|
67
|
-
const icon =
|
|
68
|
-
|
|
69
|
-
|| typeDefinition.icon
|
|
68
|
+
const icon =
|
|
69
|
+
(nodeData.icon as string) ||
|
|
70
|
+
(state && (nodeDataStates?.[state]?.icon || typeDefinition.states?.[state]?.icon)) ||
|
|
71
|
+
typeDefinition.icon;
|
|
70
72
|
|
|
71
73
|
// Get animation class based on type
|
|
72
74
|
const getAnimationClass = () => {
|
|
@@ -86,11 +88,14 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
|
|
|
86
88
|
|
|
87
89
|
const animationClass = getAnimationClass();
|
|
88
90
|
|
|
91
|
+
// Check if this is a group node
|
|
92
|
+
const isGroup = nodeData.canvasType === 'group';
|
|
93
|
+
|
|
89
94
|
// Shape-specific styles
|
|
90
95
|
const getShapeStyles = () => {
|
|
91
96
|
const baseStyles = {
|
|
92
97
|
padding: '12px 16px',
|
|
93
|
-
backgroundColor: 'white',
|
|
98
|
+
backgroundColor: isGroup ? 'rgba(255, 255, 255, 0.7)' : 'white',
|
|
94
99
|
color: '#000',
|
|
95
100
|
border: `2px solid ${hasViolations ? '#D0021B' : strokeColor}`,
|
|
96
101
|
fontSize: '12px',
|
|
@@ -100,7 +105,7 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
|
|
|
100
105
|
display: 'flex',
|
|
101
106
|
flexDirection: 'column' as const,
|
|
102
107
|
alignItems: 'center',
|
|
103
|
-
justifyContent: 'center',
|
|
108
|
+
justifyContent: isGroup ? 'flex-start' : 'center',
|
|
104
109
|
gap: '4px',
|
|
105
110
|
boxShadow: selected ? `0 0 0 2px ${strokeColor}` : '0 2px 4px rgba(0,0,0,0.1)',
|
|
106
111
|
transition: 'all 0.2s ease',
|
|
@@ -156,47 +161,53 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
|
|
|
156
161
|
// Hexagon with gentle diagonals
|
|
157
162
|
const hexagonClipPath = 'polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)';
|
|
158
163
|
const hexagonBorderWidth = 2;
|
|
159
|
-
const hexagonBorderStyle: React.CSSProperties = isHexagon
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
164
|
+
const hexagonBorderStyle: React.CSSProperties = isHexagon
|
|
165
|
+
? {
|
|
166
|
+
position: 'relative',
|
|
167
|
+
clipPath: hexagonClipPath,
|
|
168
|
+
backgroundColor: hasViolations ? '#D0021B' : strokeColor,
|
|
169
|
+
width: typeDefinition.size?.width || 120,
|
|
170
|
+
height: typeDefinition.size?.height || 120,
|
|
171
|
+
boxShadow: selected ? `0 0 0 2px ${strokeColor}` : '0 2px 4px rgba(0,0,0,0.1)',
|
|
172
|
+
transition: 'all 0.2s ease',
|
|
173
|
+
}
|
|
174
|
+
: {};
|
|
168
175
|
|
|
169
176
|
// Hexagon inner fill styles (white background inset from border)
|
|
170
|
-
const hexagonInnerStyle: React.CSSProperties = isHexagon
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
177
|
+
const hexagonInnerStyle: React.CSSProperties = isHexagon
|
|
178
|
+
? {
|
|
179
|
+
position: 'absolute',
|
|
180
|
+
top: hexagonBorderWidth,
|
|
181
|
+
left: hexagonBorderWidth,
|
|
182
|
+
right: hexagonBorderWidth,
|
|
183
|
+
bottom: hexagonBorderWidth,
|
|
184
|
+
clipPath: hexagonClipPath,
|
|
185
|
+
backgroundColor: 'white',
|
|
186
|
+
color: '#000',
|
|
187
|
+
display: 'flex',
|
|
188
|
+
flexDirection: 'column',
|
|
189
|
+
alignItems: 'center',
|
|
190
|
+
justifyContent: 'center',
|
|
191
|
+
fontSize: '12px',
|
|
192
|
+
fontWeight: 500,
|
|
193
|
+
gap: '4px',
|
|
194
|
+
}
|
|
195
|
+
: {};
|
|
187
196
|
|
|
188
197
|
// Handle styles - larger and more visible in edit mode
|
|
189
|
-
const baseHandleStyle = editable
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
const baseHandleStyle = editable
|
|
199
|
+
? {
|
|
200
|
+
background: color,
|
|
201
|
+
width: 12,
|
|
202
|
+
height: 12,
|
|
203
|
+
border: '2px solid white',
|
|
204
|
+
boxShadow: '0 0 0 1px ' + color,
|
|
205
|
+
}
|
|
206
|
+
: {
|
|
207
|
+
background: color,
|
|
208
|
+
width: 8,
|
|
209
|
+
height: 8,
|
|
210
|
+
};
|
|
200
211
|
|
|
201
212
|
// Diamond handles need to be offset to reach the tips of the rotated shape
|
|
202
213
|
// A 45° rotated square has tips at ~41% beyond the original edges
|
|
@@ -234,52 +245,44 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
|
|
|
234
245
|
|
|
235
246
|
return (
|
|
236
247
|
<>
|
|
237
|
-
{/* Input handles -
|
|
238
|
-
<Handle
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
style={getHandleStyle('top')}
|
|
243
|
-
/>
|
|
244
|
-
<Handle
|
|
245
|
-
type="target"
|
|
246
|
-
position={Position.Left}
|
|
247
|
-
id="left"
|
|
248
|
-
style={getHandleStyle('left')}
|
|
249
|
-
/>
|
|
250
|
-
<Handle
|
|
251
|
-
type="target"
|
|
252
|
-
position={Position.Right}
|
|
253
|
-
id="right"
|
|
254
|
-
style={getHandleStyle('right')}
|
|
255
|
-
/>
|
|
248
|
+
{/* Input handles - all 4 sides for incoming connections */}
|
|
249
|
+
<Handle type="target" position={Position.Top} id="top" style={getHandleStyle('top')} />
|
|
250
|
+
<Handle type="target" position={Position.Bottom} id="bottom" style={getHandleStyle('bottom')} />
|
|
251
|
+
<Handle type="target" position={Position.Left} id="left" style={getHandleStyle('left')} />
|
|
252
|
+
<Handle type="target" position={Position.Right} id="right" style={getHandleStyle('right')} />
|
|
256
253
|
|
|
257
254
|
{/* Hexagon needs a wrapper for proper border rendering */}
|
|
258
255
|
{isHexagon ? (
|
|
259
256
|
<div style={hexagonBorderStyle} className={animationClass}>
|
|
260
257
|
<div style={hexagonInnerStyle}>
|
|
261
|
-
{icon &&
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
258
|
+
{icon && (
|
|
259
|
+
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
260
|
+
{resolveIcon(icon, 20)}
|
|
261
|
+
</div>
|
|
262
|
+
)}
|
|
263
|
+
<div style={{ textAlign: 'center', wordBreak: 'break-word' }}>{displayName}</div>
|
|
265
264
|
{state && (
|
|
266
|
-
<div
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
265
|
+
<div
|
|
266
|
+
style={{
|
|
267
|
+
fontSize: '10px',
|
|
268
|
+
backgroundColor: color,
|
|
269
|
+
color: 'white',
|
|
270
|
+
padding: '2px 6px',
|
|
271
|
+
borderRadius: '4px',
|
|
272
|
+
textAlign: 'center',
|
|
273
|
+
}}
|
|
274
|
+
>
|
|
274
275
|
{nodeDataStates?.[state]?.label || typeDefinition.states?.[state]?.label || state}
|
|
275
276
|
</div>
|
|
276
277
|
)}
|
|
277
278
|
{hasViolations && (
|
|
278
|
-
<div
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
279
|
+
<div
|
|
280
|
+
style={{
|
|
281
|
+
fontSize: '10px',
|
|
282
|
+
color: '#D0021B',
|
|
283
|
+
fontWeight: 'bold',
|
|
284
|
+
}}
|
|
285
|
+
>
|
|
283
286
|
⚠️
|
|
284
287
|
</div>
|
|
285
288
|
)}
|
|
@@ -288,29 +291,58 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
|
|
|
288
291
|
) : (
|
|
289
292
|
<div style={getShapeStyles()} className={animationClass}>
|
|
290
293
|
{/* Inner content (rotated back if diamond) */}
|
|
291
|
-
<div
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
{
|
|
295
|
-
|
|
294
|
+
<div
|
|
295
|
+
style={{
|
|
296
|
+
...(isDiamond ? { transform: 'rotate(-45deg)' } : {}),
|
|
297
|
+
...(isGroup ? { width: '100%' } : {}),
|
|
298
|
+
}}
|
|
299
|
+
>
|
|
300
|
+
{/* Groups: icon and text inline, centered */}
|
|
301
|
+
{isGroup ? (
|
|
302
|
+
<div
|
|
303
|
+
style={{
|
|
304
|
+
display: 'flex',
|
|
305
|
+
alignItems: 'center',
|
|
306
|
+
justifyContent: 'center',
|
|
307
|
+
gap: '6px',
|
|
308
|
+
width: '100%',
|
|
309
|
+
}}
|
|
310
|
+
>
|
|
311
|
+
{icon && resolveIcon(icon, 18)}
|
|
312
|
+
<div style={{ wordBreak: 'break-word' }}>{displayName}</div>
|
|
313
|
+
</div>
|
|
314
|
+
) : (
|
|
315
|
+
<>
|
|
316
|
+
{icon && (
|
|
317
|
+
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
318
|
+
{resolveIcon(icon, 20)}
|
|
319
|
+
</div>
|
|
320
|
+
)}
|
|
321
|
+
<div style={{ textAlign: 'center', wordBreak: 'break-word' }}>{displayName}</div>
|
|
322
|
+
</>
|
|
323
|
+
)}
|
|
296
324
|
{state && (
|
|
297
|
-
<div
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
325
|
+
<div
|
|
326
|
+
style={{
|
|
327
|
+
fontSize: '10px',
|
|
328
|
+
backgroundColor: color,
|
|
329
|
+
color: 'white',
|
|
330
|
+
padding: '2px 6px',
|
|
331
|
+
borderRadius: '4px',
|
|
332
|
+
textAlign: 'center',
|
|
333
|
+
}}
|
|
334
|
+
>
|
|
305
335
|
{nodeDataStates?.[state]?.label || typeDefinition.states?.[state]?.label || state}
|
|
306
336
|
</div>
|
|
307
337
|
)}
|
|
308
338
|
{hasViolations && (
|
|
309
|
-
<div
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
339
|
+
<div
|
|
340
|
+
style={{
|
|
341
|
+
fontSize: '10px',
|
|
342
|
+
color: '#D0021B',
|
|
343
|
+
fontWeight: 'bold',
|
|
344
|
+
}}
|
|
345
|
+
>
|
|
314
346
|
⚠️
|
|
315
347
|
</div>
|
|
316
348
|
)}
|
|
@@ -318,19 +350,15 @@ export const CustomNode: React.FC<NodeProps<any>> = ({ data, selected }) => {
|
|
|
318
350
|
</div>
|
|
319
351
|
)}
|
|
320
352
|
|
|
321
|
-
{/* Output handles -
|
|
353
|
+
{/* Output handles - all 4 sides for outgoing connections */}
|
|
354
|
+
<Handle type="source" position={Position.Top} id="top-out" style={getHandleStyle('top')} />
|
|
322
355
|
<Handle
|
|
323
356
|
type="source"
|
|
324
357
|
position={Position.Bottom}
|
|
325
|
-
id="bottom"
|
|
358
|
+
id="bottom-out"
|
|
326
359
|
style={getHandleStyle('bottom')}
|
|
327
360
|
/>
|
|
328
|
-
<Handle
|
|
329
|
-
type="source"
|
|
330
|
-
position={Position.Left}
|
|
331
|
-
id="left-out"
|
|
332
|
-
style={getHandleStyle('left')}
|
|
333
|
-
/>
|
|
361
|
+
<Handle type="source" position={Position.Left} id="left-out" style={getHandleStyle('left')} />
|
|
334
362
|
<Handle
|
|
335
363
|
type="source"
|
|
336
364
|
position={Position.Right}
|
|
@@ -29,9 +29,10 @@ export const GenericNode: React.FC<GenericNodeProps> = ({
|
|
|
29
29
|
hasViolations,
|
|
30
30
|
}) => {
|
|
31
31
|
// Get color based on state or default
|
|
32
|
-
const color =
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
const color =
|
|
33
|
+
state && typeDefinition.states?.[state]?.color
|
|
34
|
+
? typeDefinition.states[state].color
|
|
35
|
+
: typeDefinition.color || '#888';
|
|
35
36
|
|
|
36
37
|
return (
|
|
37
38
|
<div
|