@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,802 +0,0 @@
1
- import React, { useState } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { WorkflowSequenceDiagram } from '../../components/WorkflowSequenceDiagram';
4
- import type { WorkflowScenario, Canvas } from '@principal-ai/subsystems-core';
5
- import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
6
-
7
- const meta = {
8
- title: 'Components/WorkflowSequenceDiagram',
9
- component: WorkflowSequenceDiagram,
10
- parameters: {
11
- layout: 'fullscreen',
12
- docs: {
13
- description: {
14
- component: `
15
- # WorkflowSequenceDiagram Component
16
-
17
- A wrapper around \`SequenceDiagramRenderer\` that simplifies working with workflow scenarios.
18
- Automatically converts workflow scenarios to sequence diagram format and optionally extracts
19
- participant metadata from a canvas.
20
-
21
- ## Key Features
22
-
23
- - **Simplified API**: Just pass a workflow scenario instead of manually creating events/edges
24
- - **Canvas Integration**: Optionally provide a canvas to extract scope and participant metadata
25
- - **Move vs Transform Events**: Automatically distinguishes between cross-participant (move) and internal (transform) events
26
- - **Clickable Labels**: Edge labels are fully clickable and highlight when selected (no dots needed!)
27
- - **Event Selection**: Click any label to select it - the label changes color to show selection
28
- - **Empty State Handling**: Gracefully handles scenarios with no events
29
-
30
- ## Usage
31
-
32
- \`\`\`tsx
33
- <WorkflowSequenceDiagram
34
- scenario={workflowScenario}
35
- canvas={otelCanvas}
36
- height={600}
37
- onEventIndexChange={(index) => console.log('Selected event:', index)}
38
- />
39
- \`\`\`
40
- `,
41
- },
42
- },
43
- },
44
- tags: ['autodocs'],
45
- decorators: [
46
- (Story) => (
47
- <ThemeProvider theme={defaultEditorTheme}>
48
- <div style={{ height: '100vh' }}>
49
- <Story />
50
- </div>
51
- </ThemeProvider>
52
- ),
53
- ],
54
- } satisfies Meta<typeof WorkflowSequenceDiagram>;
55
-
56
- export default meta;
57
- type Story = StoryObj<typeof meta>;
58
-
59
- // ============================================================================
60
- // Sample Scenarios
61
- // ============================================================================
62
-
63
- /**
64
- * Simple authentication workflow
65
- */
66
- const simpleAuthScenario: WorkflowScenario = {
67
- id: 'simple-auth',
68
- name: 'Simple Authentication',
69
- description: 'Basic authentication flow with validation',
70
- template: {
71
- events: {
72
- 'auth.request.received': {},
73
- 'auth.validation.started': {},
74
- 'database.user.lookup': {},
75
- 'auth.validation.completed': {},
76
- 'auth.token.generated': {},
77
- },
78
- },
79
- };
80
-
81
- /**
82
- * E-commerce order processing workflow
83
- */
84
- const orderProcessingScenario: WorkflowScenario = {
85
- id: 'order-processing',
86
- name: 'Order Processing',
87
- description: 'Complete order processing with payment and inventory',
88
- template: {
89
- events: {
90
- 'api.order.received': {},
91
- 'auth.session.validate': {},
92
- 'order.validation.started': {},
93
- 'inventory.check.availability': {},
94
- 'inventory.reserve.items': {},
95
- 'order.validation.completed': {},
96
- 'payment.charge.initiated': {},
97
- 'payment.processor.authorize': {},
98
- 'payment.charge.completed': {},
99
- 'order.fulfillment.started': {},
100
- 'notification.email.sent': {},
101
- 'api.response.sent': {},
102
- },
103
- },
104
- };
105
-
106
- /**
107
- * Microservices workflow with multiple participants
108
- */
109
- const microservicesScenario: WorkflowScenario = {
110
- id: 'microservices-flow',
111
- name: 'Microservices Communication',
112
- description: 'Inter-service communication pattern',
113
- template: {
114
- events: {
115
- 'gateway.request.received': {},
116
- 'gateway.route.determined': {},
117
- 'userservice.user.fetch': {},
118
- 'userservice.database.query': {},
119
- 'userservice.response.prepared': {},
120
- 'gateway.response.aggregated': {},
121
- 'cache.data.stored': {},
122
- 'gateway.response.sent': {},
123
- },
124
- },
125
- };
126
-
127
- /**
128
- * Error handling workflow
129
- */
130
- const errorHandlingScenario: WorkflowScenario = {
131
- id: 'error-handling',
132
- name: 'Error Handling Flow',
133
- description: 'Workflow demonstrating error handling and recovery',
134
- template: {
135
- events: {
136
- 'api.request.received': {},
137
- 'service.processing.started': {},
138
- 'service.validation.failed': {},
139
- 'service.error.logged': {},
140
- 'notification.alert.sent': {},
141
- 'api.error.response.sent': {},
142
- },
143
- },
144
- };
145
-
146
- /**
147
- * Scenario with long namespace titles for stress-testing swimlane header rendering
148
- */
149
- const longTitlesScenario: WorkflowScenario = {
150
- id: 'long-titles',
151
- name: 'Long Swimlane Titles',
152
- description: 'Scenario designed to stress-test header rendering with long participant names',
153
- template: {
154
- events: {
155
- 'authenticationAndAuthorizationService.request.received': {},
156
- 'authenticationAndAuthorizationService.token.validated': {},
157
- 'distributedTransactionCoordinator.transaction.started': {},
158
- 'inventoryReservationAndAllocationManager.items.reserved': {},
159
- 'paymentProcessingAndFraudDetectionGateway.charge.authorized': {},
160
- 'paymentProcessingAndFraudDetectionGateway.charge.captured': {},
161
- 'distributedTransactionCoordinator.transaction.committed': {},
162
- 'notificationDispatchAndDeliveryService.confirmation.sent': {},
163
- },
164
- },
165
- };
166
-
167
- /**
168
- * Empty scenario for testing empty state
169
- */
170
- const emptyScenario: WorkflowScenario = {
171
- id: 'empty',
172
- name: 'Empty Workflow',
173
- description: 'Scenario with no events',
174
- template: {
175
- events: {},
176
- },
177
- };
178
-
179
- /**
180
- * Sample canvas with OTEL event nodes for metadata extraction
181
- */
182
- const sampleCanvas: Canvas = {
183
- nodes: [
184
- {
185
- id: 'auth-validation-started',
186
- type: 'otel-event',
187
- x: 0,
188
- y: 0,
189
- width: 200,
190
- height: 60,
191
- event: { name: 'auth.validation.started' },
192
- otel: { scope: 'auth' },
193
- label: 'Start Validation',
194
- },
195
- {
196
- id: 'database-lookup',
197
- type: 'otel-event',
198
- x: 0,
199
- y: 100,
200
- width: 200,
201
- height: 60,
202
- event: { name: 'database.user.lookup' },
203
- otel: { scope: 'database' },
204
- label: 'Lookup User',
205
- },
206
- {
207
- id: 'auth-validation-completed',
208
- type: 'otel-event',
209
- x: 0,
210
- y: 200,
211
- width: 200,
212
- height: 60,
213
- event: { name: 'auth.validation.completed' },
214
- otel: { scope: 'auth' },
215
- label: 'Validation Complete',
216
- },
217
- ],
218
- edges: [],
219
- };
220
-
221
- // ============================================================================
222
- // Stories
223
- // ============================================================================
224
-
225
- /**
226
- * Diagnostic story to test click functionality
227
- * Shows click feedback with colored labels (no dots!)
228
- * Verifies the label text matches the selected event (no off-by-one errors)
229
- */
230
- export const ClickDiagnostic: Story = {
231
- render: () => {
232
- const [selectedIndex, setSelectedIndex] = useState<number | undefined>(undefined);
233
- const [clickCount, setClickCount] = useState(0);
234
- const [lastClickTime, setLastClickTime] = useState<string>('Never');
235
-
236
- const handleClick = (index: number) => {
237
- console.log('Click detected! Event index:', index);
238
- setSelectedIndex(index);
239
- setClickCount(prev => prev + 1);
240
- setLastClickTime(new Date().toLocaleTimeString());
241
- };
242
-
243
- const eventNames = Object.keys(simpleAuthScenario.template.events);
244
-
245
- return (
246
- <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
247
- <div
248
- style={{
249
- padding: '16px',
250
- background: '#1a1a1a',
251
- color: '#00ff00',
252
- fontFamily: 'monospace',
253
- borderBottom: '2px solid #00ff00',
254
- marginBottom: 16,
255
- }}
256
- >
257
- <h3 style={{ margin: '0 0 12px 0', color: '#00ff00' }}>🔍 CLICK DIAGNOSTIC MODE</h3>
258
- <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, fontSize: 13 }}>
259
- <div>Total Clicks: <strong>{clickCount}</strong></div>
260
- <div>Last Click: <strong>{lastClickTime}</strong></div>
261
- <div>Selected Index: <strong>{selectedIndex !== undefined ? selectedIndex : 'None'}</strong></div>
262
- <div>Selected Event: <strong>{selectedIndex !== undefined ? eventNames[selectedIndex] : 'None'}</strong></div>
263
- </div>
264
- <div style={{ marginTop: 12, padding: '8px', background: '#2a2a2a', borderRadius: 4 }}>
265
- 💡 <strong>Click any label</strong> - it will highlight with colored background!
266
- Watch the label change color and verify it matches the event shown above.
267
- </div>
268
- {selectedIndex !== undefined && (
269
- <div style={{ marginTop: 8, padding: '8px', background: '#1e5a1e', borderRadius: 4, fontSize: 14 }}>
270
- ✅ <strong>Selected:</strong> "{eventNames[selectedIndex]?.split('.').pop()}" (the highlighted label should match!)
271
- </div>
272
- )}
273
- </div>
274
- <div style={{ flex: 1 }}>
275
- <WorkflowSequenceDiagram
276
- scenario={simpleAuthScenario}
277
- height="100%"
278
- selectedEventIndex={selectedIndex}
279
- onEventIndexChange={handleClick}
280
- layoutOptions={{
281
- eventSpacing: 100,
282
- }}
283
- />
284
- </div>
285
- </div>
286
- );
287
- },
288
- };
289
-
290
- /**
291
- * Basic workflow scenario without canvas
292
- */
293
- export const BasicScenario: Story = {
294
- args: {
295
- scenario: simpleAuthScenario,
296
- height: '100%',
297
- layoutOptions: {
298
- eventSpacing: 80,
299
- },
300
- },
301
- };
302
-
303
- /**
304
- * Workflow with canvas metadata for enhanced labels
305
- */
306
- export const WithCanvasMetadata: Story = {
307
- args: {
308
- scenario: simpleAuthScenario,
309
- canvas: sampleCanvas,
310
- height: '100%',
311
- layoutOptions: {
312
- eventSpacing: 80,
313
- },
314
- },
315
- };
316
-
317
- /**
318
- * Complex workflow with many events and participants
319
- */
320
- export const ComplexWorkflow: Story = {
321
- args: {
322
- scenario: orderProcessingScenario,
323
- height: '100%',
324
- layoutOptions: {
325
- laneWidth: 200,
326
- eventSpacing: 70,
327
- },
328
- },
329
- };
330
-
331
- /**
332
- * Microservices communication pattern
333
- */
334
- export const MicroservicesPattern: Story = {
335
- args: {
336
- scenario: microservicesScenario,
337
- height: '100%',
338
- layoutOptions: {
339
- laneWidth: 220,
340
- eventSpacing: 75,
341
- },
342
- },
343
- };
344
-
345
- /**
346
- * Error handling workflow
347
- */
348
- export const ErrorHandling: Story = {
349
- args: {
350
- scenario: errorHandlingScenario,
351
- height: '100%',
352
- layoutOptions: {
353
- eventSpacing: 80,
354
- },
355
- },
356
- };
357
-
358
- /**
359
- * Empty scenario showing graceful handling of no events
360
- */
361
- export const EmptyState: Story = {
362
- args: {
363
- scenario: emptyScenario,
364
- height: '100%',
365
- },
366
- };
367
-
368
- /**
369
- * Long swimlane titles - stress-tests header rendering with very long participant names.
370
- * Use this to verify text wrapping, truncation, and overflow behavior in lane headers.
371
- */
372
- export const LongSwimlaneTitles: Story = {
373
- args: {
374
- scenario: longTitlesScenario,
375
- height: '100%',
376
- layoutOptions: {
377
- eventSpacing: 80,
378
- headerHeight: 100,
379
- },
380
- },
381
- };
382
-
383
- /**
384
- * Interactive example with event selection
385
- * Click on any event in the sequence diagram to select it
386
- * NOTE: Event click areas are small (14x14px by default) - use larger nodes for better UX
387
- */
388
- export const WithEventSelection: Story = {
389
- render: () => {
390
- const [selectedIndex, setSelectedIndex] = useState<number | undefined>(undefined);
391
-
392
- // Get event names from the scenario
393
- const eventNames = Object.keys(simpleAuthScenario.template.events);
394
- const selectedEventName = selectedIndex !== undefined ? eventNames[selectedIndex] : null;
395
-
396
- return (
397
- <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
398
- <div
399
- style={{
400
- padding: '16px',
401
- background: '#2c3e50',
402
- color: '#ecf0f1',
403
- borderBottom: '3px solid #3498db',
404
- marginBottom: 16,
405
- }}
406
- >
407
- <div style={{ fontSize: 16, marginBottom: 8 }}>
408
- <strong>💡 Click on any event dot in the diagram below</strong>
409
- </div>
410
- <div style={{ fontSize: 14, opacity: 0.9 }}>
411
- <strong>Selected Event:</strong>{' '}
412
- {selectedIndex !== undefined ? (
413
- <span style={{ color: '#3498db', fontFamily: 'monospace' }}>
414
- [{selectedIndex}] {selectedEventName}
415
- </span>
416
- ) : (
417
- <span style={{ opacity: 0.6 }}>None - Click an event to select</span>
418
- )}
419
- </div>
420
- {selectedIndex !== undefined && (
421
- <button
422
- onClick={() => setSelectedIndex(undefined)}
423
- style={{
424
- marginTop: 8,
425
- padding: '6px 16px',
426
- cursor: 'pointer',
427
- background: '#e74c3c',
428
- color: 'white',
429
- border: 'none',
430
- borderRadius: 4,
431
- fontSize: 13,
432
- }}
433
- >
434
- Clear Selection
435
- </button>
436
- )}
437
- </div>
438
- <div style={{ flex: 1 }}>
439
- <WorkflowSequenceDiagram
440
- scenario={simpleAuthScenario}
441
- height="100%"
442
- selectedEventIndex={selectedIndex}
443
- onEventIndexChange={setSelectedIndex}
444
- layoutOptions={{
445
- eventSpacing: 80,
446
- nodeWidth: 14,
447
- nodeHeight: 14,
448
- }}
449
- />
450
- </div>
451
- </div>
452
- );
453
- },
454
- };
455
-
456
- /**
457
- * Interactive selection example
458
- * Click labels to see them highlight with colored background
459
- */
460
- export const InteractiveSelection: Story = {
461
- render: () => {
462
- const [selectedIndex, setSelectedIndex] = useState<number | undefined>(0);
463
-
464
- // Get event names from the scenario
465
- const eventNames = Object.keys(orderProcessingScenario.template.events);
466
- const selectedEventName = selectedIndex !== undefined ? eventNames[selectedIndex] : null;
467
-
468
- return (
469
- <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
470
- <div
471
- style={{
472
- padding: '16px',
473
- background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
474
- color: 'white',
475
- borderBottom: '4px solid #5a67d8',
476
- marginBottom: 16,
477
- }}
478
- >
479
- <div style={{ fontSize: 16, marginBottom: 8 }}>
480
- <strong>✨ Clickable Labels Demo</strong>
481
- </div>
482
- <div style={{ fontSize: 14, marginBottom: 8 }}>
483
- Labels highlight with colored background when selected - clean and intuitive!
484
- </div>
485
- <div style={{ fontSize: 14, opacity: 0.9 }}>
486
- <strong>Selected:</strong>{' '}
487
- {selectedIndex !== undefined ? (
488
- <span style={{ color: '#ffd700', fontFamily: 'monospace', fontWeight: 'bold' }}>
489
- Event {selectedIndex + 1} - {selectedEventName}
490
- </span>
491
- ) : (
492
- <span style={{ opacity: 0.7 }}>Click any label</span>
493
- )}
494
- </div>
495
- </div>
496
- <div style={{ flex: 1 }}>
497
- <WorkflowSequenceDiagram
498
- scenario={orderProcessingScenario}
499
- height="100%"
500
- selectedEventIndex={selectedIndex}
501
- onEventIndexChange={setSelectedIndex}
502
- layoutOptions={{
503
- eventSpacing: 80,
504
- }}
505
- />
506
- </div>
507
- </div>
508
- );
509
- },
510
- };
511
-
512
- /**
513
- * Comparison: Edge Labels (Default) vs Node Labels (Optional)
514
- * Both are clickable! This shows different visualization styles.
515
- */
516
- export const EdgeLabelsVsNodeLabels: Story = {
517
- render: () => {
518
- const [selectedLeft, setSelectedLeft] = useState<number | undefined>(undefined);
519
- const [selectedRight, setSelectedRight] = useState<number | undefined>(undefined);
520
-
521
- return (
522
- <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', height: '100%', gap: 16 }}>
523
- <div style={{ display: 'flex', flexDirection: 'column' }}>
524
- <div style={{
525
- padding: '12px',
526
- background: '#3498db',
527
- color: 'white',
528
- borderBottom: '3px solid #2980b9',
529
- marginBottom: 12,
530
- }}>
531
- <h3 style={{ margin: '0 0 8px 0', fontSize: 15 }}>📊 Edge Labels (Default)</h3>
532
- <div style={{ fontSize: 13 }}>
533
- Selected: {selectedLeft !== undefined ? `Event ${selectedLeft + 1}` : 'None'}
534
- </div>
535
- <div style={{ fontSize: 12, marginTop: 4, opacity: 0.9 }}>
536
- Traditional UML style - labels on arrows
537
- </div>
538
- </div>
539
- <div style={{ flex: 1, border: '1px solid #ddd', borderRadius: 4 }}>
540
- <WorkflowSequenceDiagram
541
- scenario={simpleAuthScenario}
542
- height="100%"
543
- selectedEventIndex={selectedLeft}
544
- onEventIndexChange={setSelectedLeft}
545
- showEventLabels={false}
546
- layoutOptions={{
547
- eventSpacing: 100,
548
- }}
549
- />
550
- </div>
551
- </div>
552
- <div style={{ display: 'flex', flexDirection: 'column' }}>
553
- <div style={{
554
- padding: '12px',
555
- background: '#9b59b6',
556
- color: 'white',
557
- borderBottom: '3px solid #8e44ad',
558
- marginBottom: 12,
559
- }}>
560
- <h3 style={{ margin: '0 0 8px 0', fontSize: 15 }}>🏷️ Node Labels (Alternative)</h3>
561
- <div style={{ fontSize: 13 }}>
562
- Selected: {selectedRight !== undefined ? `Event ${selectedRight + 1}` : 'None'}
563
- </div>
564
- <div style={{ fontSize: 12, marginTop: 4, opacity: 0.9 }}>
565
- Labels directly on events - alternative style
566
- </div>
567
- </div>
568
- <div style={{ flex: 1, border: '1px solid #ddd', borderRadius: 4 }}>
569
- <WorkflowSequenceDiagram
570
- scenario={simpleAuthScenario}
571
- height="100%"
572
- selectedEventIndex={selectedRight}
573
- onEventIndexChange={setSelectedRight}
574
- showEventLabels={true}
575
- layoutOptions={{
576
- eventSpacing: 100,
577
- nodeWidth: 80,
578
- nodeHeight: 50,
579
- }}
580
- />
581
- </div>
582
- </div>
583
- </div>
584
- );
585
- },
586
- };
587
-
588
- /**
589
- * Comparison of default lanes vs fully-drilled lanes
590
- */
591
- export const DefaultVsDrilled: Story = {
592
- render: () => {
593
- const allTopSegments = Array.from(
594
- new Set(
595
- Object.keys(orderProcessingScenario.template.events ?? {})
596
- .map((name) => name.split('.')[0])
597
- .filter(Boolean)
598
- )
599
- );
600
-
601
- return (
602
- <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', height: '100%', gap: 16 }}>
603
- <div style={{ display: 'flex', flexDirection: 'column' }}>
604
- <h3 style={{ margin: '0 0 12px 0' }}>Default (top-level lanes)</h3>
605
- <div style={{ flex: 1, border: '1px solid #ddd', borderRadius: 4 }}>
606
- <WorkflowSequenceDiagram
607
- scenario={orderProcessingScenario}
608
- height="100%"
609
- layoutOptions={{ laneWidth: 180, eventSpacing: 60 }}
610
- />
611
- </div>
612
- </div>
613
- <div style={{ display: 'flex', flexDirection: 'column' }}>
614
- <h3 style={{ margin: '0 0 12px 0' }}>All top-level lanes drilled</h3>
615
- <div style={{ flex: 1, border: '1px solid #ddd', borderRadius: 4 }}>
616
- <WorkflowSequenceDiagram
617
- scenario={orderProcessingScenario}
618
- height="100%"
619
- layoutOptions={{
620
- openedNamespaces: allTopSegments,
621
- laneWidth: 180,
622
- eventSpacing: 60,
623
- }}
624
- />
625
- </div>
626
- </div>
627
- </div>
628
- );
629
- },
630
- };
631
-
632
- /**
633
- * Custom layout options - compact view
634
- */
635
- export const CompactLayout: Story = {
636
- args: {
637
- scenario: simpleAuthScenario,
638
- height: '100%',
639
- layoutOptions: {
640
- laneWidth: 150,
641
- laneGap: 30,
642
- eventSpacing: 50,
643
- },
644
- },
645
- };
646
-
647
- /**
648
- * Custom layout options - spacious view
649
- */
650
- export const SpaciousLayout: Story = {
651
- args: {
652
- scenario: simpleAuthScenario,
653
- height: '100%',
654
- layoutOptions: {
655
- laneWidth: 250,
656
- laneGap: 100,
657
- eventSpacing: 120,
658
- },
659
- },
660
- };
661
-
662
- /**
663
- * Without controls for embedded use
664
- */
665
- export const WithoutControls: Story = {
666
- args: {
667
- scenario: simpleAuthScenario,
668
- height: '100%',
669
- showControls: false,
670
- layoutOptions: {
671
- eventSpacing: 80,
672
- },
673
- },
674
- };
675
-
676
- /**
677
- * With background grid
678
- */
679
- export const WithBackground: Story = {
680
- args: {
681
- scenario: simpleAuthScenario,
682
- height: '100%',
683
- showBackground: true,
684
- layoutOptions: {
685
- eventSpacing: 80,
686
- },
687
- },
688
- };
689
-
690
- /**
691
- * Full-featured example with all options
692
- * Demonstrates event navigation, selection, and visual customization
693
- */
694
- export const FullFeatured: Story = {
695
- render: () => {
696
- const [selectedIndex, setSelectedIndex] = useState<number>(0);
697
- const [showControls, setShowControls] = useState(true);
698
- const [showBackground, setShowBackground] = useState(false);
699
-
700
- const eventNames = Object.keys(orderProcessingScenario.template.events);
701
- const totalEvents = eventNames.length;
702
-
703
- return (
704
- <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
705
- <div
706
- style={{
707
- padding: '16px',
708
- background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
709
- color: 'white',
710
- borderBottom: '4px solid #5a67d8',
711
- marginBottom: 16,
712
- }}
713
- >
714
- <div style={{ marginBottom: 12, fontSize: 15 }}>
715
- <strong>📍 Current Event:</strong>{' '}
716
- <span style={{ fontFamily: 'monospace', background: 'rgba(255,255,255,0.2)', padding: '4px 8px', borderRadius: 3 }}>
717
- {selectedIndex + 1} / {totalEvents}
718
- </span>
719
- {' - '}
720
- <span style={{ fontFamily: 'monospace' }}>{eventNames[selectedIndex]}</span>
721
- </div>
722
-
723
- <div style={{ display: 'flex', gap: 16, alignItems: 'center', flexWrap: 'wrap' }}>
724
- <div style={{ display: 'flex', gap: 8 }}>
725
- <button
726
- onClick={() => setSelectedIndex(Math.max(0, selectedIndex - 1))}
727
- disabled={selectedIndex === 0}
728
- style={{
729
- padding: '8px 16px',
730
- cursor: selectedIndex === 0 ? 'not-allowed' : 'pointer',
731
- background: selectedIndex === 0 ? '#ccc' : '#3498db',
732
- color: 'white',
733
- border: 'none',
734
- borderRadius: 4,
735
- fontSize: 13,
736
- fontWeight: 'bold',
737
- }}
738
- >
739
- ← Previous
740
- </button>
741
- <button
742
- onClick={() => setSelectedIndex(Math.min(totalEvents - 1, selectedIndex + 1))}
743
- disabled={selectedIndex === totalEvents - 1}
744
- style={{
745
- padding: '8px 16px',
746
- cursor: selectedIndex === totalEvents - 1 ? 'not-allowed' : 'pointer',
747
- background: selectedIndex === totalEvents - 1 ? '#ccc' : '#3498db',
748
- color: 'white',
749
- border: 'none',
750
- borderRadius: 4,
751
- fontSize: 13,
752
- fontWeight: 'bold',
753
- }}
754
- >
755
- Next →
756
- </button>
757
- </div>
758
-
759
- <div style={{ borderLeft: '2px solid rgba(255,255,255,0.3)', paddingLeft: 16, display: 'flex', gap: 12 }}>
760
- <label style={{ cursor: 'pointer', fontSize: 13 }}>
761
- <input
762
- type="checkbox"
763
- checked={showControls}
764
- onChange={(e) => setShowControls(e.target.checked)}
765
- style={{ marginRight: 6 }}
766
- />
767
- Show Controls
768
- </label>
769
- <label style={{ cursor: 'pointer', fontSize: 13 }}>
770
- <input
771
- type="checkbox"
772
- checked={showBackground}
773
- onChange={(e) => setShowBackground(e.target.checked)}
774
- style={{ marginRight: 6 }}
775
- />
776
- Show Background
777
- </label>
778
- </div>
779
- </div>
780
-
781
- <div style={{ marginTop: 12, fontSize: 12, opacity: 0.9, background: 'rgba(0,0,0,0.2)', padding: '8px', borderRadius: 4 }}>
782
- 💡 <strong>Tip:</strong> Click directly on any event dot in the diagram to jump to it!
783
- </div>
784
- </div>
785
- <div style={{ flex: 1 }}>
786
- <WorkflowSequenceDiagram
787
- scenario={orderProcessingScenario}
788
- height="100%"
789
- selectedEventIndex={selectedIndex}
790
- onEventIndexChange={setSelectedIndex}
791
- showControls={showControls}
792
- showBackground={showBackground}
793
- layoutOptions={{
794
- laneWidth: 200,
795
- eventSpacing: 70,
796
- }}
797
- />
798
- </div>
799
- </div>
800
- );
801
- },
802
- };