@principal-ai/subsystems-react 0.17.0 → 0.18.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 (98) hide show
  1. package/dist/index.d.ts +7 -44
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +6 -32
  4. package/dist/index.js.map +1 -1
  5. package/package.json +2 -2
  6. package/src/index.ts +9 -199
  7. package/src/components/ConfigurationSelector.tsx +0 -133
  8. package/src/components/GraphRenderer.tsx +0 -3195
  9. package/src/components/MultiCanvasRenderer.tsx +0 -295
  10. package/src/components/NodeTooltip.tsx +0 -279
  11. package/src/components/PendingChanges.test.tsx +0 -375
  12. package/src/components/SelectionSidebar.tsx +0 -352
  13. package/src/components/SequenceDiagramRenderer.tsx +0 -1156
  14. package/src/components/WorkflowSequenceDiagram.tsx +0 -270
  15. package/src/components/dashboard/DashboardRenderer.tsx +0 -317
  16. package/src/components/dashboard/MetricPanel.tsx +0 -254
  17. package/src/components/dashboard/MockDataProvider.ts +0 -330
  18. package/src/components/dashboard/components/BarChart.tsx +0 -299
  19. package/src/components/dashboard/components/LineChart.tsx +0 -279
  20. package/src/components/dashboard/components/MetricCard.tsx +0 -270
  21. package/src/components/dashboard/components/SourceLink.tsx +0 -63
  22. package/src/components/dashboard/components/TimeRangeSelector.tsx +0 -280
  23. package/src/components/dashboard/components/index.ts +0 -5
  24. package/src/components/dashboard/index.ts +0 -47
  25. package/src/components/dashboard/types.ts +0 -126
  26. package/src/components/state-view/PipelineView.tsx +0 -347
  27. package/src/components/state-view/index.ts +0 -14
  28. package/src/components/state-view/types.ts +0 -261
  29. package/src/components/state-view/useStateView.ts +0 -205
  30. package/src/contexts/GraphEditContext.tsx +0 -27
  31. package/src/contexts/TooltipPortalContext.ts +0 -8
  32. package/src/edges/CustomEdge.tsx +0 -402
  33. package/src/edges/GenericEdge.tsx +0 -36
  34. package/src/hooks/usePathBasedEvents.ts +0 -192
  35. package/src/hooks/useSequenceLayout.ts +0 -543
  36. package/src/hooks/useUndoRedo.ts +0 -144
  37. package/src/nodes/CustomNode.tsx +0 -1231
  38. package/src/nodes/GenericNode.tsx +0 -55
  39. package/src/nodes/otel/OtelBoundaryNode.tsx +0 -224
  40. package/src/nodes/otel/OtelEventNode.tsx +0 -225
  41. package/src/nodes/otel/OtelResourceNode.tsx +0 -251
  42. package/src/nodes/otel/OtelScopeNode.tsx +0 -221
  43. package/src/nodes/otel/OtelSpanConventionNode.tsx +0 -295
  44. package/src/nodes/otel/index.ts +0 -23
  45. package/src/nodes/otel/shared/NodeBadges.tsx +0 -295
  46. package/src/nodes/otel/shared/NodeContent.tsx +0 -204
  47. package/src/nodes/otel/shared/index.ts +0 -8
  48. package/src/nodes/otel/shared/types.ts +0 -138
  49. package/src/nodes/otel/shared/useNodeBehavior.ts +0 -114
  50. package/src/stories/Audit/BoundaryNodes.stories.tsx +0 -385
  51. package/src/stories/Audit/CanvasEdgeTypes.stories.tsx +0 -1448
  52. package/src/stories/Audit/CanvasNodeTypes.stories.tsx +0 -1049
  53. package/src/stories/Audit/ColorPriority.stories.tsx +0 -895
  54. package/src/stories/Audit/DiamondBadges.stories.tsx +0 -629
  55. package/src/stories/Audit/NodeDimensionsTesting.stories.tsx +0 -724
  56. package/src/stories/Audit/NodeFieldsAudit.stories.tsx +0 -996
  57. package/src/stories/Audit/NodeFontSizeTesting.stories.tsx +0 -715
  58. package/src/stories/Audit/NodeShapes.stories.tsx +0 -1208
  59. package/src/stories/Audit/WideNodesTesting.stories.tsx +0 -1045
  60. package/src/stories/Components/DashboardRenderer.stories.tsx +0 -263
  61. package/src/stories/Components/GraphRenderer.stories.tsx +0 -3236
  62. package/src/stories/Components/MultiCanvasRenderer.stories.tsx +0 -370
  63. package/src/stories/Components/SequenceDiagram.stories.tsx +0 -255
  64. package/src/stories/Components/StateView.stories.tsx +0 -417
  65. package/src/stories/Components/TraceViewer.stories.tsx +0 -442
  66. package/src/stories/Components/WorkflowSequenceDiagram.stories.tsx +0 -802
  67. package/src/stories/Features/AnimationWorkshop.stories.tsx +0 -727
  68. package/src/stories/Features/ChangeTypeVisual.stories.tsx +0 -67
  69. package/src/stories/Features/EventDrivenAnimations.stories.tsx +0 -513
  70. package/src/stories/Features/MultiConfig.stories.tsx +0 -539
  71. package/src/stories/Layout/ElkEdgeRouting.stories.tsx +0 -539
  72. package/src/stories/Layout/GraphOrientation.stories.tsx +0 -500
  73. package/src/stories/Layout/MultiDirectionalConnections.stories.tsx +0 -352
  74. package/src/stories/Nodes/NodeDefinitionComparison.stories.tsx +0 -560
  75. package/src/stories/Nodes/NodeNameFallbacks.stories.tsx +0 -491
  76. package/src/stories/Nodes/NumericColors.stories.tsx +0 -440
  77. package/src/stories/OTEL/EventNameDisplay.stories.tsx +0 -694
  78. package/src/stories/OTEL/EventNameFallback.stories.tsx +0 -578
  79. package/src/stories/OTEL/OtelComponents.stories.tsx +0 -422
  80. package/src/stories/OTEL/OtelNodeTypesPrototype.stories.tsx +0 -806
  81. package/src/stories/OTEL/RawEventCards.stories.tsx +0 -57
  82. package/src/stories/OTEL/SpanBadges.stories.tsx +0 -840
  83. package/src/stories/OTEL/ValidationEventsCanvas.stories.tsx +0 -209
  84. package/src/stories/RealWorld/BacklogMdCanvases.stories.tsx +0 -721
  85. package/src/stories/RealWorld/FileCitySequence.stories.tsx +0 -516
  86. package/src/stories/RealWorld/RealCanvasFiles.stories.tsx +0 -336
  87. package/src/stories/__traces__/test-run.canvas.json +0 -241
  88. package/src/stories/data/activity-feed-analytics.dashboard.json +0 -300
  89. package/src/stories/data/file-city-images-transformed.otel.canvas.json +0 -295
  90. package/src/stories/data/file-city-workflows.json +0 -269
  91. package/src/stories/data/graph-converter-test-execution.json +0 -443
  92. package/src/stories/data/graph-converter-test.workflow.json +0 -105
  93. package/src/stories/data/graph-converter-validated-execution.json +0 -58
  94. package/src/utils/animationMapping.ts +0 -166
  95. package/src/utils/canvasBounds.ts +0 -146
  96. package/src/utils/graphConverter.ts +0 -148
  97. package/src/utils/iconResolver.tsx +0 -53
  98. 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
- };