@principal-ai/subsystems-react 0.17.0 → 0.19.0

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 (108) hide show
  1. package/README.md +11 -85
  2. package/dist/index.d.ts +7 -44
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +6 -32
  5. package/dist/index.js.map +1 -1
  6. package/dist/subsystem/SubsystemCarousel.d.ts +36 -0
  7. package/dist/subsystem/SubsystemCarousel.d.ts.map +1 -0
  8. package/dist/subsystem/SubsystemCarousel.js +163 -0
  9. package/dist/subsystem/SubsystemCarousel.js.map +1 -0
  10. package/dist/subsystem/SubsystemComponentGraph.d.ts +25 -0
  11. package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
  12. package/dist/subsystem/SubsystemComponentGraph.js +329 -68
  13. package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
  14. package/package.json +4 -4
  15. package/src/index.ts +9 -199
  16. package/src/subsystem/SubsystemComponentGraph.tsx +396 -50
  17. package/src/components/ConfigurationSelector.tsx +0 -133
  18. package/src/components/GraphRenderer.tsx +0 -3195
  19. package/src/components/MultiCanvasRenderer.tsx +0 -295
  20. package/src/components/NodeTooltip.tsx +0 -279
  21. package/src/components/PendingChanges.test.tsx +0 -375
  22. package/src/components/SelectionSidebar.tsx +0 -352
  23. package/src/components/SequenceDiagramRenderer.tsx +0 -1156
  24. package/src/components/WorkflowSequenceDiagram.tsx +0 -270
  25. package/src/components/dashboard/DashboardRenderer.tsx +0 -317
  26. package/src/components/dashboard/MetricPanel.tsx +0 -254
  27. package/src/components/dashboard/MockDataProvider.ts +0 -330
  28. package/src/components/dashboard/components/BarChart.tsx +0 -299
  29. package/src/components/dashboard/components/LineChart.tsx +0 -279
  30. package/src/components/dashboard/components/MetricCard.tsx +0 -270
  31. package/src/components/dashboard/components/SourceLink.tsx +0 -63
  32. package/src/components/dashboard/components/TimeRangeSelector.tsx +0 -280
  33. package/src/components/dashboard/components/index.ts +0 -5
  34. package/src/components/dashboard/index.ts +0 -47
  35. package/src/components/dashboard/types.ts +0 -126
  36. package/src/components/state-view/PipelineView.tsx +0 -347
  37. package/src/components/state-view/index.ts +0 -14
  38. package/src/components/state-view/types.ts +0 -261
  39. package/src/components/state-view/useStateView.ts +0 -205
  40. package/src/contexts/GraphEditContext.tsx +0 -27
  41. package/src/contexts/TooltipPortalContext.ts +0 -8
  42. package/src/edges/CustomEdge.tsx +0 -402
  43. package/src/edges/GenericEdge.tsx +0 -36
  44. package/src/hooks/usePathBasedEvents.ts +0 -192
  45. package/src/hooks/useSequenceLayout.ts +0 -543
  46. package/src/hooks/useUndoRedo.ts +0 -144
  47. package/src/nodes/CustomNode.tsx +0 -1231
  48. package/src/nodes/GenericNode.tsx +0 -55
  49. package/src/nodes/otel/OtelBoundaryNode.tsx +0 -224
  50. package/src/nodes/otel/OtelEventNode.tsx +0 -225
  51. package/src/nodes/otel/OtelResourceNode.tsx +0 -251
  52. package/src/nodes/otel/OtelScopeNode.tsx +0 -221
  53. package/src/nodes/otel/OtelSpanConventionNode.tsx +0 -295
  54. package/src/nodes/otel/index.ts +0 -23
  55. package/src/nodes/otel/shared/NodeBadges.tsx +0 -295
  56. package/src/nodes/otel/shared/NodeContent.tsx +0 -204
  57. package/src/nodes/otel/shared/index.ts +0 -8
  58. package/src/nodes/otel/shared/types.ts +0 -138
  59. package/src/nodes/otel/shared/useNodeBehavior.ts +0 -114
  60. package/src/stories/Audit/BoundaryNodes.stories.tsx +0 -385
  61. package/src/stories/Audit/CanvasEdgeTypes.stories.tsx +0 -1448
  62. package/src/stories/Audit/CanvasNodeTypes.stories.tsx +0 -1049
  63. package/src/stories/Audit/ColorPriority.stories.tsx +0 -895
  64. package/src/stories/Audit/DiamondBadges.stories.tsx +0 -629
  65. package/src/stories/Audit/NodeDimensionsTesting.stories.tsx +0 -724
  66. package/src/stories/Audit/NodeFieldsAudit.stories.tsx +0 -996
  67. package/src/stories/Audit/NodeFontSizeTesting.stories.tsx +0 -715
  68. package/src/stories/Audit/NodeShapes.stories.tsx +0 -1208
  69. package/src/stories/Audit/WideNodesTesting.stories.tsx +0 -1045
  70. package/src/stories/Components/DashboardRenderer.stories.tsx +0 -263
  71. package/src/stories/Components/GraphRenderer.stories.tsx +0 -3236
  72. package/src/stories/Components/MultiCanvasRenderer.stories.tsx +0 -370
  73. package/src/stories/Components/SequenceDiagram.stories.tsx +0 -255
  74. package/src/stories/Components/StateView.stories.tsx +0 -417
  75. package/src/stories/Components/TraceViewer.stories.tsx +0 -442
  76. package/src/stories/Components/WorkflowSequenceDiagram.stories.tsx +0 -802
  77. package/src/stories/Features/AnimationWorkshop.stories.tsx +0 -727
  78. package/src/stories/Features/ChangeTypeVisual.stories.tsx +0 -67
  79. package/src/stories/Features/EventDrivenAnimations.stories.tsx +0 -513
  80. package/src/stories/Features/MultiConfig.stories.tsx +0 -539
  81. package/src/stories/Layout/ElkEdgeRouting.stories.tsx +0 -539
  82. package/src/stories/Layout/GraphOrientation.stories.tsx +0 -500
  83. package/src/stories/Layout/MultiDirectionalConnections.stories.tsx +0 -352
  84. package/src/stories/Nodes/NodeDefinitionComparison.stories.tsx +0 -560
  85. package/src/stories/Nodes/NodeNameFallbacks.stories.tsx +0 -491
  86. package/src/stories/Nodes/NumericColors.stories.tsx +0 -440
  87. package/src/stories/OTEL/EventNameDisplay.stories.tsx +0 -694
  88. package/src/stories/OTEL/EventNameFallback.stories.tsx +0 -578
  89. package/src/stories/OTEL/OtelComponents.stories.tsx +0 -422
  90. package/src/stories/OTEL/OtelNodeTypesPrototype.stories.tsx +0 -806
  91. package/src/stories/OTEL/RawEventCards.stories.tsx +0 -57
  92. package/src/stories/OTEL/SpanBadges.stories.tsx +0 -840
  93. package/src/stories/OTEL/ValidationEventsCanvas.stories.tsx +0 -209
  94. package/src/stories/RealWorld/BacklogMdCanvases.stories.tsx +0 -721
  95. package/src/stories/RealWorld/FileCitySequence.stories.tsx +0 -516
  96. package/src/stories/RealWorld/RealCanvasFiles.stories.tsx +0 -336
  97. package/src/stories/__traces__/test-run.canvas.json +0 -241
  98. package/src/stories/data/activity-feed-analytics.dashboard.json +0 -300
  99. package/src/stories/data/file-city-images-transformed.otel.canvas.json +0 -295
  100. package/src/stories/data/file-city-workflows.json +0 -269
  101. package/src/stories/data/graph-converter-test-execution.json +0 -443
  102. package/src/stories/data/graph-converter-test.workflow.json +0 -105
  103. package/src/stories/data/graph-converter-validated-execution.json +0 -58
  104. package/src/utils/animationMapping.ts +0 -166
  105. package/src/utils/canvasBounds.ts +0 -146
  106. package/src/utils/graphConverter.ts +0 -148
  107. package/src/utils/iconResolver.tsx +0 -53
  108. package/src/utils/orientationUtils.ts +0 -73
@@ -1,895 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { GraphRenderer } from '../../components/GraphRenderer';
4
- import type { ExtendedCanvas, GraphEvent, ComponentLibrary } from '@principal-ai/subsystems-core';
5
- import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
6
-
7
- // Helper component that sets initial node states via events
8
- const GraphWithInitialStates: React.FC<{
9
- canvas: ExtendedCanvas;
10
- width: number;
11
- height: number;
12
- showMinimap: boolean;
13
- initialStates?: Record<string, string>;
14
- }> = ({ canvas, width, height, showMinimap, initialStates }) => {
15
- const [events, setEvents] = useState<GraphEvent[]>([]);
16
-
17
- useEffect(() => {
18
- if (initialStates) {
19
- const stateEvents: GraphEvent[] = Object.entries(initialStates).map(
20
- ([nodeId, newState], idx) => ({
21
- id: `init-state-${idx}`,
22
- type: 'state_changed',
23
- timestamp: Date.now(),
24
- category: 'state' as const,
25
- payload: { nodeId, newState },
26
- })
27
- );
28
- setEvents(stateEvents);
29
- }
30
- // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally run only on mount
31
- }, []);
32
-
33
- return (
34
- <GraphRenderer
35
- canvas={canvas}
36
- width={width}
37
- height={height}
38
- showMinimap={showMinimap}
39
- events={events}
40
- />
41
- );
42
- };
43
-
44
- const meta = {
45
- title: 'Audit/ColorPriority',
46
- component: GraphRenderer,
47
- parameters: {
48
- layout: 'centered',
49
- },
50
- tags: ['autodocs'],
51
- decorators: [
52
- (Story) => (
53
- <ThemeProvider theme={defaultEditorTheme}>
54
- <Story />
55
- </ThemeProvider>
56
- ),
57
- ],
58
- } satisfies Meta<typeof GraphRenderer>;
59
-
60
- export default meta;
61
- type Story = StoryObj<typeof meta>;
62
-
63
- // ============================================================================
64
- // Node Color Priority Story
65
- // Priority: state color > node data color (pv.fill) > node.color > type definition color > default (#888)
66
- // ============================================================================
67
-
68
- const nodeColorPriorityCanvas: ExtendedCanvas = {
69
- nodes: [
70
- // 1. Default color only (no color specified anywhere)
71
- {
72
- id: 'default-only',
73
- type: 'text',
74
- x: 100,
75
- y: 100,
76
- width: 180,
77
- height: 100,
78
- text: 'SOURCE: Default\nEXPECT: Gray',
79
- pv: {
80
- nodeType: 'default-demo',
81
- shape: 'rectangle',
82
- },
83
- },
84
- // 2. Type definition color via node.color
85
- {
86
- id: 'node-color',
87
- type: 'text',
88
- x: 320,
89
- y: 100,
90
- width: 180,
91
- height: 100,
92
- text: 'SOURCE: node.color\nEXPECT: BLUE',
93
- color: '#0000FF',
94
- pv: {
95
- nodeType: 'color-demo',
96
- shape: 'rectangle',
97
- },
98
- },
99
- // 3. pv.fill overrides node.color
100
- {
101
- id: 'pv-fill',
102
- type: 'text',
103
- x: 540,
104
- y: 100,
105
- width: 180,
106
- height: 100,
107
- text: 'SOURCE: pv.fill\nEXPECT: GREEN\n(node.color=BLUE ignored)',
108
- color: '#0000FF', // This should be overridden
109
- pv: {
110
- nodeType: 'fill-demo',
111
- shape: 'rectangle',
112
- fill: '#00FF00', // This wins
113
- },
114
- },
115
- // 4. State color overrides everything (idle state)
116
- {
117
- id: 'state-color-idle',
118
- type: 'text',
119
- x: 760,
120
- y: 100,
121
- width: 180,
122
- height: 100,
123
- text: 'SOURCE: state.idle.color\nEXPECT: RED\n(pv.fill & node.color ignored)',
124
- color: '#0000FF', // Overridden
125
- pv: {
126
- nodeType: 'state-demo',
127
- shape: 'rectangle',
128
- fill: '#00FF00', // Overridden
129
- states: {
130
- idle: { label: 'Idle', color: '#FF0000' },
131
- active: { label: 'Active', color: '#FFFF00' },
132
- },
133
- },
134
- },
135
- ],
136
- edges: [],
137
- pv: {
138
- version: '1.0.0',
139
- name: 'Node Color Priority Demo',
140
- description: 'Shows how node colors are prioritized',
141
- edgeTypes: {},
142
- },
143
- };
144
-
145
- export const NodeColorPriority: Story = {
146
- render: () => (
147
- <GraphWithInitialStates
148
- canvas={nodeColorPriorityCanvas}
149
- width={1050}
150
- height={320}
151
- showMinimap={false}
152
- initialStates={{ 'state-color-idle': 'idle' }}
153
- />
154
- ),
155
- parameters: {
156
- docs: {
157
- description: {
158
- story: `
159
- **Node Fill Color Priority** (highest to lowest):
160
-
161
- 1. **State color** - When a node has an active state with a color defined
162
- 2. **pv.fill** - Explicit fill color in the PV extension
163
- 3. **node.color** - The standard canvas node color property
164
- 4. **Type definition color** - Color defined in the node type
165
- 5. **Default** - Falls back to #888
166
-
167
- In this demo:
168
- - Node 1: No color specified → uses default #888
169
- - Node 2: Only node.color → shows blue
170
- - Node 3: Both node.color AND pv.fill → pv.fill (green) wins
171
- - Node 4: Has state "idle" with purple color → state color wins over everything
172
- `,
173
- },
174
- },
175
- },
176
- };
177
-
178
- // ============================================================================
179
- // Node Stroke Color Priority Story
180
- // Priority: pv.stroke > type definition stroke > fill color
181
- // ============================================================================
182
-
183
- const strokeColorPriorityCanvas: ExtendedCanvas = {
184
- nodes: [
185
- // 1. Stroke defaults to fill color
186
- {
187
- id: 'stroke-default',
188
- type: 'text',
189
- x: 100,
190
- y: 100,
191
- width: 200,
192
- height: 100,
193
- text: 'SOURCE: Default (=fill)\nEXPECT BORDER: BLUE\n(same as fill color)',
194
- color: '#0000FF',
195
- pv: {
196
- nodeType: 'stroke-default-demo',
197
- shape: 'rectangle',
198
- },
199
- },
200
- // 2. Explicit stroke color
201
- {
202
- id: 'stroke-explicit',
203
- type: 'text',
204
- x: 340,
205
- y: 100,
206
- width: 200,
207
- height: 100,
208
- text: 'SOURCE: pv.stroke\nEXPECT BORDER: RED\n(fill is BLUE)',
209
- color: '#0000FF', // Fill is blue
210
- pv: {
211
- nodeType: 'stroke-explicit-demo',
212
- shape: 'rectangle',
213
- stroke: '#FF0000', // But stroke is red
214
- },
215
- },
216
- // 3. Different fill and stroke
217
- {
218
- id: 'fill-and-stroke',
219
- type: 'text',
220
- x: 580,
221
- y: 100,
222
- width: 200,
223
- height: 100,
224
- text: 'SOURCE: pv.fill + pv.stroke\nEXPECT BORDER: YELLOW\nEXPECT FILL: GREEN',
225
- pv: {
226
- nodeType: 'both-demo',
227
- shape: 'rectangle',
228
- fill: '#00FF00',
229
- stroke: '#FFFF00',
230
- },
231
- },
232
- ],
233
- edges: [],
234
- pv: {
235
- version: '1.0.0',
236
- name: 'Stroke Color Priority Demo',
237
- description: 'Shows how stroke colors are prioritized',
238
- edgeTypes: {},
239
- },
240
- };
241
-
242
- export const StrokeColorPriority: Story = {
243
- args: {
244
- canvas: strokeColorPriorityCanvas,
245
- width: 880,
246
- height: 320,
247
- showMinimap: false,
248
- },
249
- parameters: {
250
- docs: {
251
- description: {
252
- story: `
253
- **Node Stroke Color Priority** (highest to lowest):
254
-
255
- 1. **pv.stroke** - Explicit stroke color in the PV extension
256
- 2. **Type definition stroke** - Stroke defined in node type
257
- 3. **Fill color** - Falls back to the resolved fill color
258
-
259
- In this demo:
260
- - Node 1: No stroke specified → border matches fill color (blue)
261
- - Node 2: Blue fill but red pv.stroke → red border
262
- - Node 3: Green fill with orange stroke → independent colors
263
- `,
264
- },
265
- },
266
- },
267
- };
268
-
269
- // ============================================================================
270
- // Edge Color Priority Story
271
- // Priority: edge.color > edge type definition color > default
272
- // ============================================================================
273
-
274
- const edgeColorPriorityCanvas: ExtendedCanvas = {
275
- nodes: [
276
- {
277
- id: 'source-1',
278
- type: 'text',
279
- x: 100,
280
- y: 100,
281
- width: 180,
282
- height: 70,
283
- text: 'SOURCE: edgeType color\nEXPECT EDGE: BLUE',
284
- color: '#888888',
285
- pv: { nodeType: 'source', shape: 'rectangle' },
286
- },
287
- {
288
- id: 'target-1',
289
- type: 'text',
290
- x: 450,
291
- y: 100,
292
- width: 100,
293
- height: 70,
294
- text: 'Target',
295
- color: '#888888',
296
- pv: { nodeType: 'target', shape: 'rectangle' },
297
- },
298
- {
299
- id: 'source-2',
300
- type: 'text',
301
- x: 100,
302
- y: 220,
303
- width: 180,
304
- height: 70,
305
- text: 'SOURCE: edge.color\nEXPECT EDGE: RED\n(edgeType=BLUE ignored)',
306
- color: '#888888',
307
- pv: { nodeType: 'source', shape: 'rectangle' },
308
- },
309
- {
310
- id: 'target-2',
311
- type: 'text',
312
- x: 450,
313
- y: 220,
314
- width: 100,
315
- height: 70,
316
- text: 'Target',
317
- color: '#888888',
318
- pv: { nodeType: 'target', shape: 'rectangle' },
319
- },
320
- {
321
- id: 'source-3',
322
- type: 'text',
323
- x: 100,
324
- y: 340,
325
- width: 180,
326
- height: 70,
327
- text: 'SOURCE: Default\nEXPECT EDGE: Gray',
328
- color: '#888888',
329
- pv: { nodeType: 'source', shape: 'rectangle' },
330
- },
331
- {
332
- id: 'target-3',
333
- type: 'text',
334
- x: 450,
335
- y: 340,
336
- width: 100,
337
- height: 70,
338
- text: 'Target',
339
- color: '#888888',
340
- pv: { nodeType: 'target', shape: 'rectangle' },
341
- },
342
- ],
343
- edges: [
344
- // 1. Edge type color only (from edgeTypes definition)
345
- {
346
- id: 'edge-type-color',
347
- fromNode: 'source-1',
348
- toNode: 'target-1',
349
- pv: {
350
- edgeType: 'dataflow', // Uses edgeTypes.dataflow.color = BLUE
351
- },
352
- },
353
- // 2. Edge's own color overrides edge type
354
- {
355
- id: 'edge-own-color',
356
- fromNode: 'source-2',
357
- toNode: 'target-2',
358
- color: '#FF0000', // RED - overrides the edge type color
359
- pv: {
360
- edgeType: 'dataflow', // Would be blue, but overridden
361
- },
362
- },
363
- // 3. No color specified (default)
364
- {
365
- id: 'edge-default',
366
- fromNode: 'source-3',
367
- toNode: 'target-3',
368
- pv: {
369
- edgeType: 'uncolored', // No color in edge type
370
- },
371
- },
372
- ],
373
- pv: {
374
- version: '1.0.0',
375
- name: 'Edge Color Priority Demo',
376
- description: 'Shows how edge colors are prioritized',
377
- edgeTypes: {
378
- dataflow: {
379
- style: 'solid',
380
- color: '#0000FF', // BLUE
381
- directed: true,
382
- },
383
- uncolored: {
384
- style: 'dashed',
385
- directed: true,
386
- // No color defined
387
- },
388
- },
389
- },
390
- };
391
-
392
- export const EdgeColorPriority: Story = {
393
- args: {
394
- canvas: edgeColorPriorityCanvas,
395
- width: 650,
396
- height: 520,
397
- showMinimap: false,
398
- },
399
- parameters: {
400
- docs: {
401
- description: {
402
- story: `
403
- **Edge Color Priority** (highest to lowest):
404
-
405
- 1. **edge.color** - The edge's own color property
406
- 2. **Edge type definition color** - Color from pv.edgeTypes[type].color
407
- 3. **Default** - System default edge color
408
-
409
- In this demo:
410
- - Edge 1: Uses "dataflow" type → shows cyan from edgeTypes
411
- - Edge 2: Has edge.color AND uses "dataflow" type → purple (edge.color) wins
412
- - Edge 3: Uses "uncolored" type with no color → default styling
413
- `,
414
- },
415
- },
416
- },
417
- };
418
-
419
- // ============================================================================
420
- // Icon Priority Story
421
- // Priority: node data icon > state icon > type definition icon
422
- // ============================================================================
423
-
424
- const iconPriorityCanvas: ExtendedCanvas = {
425
- nodes: [
426
- // 1. Type definition icon only
427
- {
428
- id: 'icon-type',
429
- type: 'text',
430
- x: 100,
431
- y: 100,
432
- width: 180,
433
- height: 100,
434
- text: 'SOURCE: pv.icon\nEXPECT ICON: Settings',
435
- color: '#0000FF',
436
- pv: {
437
- nodeType: 'icon-type-demo',
438
- shape: 'rectangle',
439
- icon: 'Settings',
440
- },
441
- },
442
- // 2. State icon (when in a state)
443
- {
444
- id: 'icon-state',
445
- type: 'text',
446
- x: 320,
447
- y: 100,
448
- width: 180,
449
- height: 100,
450
- text: 'SOURCE: state.icon\nEXPECT ICON: Check\n(pv.icon=settings ignored)',
451
- color: '#00FF00',
452
- pv: {
453
- nodeType: 'icon-state-demo',
454
- shape: 'rectangle',
455
- icon: 'Settings', // Type icon - should be overridden
456
- states: {
457
- complete: { label: 'Complete', color: '#00FF00', icon: 'Check' },
458
- },
459
- },
460
- },
461
- ],
462
- edges: [],
463
- pv: {
464
- version: '1.0.0',
465
- name: 'Icon Priority Demo',
466
- description: 'Shows how icons are prioritized',
467
- edgeTypes: {},
468
- },
469
- };
470
-
471
- export const IconPriority: Story = {
472
- render: () => (
473
- <GraphWithInitialStates
474
- canvas={iconPriorityCanvas}
475
- width={600}
476
- height={320}
477
- showMinimap={false}
478
- initialStates={{ 'icon-state': 'complete' }}
479
- />
480
- ),
481
- parameters: {
482
- docs: {
483
- description: {
484
- story: `
485
- **Icon Priority** (highest to lowest):
486
-
487
- 1. **Node data icon override** - Explicitly set icon in node data
488
- 2. **State icon** - Icon from the current state definition
489
- 3. **Type definition icon** - Icon from pv.icon
490
-
491
- In this demo:
492
- - Node 1: Only has type icon (settings) → shows settings
493
- - Node 2: Has type icon (settings) BUT is in "complete" state with check icon → shows check
494
- `,
495
- },
496
- },
497
- },
498
- };
499
-
500
- // ============================================================================
501
- // Combined Priority Demo - All in One
502
- // ============================================================================
503
-
504
- const combinedPriorityCanvas: ExtendedCanvas = {
505
- nodes: [
506
- // Row 1 label
507
- {
508
- id: 'label-row1',
509
- type: 'text',
510
- x: 50,
511
- y: 50,
512
- width: 700,
513
- height: 40,
514
- text: 'FILL COLOR PRIORITY (low to high): Default → node.color → pv.fill → state.color',
515
- pv: { nodeType: 'label', shape: 'rectangle' },
516
- },
517
- // Lowest priority
518
- {
519
- id: 'fill-1',
520
- type: 'text',
521
- x: 50,
522
- y: 120,
523
- width: 150,
524
- height: 80,
525
- text: 'SOURCE: Default\nEXPECT: Gray',
526
- pv: { nodeType: 'fill-demo', shape: 'rectangle' },
527
- },
528
- // node.color
529
- {
530
- id: 'fill-2',
531
- type: 'text',
532
- x: 230,
533
- y: 120,
534
- width: 150,
535
- height: 80,
536
- text: 'SOURCE: node.color\nEXPECT: BLUE',
537
- color: '#0000FF',
538
- pv: { nodeType: 'fill-demo', shape: 'rectangle' },
539
- },
540
- // pv.fill
541
- {
542
- id: 'fill-3',
543
- type: 'text',
544
- x: 410,
545
- y: 120,
546
- width: 150,
547
- height: 80,
548
- text: 'SOURCE: pv.fill\nEXPECT: GREEN',
549
- color: '#0000FF',
550
- pv: { nodeType: 'fill-demo', shape: 'rectangle', fill: '#00FF00' },
551
- },
552
- // state color
553
- {
554
- id: 'fill-4',
555
- type: 'text',
556
- x: 590,
557
- y: 120,
558
- width: 150,
559
- height: 80,
560
- text: 'SOURCE: state.color\nEXPECT: RED',
561
- color: '#0000FF',
562
- pv: {
563
- nodeType: 'fill-demo',
564
- shape: 'rectangle',
565
- fill: '#00FF00',
566
- states: { error: { label: 'Error', color: '#FF0000' } },
567
- },
568
- },
569
- // Row 2 label
570
- {
571
- id: 'label-row2',
572
- type: 'text',
573
- x: 50,
574
- y: 240,
575
- width: 500,
576
- height: 40,
577
- text: 'STROKE PRIORITY (low to high): fill color → pv.stroke',
578
- pv: { nodeType: 'label', shape: 'rectangle' },
579
- },
580
- // Stroke = fill
581
- {
582
- id: 'stroke-1',
583
- type: 'text',
584
- x: 50,
585
- y: 310,
586
- width: 180,
587
- height: 80,
588
- text: 'SOURCE: Default (=fill)\nEXPECT BORDER: BLUE',
589
- color: '#0000FF',
590
- pv: { nodeType: 'stroke-demo', shape: 'rectangle' },
591
- },
592
- // pv.stroke
593
- {
594
- id: 'stroke-2',
595
- type: 'text',
596
- x: 260,
597
- y: 310,
598
- width: 180,
599
- height: 80,
600
- text: 'SOURCE: pv.stroke\nEXPECT BORDER: YELLOW',
601
- color: '#0000FF',
602
- pv: { nodeType: 'stroke-demo', shape: 'rectangle', stroke: '#FFFF00' },
603
- },
604
- ],
605
- edges: [],
606
- pv: {
607
- version: '1.0.0',
608
- name: 'Combined Color Priority Demo',
609
- description: 'Overview of all color priorities',
610
- edgeTypes: {},
611
- },
612
- };
613
-
614
- export const CombinedPriorityOverview: Story = {
615
- render: () => (
616
- <GraphWithInitialStates
617
- canvas={combinedPriorityCanvas}
618
- width={850}
619
- height={480}
620
- showMinimap={false}
621
- initialStates={{ 'fill-4': 'error' }}
622
- />
623
- ),
624
- parameters: {
625
- docs: {
626
- description: {
627
- story: `
628
- **Complete Color Priority Reference**
629
-
630
- This story shows all color priority chains at a glance:
631
-
632
- **Fill Color:** Default (#888) → node.color → pv.fill → State color
633
-
634
- **Stroke Color:** Fill color → pv.stroke
635
-
636
- Higher priority always wins when multiple values are specified.
637
- `,
638
- },
639
- },
640
- },
641
- };
642
-
643
- // ============================================================================
644
- // Library Color Priority Story
645
- // Priority: library.nodeComponents.color → node.color → pv.fill → state.color
646
- // ============================================================================
647
-
648
- const libraryColorTestLibrary: ComponentLibrary = {
649
- version: '1.0.0',
650
- name: 'Color Test Library',
651
- description: 'Library for testing color inheritance',
652
- resources: {},
653
- nodeComponents: {
654
- 'service-red': {
655
- description: 'Service with red color from library',
656
- shape: 'rectangle',
657
- color: '#FF0000', // RED from library
658
- icon: 'Server',
659
- },
660
- 'service-green': {
661
- description: 'Service with green color from library',
662
- shape: 'rectangle',
663
- color: '#00FF00', // GREEN from library
664
- icon: 'Database',
665
- },
666
- 'service-blue': {
667
- description: 'Service with blue color from library',
668
- shape: 'rectangle',
669
- color: '#0000FF', // BLUE from library
670
- icon: 'Settings',
671
- },
672
- 'service-purple': {
673
- description: 'Service with purple color from library',
674
- shape: 'rectangle',
675
- color: '#8B00FF', // PURPLE from library
676
- icon: 'Zap',
677
- },
678
- },
679
- edgeComponents: {
680
- 'flow-orange': {
681
- description: 'Flow edge with orange color from library',
682
- style: 'solid',
683
- color: '#FFA500', // ORANGE from library
684
- directed: true,
685
- },
686
- 'flow-cyan': {
687
- description: 'Flow edge with cyan color from library',
688
- style: 'solid',
689
- color: '#00FFFF', // CYAN from library
690
- directed: true,
691
- },
692
- },
693
- };
694
-
695
- const libraryColorPriorityCanvas: ExtendedCanvas = {
696
- nodes: [
697
- // 1. Library color only (nodeType references library, no other colors)
698
- {
699
- id: 'library-only',
700
- type: 'text',
701
- x: 100,
702
- y: 100,
703
- width: 180,
704
- height: 100,
705
- text: 'SOURCE: library\nEXPECT: RED\n(from service-red)',
706
- pv: {
707
- nodeType: 'service-red', // Should get RED from library
708
- shape: 'rectangle',
709
- },
710
- },
711
- // 2. Library color overridden by node.color
712
- {
713
- id: 'library-vs-node-color',
714
- type: 'text',
715
- x: 320,
716
- y: 100,
717
- width: 180,
718
- height: 100,
719
- text: 'SOURCE: node.color\nEXPECT: YELLOW\n(library GREEN ignored)',
720
- color: '#FFFF00', // YELLOW - should override library GREEN
721
- pv: {
722
- nodeType: 'service-green', // Would be GREEN, but overridden
723
- shape: 'rectangle',
724
- },
725
- },
726
- // 3. Library color overridden by pv.fill
727
- {
728
- id: 'library-vs-pv-fill',
729
- type: 'text',
730
- x: 540,
731
- y: 100,
732
- width: 180,
733
- height: 100,
734
- text: 'SOURCE: pv.fill\nEXPECT: CYAN\n(library BLUE ignored)',
735
- pv: {
736
- nodeType: 'service-blue', // Would be BLUE, but overridden
737
- shape: 'rectangle',
738
- fill: '#00FFFF', // CYAN - should override library BLUE
739
- },
740
- },
741
- // 4. Library color overridden by state color
742
- {
743
- id: 'library-vs-state',
744
- type: 'text',
745
- x: 760,
746
- y: 100,
747
- width: 180,
748
- height: 100,
749
- text: 'SOURCE: state.color\nEXPECT: ORANGE\n(library PURPLE ignored)',
750
- pv: {
751
- nodeType: 'service-purple', // Would be PURPLE, but overridden by state
752
- shape: 'rectangle',
753
- states: {
754
- active: { label: 'Active', color: '#FFA500' }, // ORANGE
755
- },
756
- },
757
- },
758
- // 5. Another library color node for comparison
759
- {
760
- id: 'library-green',
761
- type: 'text',
762
- x: 100,
763
- y: 250,
764
- width: 180,
765
- height: 100,
766
- text: 'SOURCE: library\nEXPECT: GREEN\n(from service-green)',
767
- pv: {
768
- nodeType: 'service-green',
769
- shape: 'rectangle',
770
- },
771
- },
772
- // 6. Library blue for comparison
773
- {
774
- id: 'library-blue',
775
- type: 'text',
776
- x: 320,
777
- y: 250,
778
- width: 180,
779
- height: 100,
780
- text: 'SOURCE: library\nEXPECT: BLUE\n(from service-blue)',
781
- pv: {
782
- nodeType: 'service-blue',
783
- shape: 'rectangle',
784
- },
785
- },
786
- ],
787
- edges: [
788
- // Edge using library color
789
- {
790
- id: 'edge-library-orange',
791
- fromNode: 'library-green',
792
- toNode: 'library-blue',
793
- pv: {
794
- edgeType: 'flow-orange', // Should get ORANGE from library
795
- },
796
- },
797
- // Edge with library color overridden
798
- {
799
- id: 'edge-override',
800
- fromNode: 'library-only',
801
- toNode: 'library-vs-node-color',
802
- color: '#FF00FF', // MAGENTA - overrides library
803
- pv: {
804
- edgeType: 'flow-cyan', // Would be CYAN, but overridden
805
- },
806
- },
807
- ],
808
- pv: {
809
- version: '1.0.0',
810
- name: 'Library Color Priority Demo',
811
- description: 'Shows how library.yaml colors are applied and overridden',
812
- edgeTypes: {},
813
- },
814
- };
815
-
816
- // Helper component for library color test
817
- const GraphWithLibrary: React.FC<{
818
- canvas: ExtendedCanvas;
819
- library: ComponentLibrary;
820
- width: number;
821
- height: number;
822
- showMinimap: boolean;
823
- initialStates?: Record<string, string>;
824
- }> = ({ canvas, library, width, height, showMinimap, initialStates }) => {
825
- const [events, setEvents] = useState<GraphEvent[]>([]);
826
-
827
- useEffect(() => {
828
- if (initialStates) {
829
- const stateEvents: GraphEvent[] = Object.entries(initialStates).map(
830
- ([nodeId, newState], idx) => ({
831
- id: `init-state-${idx}`,
832
- type: 'state_changed',
833
- timestamp: Date.now(),
834
- category: 'state' as const,
835
- payload: { nodeId, newState },
836
- })
837
- );
838
- setEvents(stateEvents);
839
- }
840
- // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally run only on mount
841
- }, []);
842
-
843
- return (
844
- <GraphRenderer
845
- canvas={canvas}
846
- library={library}
847
- width={width}
848
- height={height}
849
- showMinimap={showMinimap}
850
- events={events}
851
- />
852
- );
853
- };
854
-
855
- export const LibraryColorPriority: Story = {
856
- render: () => (
857
- <GraphWithLibrary
858
- canvas={libraryColorPriorityCanvas}
859
- library={libraryColorTestLibrary}
860
- width={1050}
861
- height={450}
862
- showMinimap={false}
863
- initialStates={{ 'library-vs-state': 'active' }}
864
- />
865
- ),
866
- parameters: {
867
- docs: {
868
- description: {
869
- story: `
870
- **Library Color Priority** - Tests colors from library.yaml nodeComponents
871
-
872
- This story validates that the \`library\` prop correctly provides colors to nodes via nodeType lookup.
873
-
874
- **Full Fill Color Priority Chain** (lowest to highest):
875
- 1. **Default** - Falls back to #888 gray
876
- 2. **Library nodeComponent color** - Color from library.yaml nodeComponents[nodeType].color
877
- 3. **node.color** - The standard canvas node color property
878
- 4. **pv.fill** - Explicit fill color in the PV extension
879
- 5. **state.color** - When a node has an active state with a color defined
880
-
881
- **In this demo:**
882
- - Row 1 Node 1: Uses \`service-red\` nodeType → gets RED from library
883
- - Row 1 Node 2: Uses \`service-green\` nodeType but has node.color=YELLOW → YELLOW wins
884
- - Row 1 Node 3: Uses \`service-blue\` nodeType but has pv.fill=CYAN → CYAN wins
885
- - Row 1 Node 4: Uses \`service-purple\` nodeType but is in "active" state → ORANGE wins
886
- - Row 2: Pure library colors (GREEN, BLUE) for visual comparison
887
-
888
- **Edge Colors:**
889
- - Bottom edge: Uses \`flow-orange\` edgeType → gets ORANGE from library
890
- - Top edge: Uses \`flow-cyan\` but has edge.color=MAGENTA → MAGENTA wins
891
- `,
892
- },
893
- },
894
- },
895
- };