@principal-ai/principal-view-react 0.6.6

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 (96) hide show
  1. package/README.md +111 -0
  2. package/dist/components/ConfigurationSelector.d.ts +37 -0
  3. package/dist/components/ConfigurationSelector.d.ts.map +1 -0
  4. package/dist/components/ConfigurationSelector.js +67 -0
  5. package/dist/components/ConfigurationSelector.js.map +1 -0
  6. package/dist/components/EdgeInfoPanel.d.ts +16 -0
  7. package/dist/components/EdgeInfoPanel.d.ts.map +1 -0
  8. package/dist/components/EdgeInfoPanel.js +85 -0
  9. package/dist/components/EdgeInfoPanel.js.map +1 -0
  10. package/dist/components/EventLog.d.ts +20 -0
  11. package/dist/components/EventLog.d.ts.map +1 -0
  12. package/dist/components/EventLog.js +13 -0
  13. package/dist/components/EventLog.js.map +1 -0
  14. package/dist/components/EventLog.test.d.ts +2 -0
  15. package/dist/components/EventLog.test.d.ts.map +1 -0
  16. package/dist/components/EventLog.test.js +73 -0
  17. package/dist/components/EventLog.test.js.map +1 -0
  18. package/dist/components/GraphRenderer.d.ts +121 -0
  19. package/dist/components/GraphRenderer.d.ts.map +1 -0
  20. package/dist/components/GraphRenderer.js +809 -0
  21. package/dist/components/GraphRenderer.js.map +1 -0
  22. package/dist/components/GraphRenderer.test.d.ts +2 -0
  23. package/dist/components/GraphRenderer.test.d.ts.map +1 -0
  24. package/dist/components/GraphRenderer.test.js +88 -0
  25. package/dist/components/GraphRenderer.test.js.map +1 -0
  26. package/dist/components/MetricsDashboard.d.ts +14 -0
  27. package/dist/components/MetricsDashboard.d.ts.map +1 -0
  28. package/dist/components/MetricsDashboard.js +13 -0
  29. package/dist/components/MetricsDashboard.js.map +1 -0
  30. package/dist/components/NodeInfoPanel.d.ts +21 -0
  31. package/dist/components/NodeInfoPanel.d.ts.map +1 -0
  32. package/dist/components/NodeInfoPanel.js +217 -0
  33. package/dist/components/NodeInfoPanel.js.map +1 -0
  34. package/dist/edges/CustomEdge.d.ts +16 -0
  35. package/dist/edges/CustomEdge.d.ts.map +1 -0
  36. package/dist/edges/CustomEdge.js +200 -0
  37. package/dist/edges/CustomEdge.js.map +1 -0
  38. package/dist/edges/GenericEdge.d.ts +18 -0
  39. package/dist/edges/GenericEdge.d.ts.map +1 -0
  40. package/dist/edges/GenericEdge.js +14 -0
  41. package/dist/edges/GenericEdge.js.map +1 -0
  42. package/dist/hooks/usePathBasedEvents.d.ts +42 -0
  43. package/dist/hooks/usePathBasedEvents.d.ts.map +1 -0
  44. package/dist/hooks/usePathBasedEvents.js +122 -0
  45. package/dist/hooks/usePathBasedEvents.js.map +1 -0
  46. package/dist/index.d.ts +33 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +41 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/nodes/CustomNode.d.ts +18 -0
  51. package/dist/nodes/CustomNode.d.ts.map +1 -0
  52. package/dist/nodes/CustomNode.js +298 -0
  53. package/dist/nodes/CustomNode.js.map +1 -0
  54. package/dist/nodes/GenericNode.d.ts +20 -0
  55. package/dist/nodes/GenericNode.d.ts.map +1 -0
  56. package/dist/nodes/GenericNode.js +24 -0
  57. package/dist/nodes/GenericNode.js.map +1 -0
  58. package/dist/utils/animationMapping.d.ts +53 -0
  59. package/dist/utils/animationMapping.d.ts.map +1 -0
  60. package/dist/utils/animationMapping.js +133 -0
  61. package/dist/utils/animationMapping.js.map +1 -0
  62. package/dist/utils/graphConverter.d.ts +22 -0
  63. package/dist/utils/graphConverter.d.ts.map +1 -0
  64. package/dist/utils/graphConverter.js +176 -0
  65. package/dist/utils/graphConverter.js.map +1 -0
  66. package/dist/utils/iconResolver.d.ts +29 -0
  67. package/dist/utils/iconResolver.d.ts.map +1 -0
  68. package/dist/utils/iconResolver.js +68 -0
  69. package/dist/utils/iconResolver.js.map +1 -0
  70. package/package.json +61 -0
  71. package/src/components/ConfigurationSelector.tsx +147 -0
  72. package/src/components/EdgeInfoPanel.tsx +198 -0
  73. package/src/components/EventLog.test.tsx +85 -0
  74. package/src/components/EventLog.tsx +51 -0
  75. package/src/components/GraphRenderer.test.tsx +118 -0
  76. package/src/components/GraphRenderer.tsx +1222 -0
  77. package/src/components/MetricsDashboard.tsx +40 -0
  78. package/src/components/NodeInfoPanel.tsx +425 -0
  79. package/src/edges/CustomEdge.tsx +344 -0
  80. package/src/edges/GenericEdge.tsx +40 -0
  81. package/src/hooks/usePathBasedEvents.ts +182 -0
  82. package/src/index.ts +67 -0
  83. package/src/nodes/CustomNode.tsx +432 -0
  84. package/src/nodes/GenericNode.tsx +54 -0
  85. package/src/stories/AnimationWorkshop.stories.tsx +608 -0
  86. package/src/stories/EventDrivenAnimations.stories.tsx +499 -0
  87. package/src/stories/EventLog.stories.tsx +161 -0
  88. package/src/stories/GraphRenderer.stories.tsx +628 -0
  89. package/src/stories/Introduction.mdx +51 -0
  90. package/src/stories/MetricsDashboard.stories.tsx +227 -0
  91. package/src/stories/MultiConfig.stories.tsx +531 -0
  92. package/src/stories/MultiDirectionalConnections.stories.tsx +345 -0
  93. package/src/stories/NodeShapes.stories.tsx +769 -0
  94. package/src/utils/animationMapping.ts +170 -0
  95. package/src/utils/graphConverter.ts +218 -0
  96. package/src/utils/iconResolver.tsx +49 -0
@@ -0,0 +1,499 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { GraphRenderer } from '../components/GraphRenderer';
3
+ import type { GraphEvent, ExtendedCanvas } from '@principal-ai/principal-view-core';
4
+ import { useState, useEffect } from 'react';
5
+ import React from 'react';
6
+
7
+ const meta = {
8
+ title: 'Workshop/Event-Driven Animations',
9
+ component: GraphRenderer,
10
+ parameters: {
11
+ layout: 'fullscreen',
12
+ },
13
+ tags: ['autodocs'],
14
+ } satisfies Meta<typeof GraphRenderer>;
15
+
16
+ export default meta;
17
+ type Story = StoryObj<typeof meta>;
18
+
19
+ // Sample canvas for event-driven animations
20
+ const sampleCanvas: ExtendedCanvas = {
21
+ nodes: [
22
+ {
23
+ id: 'node-1',
24
+ type: 'text',
25
+ x: 100,
26
+ y: 100,
27
+ width: 140,
28
+ height: 70,
29
+ text: 'Source',
30
+ color: '#4A90E2',
31
+ vv: {
32
+ nodeType: 'process',
33
+ shape: 'rectangle',
34
+ icon: 'Settings',
35
+ states: {
36
+ idle: { label: 'Idle', color: '#888' },
37
+ processing: { label: 'Processing', color: '#4A90E2' },
38
+ completed: { label: 'Completed', color: '#10B981' },
39
+ error: { label: 'Error', color: '#EF4444' },
40
+ },
41
+ },
42
+ },
43
+ {
44
+ id: 'node-2',
45
+ type: 'text',
46
+ x: 300,
47
+ y: 100,
48
+ width: 140,
49
+ height: 70,
50
+ text: 'Processor',
51
+ color: '#4A90E2',
52
+ vv: {
53
+ nodeType: 'process',
54
+ shape: 'rectangle',
55
+ icon: 'Settings',
56
+ states: {
57
+ idle: { label: 'Idle', color: '#888' },
58
+ processing: { label: 'Processing', color: '#4A90E2' },
59
+ completed: { label: 'Completed', color: '#10B981' },
60
+ error: { label: 'Error', color: '#EF4444' },
61
+ },
62
+ },
63
+ },
64
+ {
65
+ id: 'node-3',
66
+ type: 'text',
67
+ x: 500,
68
+ y: 100,
69
+ width: 100,
70
+ height: 100,
71
+ text: 'Storage',
72
+ color: '#7B68EE',
73
+ vv: {
74
+ nodeType: 'data',
75
+ shape: 'circle',
76
+ icon: 'Database',
77
+ },
78
+ },
79
+ ],
80
+ edges: [
81
+ {
82
+ id: 'edge-1',
83
+ fromNode: 'node-1',
84
+ toNode: 'node-2',
85
+ vv: { edgeType: 'dataflow' },
86
+ },
87
+ {
88
+ id: 'edge-2',
89
+ fromNode: 'node-2',
90
+ toNode: 'node-3',
91
+ vv: { edgeType: 'dataflow' },
92
+ },
93
+ ],
94
+ vv: {
95
+ version: '1.0.0',
96
+ name: 'Event-Driven Animation Demo',
97
+ description: 'Demonstrates event-driven animations',
98
+ edgeTypes: {
99
+ dataflow: {
100
+ style: 'solid',
101
+ color: '#50E3C2',
102
+ directed: true,
103
+ },
104
+ },
105
+ },
106
+ };
107
+
108
+ /**
109
+ * Story: Automatic Event Sequence
110
+ * Demonstrates automatic event playback with animations
111
+ */
112
+ export const AutomaticEventSequence: Story = {
113
+ render: () => {
114
+ const [events, setEvents] = useState<GraphEvent[]>([]);
115
+ const [eventLog, setEventLog] = useState<string[]>([]);
116
+
117
+ useEffect(() => {
118
+ // Define event sequence
119
+ const eventSequence: Array<{ delay: number; event: GraphEvent }> = [
120
+ {
121
+ delay: 500,
122
+ event: {
123
+ id: 'evt-1',
124
+ type: 'edge_animated',
125
+ timestamp: Date.now(),
126
+ category: 'edge',
127
+ operation: 'animate',
128
+ payload: {
129
+ operation: 'animate',
130
+ edgeId: 'edge-1',
131
+ edgeType: 'dataflow',
132
+ from: 'node-1',
133
+ to: 'node-2',
134
+ animation: {
135
+ duration: 1000,
136
+ direction: 'forward' as const,
137
+ },
138
+ },
139
+ },
140
+ },
141
+ {
142
+ delay: 1500,
143
+ event: {
144
+ id: 'evt-2',
145
+ type: 'state_changed',
146
+ timestamp: Date.now(),
147
+ category: 'state',
148
+ operation: 'update',
149
+ payload: {
150
+ nodeId: 'node-2',
151
+ newState: 'processing',
152
+ },
153
+ },
154
+ },
155
+ {
156
+ delay: 3000,
157
+ event: {
158
+ id: 'evt-3',
159
+ type: 'edge_animated',
160
+ timestamp: Date.now(),
161
+ category: 'edge',
162
+ operation: 'animate',
163
+ payload: {
164
+ operation: 'animate',
165
+ edgeId: 'edge-2',
166
+ edgeType: 'dataflow',
167
+ from: 'node-2',
168
+ to: 'node-3',
169
+ animation: {
170
+ duration: 1000,
171
+ direction: 'forward' as const,
172
+ },
173
+ },
174
+ },
175
+ },
176
+ {
177
+ delay: 4000,
178
+ event: {
179
+ id: 'evt-4',
180
+ type: 'state_changed',
181
+ timestamp: Date.now(),
182
+ category: 'state',
183
+ operation: 'update',
184
+ payload: {
185
+ nodeId: 'node-2',
186
+ newState: 'completed',
187
+ },
188
+ },
189
+ },
190
+ ];
191
+
192
+ // Play events with delays
193
+ eventSequence.forEach(({ delay, event }) => {
194
+ setTimeout(() => {
195
+ setEvents((prev) => [...prev, event]);
196
+ setEventLog((prev) => [...prev, `${event.type} at ${delay}ms`]);
197
+ }, delay);
198
+ });
199
+
200
+ // Loop the sequence
201
+ const loopInterval = setInterval(() => {
202
+ setEvents([]);
203
+ setEventLog(['--- Sequence restart ---']);
204
+ eventSequence.forEach(({ delay, event }) => {
205
+ setTimeout(() => {
206
+ setEvents((prev) => [...prev, event]);
207
+ setEventLog((prev) => [...prev, `${event.type} at ${delay}ms`]);
208
+ }, delay);
209
+ });
210
+ }, 5000);
211
+
212
+ return () => clearInterval(loopInterval);
213
+ }, []);
214
+
215
+ return (
216
+ <div style={{ display: 'flex', height: '100vh' }}>
217
+ <div style={{ flex: 1 }}>
218
+ <GraphRenderer
219
+ canvas={sampleCanvas}
220
+ events={events}
221
+ width="100%"
222
+ height="100%"
223
+ onEventProcessed={(event) => {
224
+ console.log('Event processed:', event);
225
+ }}
226
+ />
227
+ </div>
228
+ <div
229
+ style={{
230
+ width: '300px',
231
+ backgroundColor: '#f5f5f5',
232
+ padding: '16px',
233
+ overflowY: 'auto',
234
+ borderLeft: '1px solid #ddd',
235
+ }}
236
+ >
237
+ <h3 style={{ marginTop: 0 }}>Event Log</h3>
238
+ <div style={{ fontSize: '12px', fontFamily: 'monospace' }}>
239
+ {eventLog.map((log, idx) => (
240
+ <div key={idx} style={{ padding: '4px 0', borderBottom: '1px solid #e0e0e0' }}>
241
+ {log}
242
+ </div>
243
+ ))}
244
+ </div>
245
+ </div>
246
+ </div>
247
+ );
248
+ },
249
+ };
250
+
251
+ /**
252
+ * Story: Interactive Event Triggers
253
+ * Allows user to manually trigger animation events
254
+ */
255
+ export const InteractiveEventTriggers: Story = {
256
+ render: () => {
257
+ const [events, setEvents] = useState<GraphEvent[]>([]);
258
+
259
+ const triggerEdgeAnimation = (edgeId: string) => {
260
+ const event: GraphEvent = {
261
+ id: `evt-${Date.now()}`,
262
+ type: 'edge_animated',
263
+ timestamp: Date.now(),
264
+ category: 'edge',
265
+ operation: 'animate',
266
+ payload: {
267
+ operation: 'animate',
268
+ edgeId,
269
+ edgeType: 'dataflow',
270
+ from: edgeId === 'edge-1' ? 'node-1' : 'node-2',
271
+ to: edgeId === 'edge-1' ? 'node-2' : 'node-3',
272
+ animation: {
273
+ duration: 1000,
274
+ direction: 'forward' as const,
275
+ },
276
+ },
277
+ };
278
+ setEvents((prev) => [...prev, event]);
279
+ };
280
+
281
+ const triggerNodeState = (nodeId: string, state: string) => {
282
+ const event: GraphEvent = {
283
+ id: `evt-${Date.now()}`,
284
+ type: 'state_changed',
285
+ timestamp: Date.now(),
286
+ category: 'state',
287
+ operation: 'update',
288
+ payload: {
289
+ nodeId,
290
+ newState: state,
291
+ },
292
+ };
293
+ setEvents((prev) => [...prev, event]);
294
+ };
295
+
296
+ return (
297
+ <div style={{ display: 'flex', height: '100vh' }}>
298
+ <div style={{ flex: 1 }}>
299
+ <GraphRenderer canvas={sampleCanvas} events={events} width="100%" height="100%" />
300
+ </div>
301
+ <div
302
+ style={{
303
+ width: '300px',
304
+ backgroundColor: '#f5f5f5',
305
+ padding: '16px',
306
+ overflowY: 'auto',
307
+ borderLeft: '1px solid #ddd',
308
+ }}
309
+ >
310
+ <h3 style={{ marginTop: 0 }}>Event Controls</h3>
311
+
312
+ <div style={{ marginBottom: '24px' }}>
313
+ <h4>Edge Animations</h4>
314
+ <button
315
+ onClick={() => triggerEdgeAnimation('edge-1')}
316
+ style={{
317
+ width: '100%',
318
+ padding: '8px',
319
+ marginBottom: '8px',
320
+ backgroundColor: '#50E3C2',
321
+ border: 'none',
322
+ borderRadius: '4px',
323
+ cursor: 'pointer',
324
+ }}
325
+ >
326
+ Animate Edge 1
327
+ </button>
328
+ <button
329
+ onClick={() => triggerEdgeAnimation('edge-2')}
330
+ style={{
331
+ width: '100%',
332
+ padding: '8px',
333
+ backgroundColor: '#50E3C2',
334
+ border: 'none',
335
+ borderRadius: '4px',
336
+ cursor: 'pointer',
337
+ }}
338
+ >
339
+ Animate Edge 2
340
+ </button>
341
+ </div>
342
+
343
+ <div>
344
+ <h4>Node States</h4>
345
+ <button
346
+ onClick={() => triggerNodeState('node-2', 'processing')}
347
+ style={{
348
+ width: '100%',
349
+ padding: '8px',
350
+ marginBottom: '8px',
351
+ backgroundColor: '#4A90E2',
352
+ color: 'white',
353
+ border: 'none',
354
+ borderRadius: '4px',
355
+ cursor: 'pointer',
356
+ }}
357
+ >
358
+ Set Node 2: Processing
359
+ </button>
360
+ <button
361
+ onClick={() => triggerNodeState('node-2', 'completed')}
362
+ style={{
363
+ width: '100%',
364
+ padding: '8px',
365
+ marginBottom: '8px',
366
+ backgroundColor: '#10B981',
367
+ color: 'white',
368
+ border: 'none',
369
+ borderRadius: '4px',
370
+ cursor: 'pointer',
371
+ }}
372
+ >
373
+ Set Node 2: Completed
374
+ </button>
375
+ <button
376
+ onClick={() => triggerNodeState('node-2', 'error')}
377
+ style={{
378
+ width: '100%',
379
+ padding: '8px',
380
+ backgroundColor: '#EF4444',
381
+ color: 'white',
382
+ border: 'none',
383
+ borderRadius: '4px',
384
+ cursor: 'pointer',
385
+ }}
386
+ >
387
+ Set Node 2: Error
388
+ </button>
389
+ </div>
390
+ </div>
391
+ </div>
392
+ );
393
+ },
394
+ };
395
+
396
+ /**
397
+ * Story: Pipeline Processing Flow
398
+ * Shows a realistic data pipeline with cascading animations
399
+ */
400
+ export const PipelineProcessingFlow: Story = {
401
+ render: () => {
402
+ const [events, setEvents] = useState<GraphEvent[]>([]);
403
+ const [isRunning, setIsRunning] = useState(false);
404
+
405
+ const runPipeline = () => {
406
+ setIsRunning(true);
407
+ setEvents([]);
408
+
409
+ // Simulate pipeline execution
410
+ const sequence = [
411
+ { delay: 0, type: 'edge', target: 'edge-1' },
412
+ { delay: 800, type: 'state', target: 'node-2', state: 'processing' },
413
+ { delay: 2500, type: 'edge', target: 'edge-2' },
414
+ { delay: 3200, type: 'state', target: 'node-2', state: 'completed' },
415
+ ];
416
+
417
+ sequence.forEach(({ delay, type, target, state }) => {
418
+ setTimeout(() => {
419
+ if (type === 'edge') {
420
+ setEvents((prev) => [
421
+ ...prev,
422
+ {
423
+ id: `evt-${Date.now()}`,
424
+ type: 'edge_animated',
425
+ timestamp: Date.now(),
426
+ category: 'edge',
427
+ operation: 'animate',
428
+ payload: {
429
+ operation: 'animate',
430
+ edgeId: target,
431
+ edgeType: 'dataflow',
432
+ from: target === 'edge-1' ? 'node-1' : 'node-2',
433
+ to: target === 'edge-1' ? 'node-2' : 'node-3',
434
+ animation: {
435
+ duration: 1000,
436
+ direction: 'forward' as const,
437
+ },
438
+ },
439
+ },
440
+ ]);
441
+ } else if (type === 'state' && state) {
442
+ setEvents((prev) => [
443
+ ...prev,
444
+ {
445
+ id: `evt-${Date.now()}`,
446
+ type: 'state_changed',
447
+ timestamp: Date.now(),
448
+ category: 'state',
449
+ operation: 'update',
450
+ payload: {
451
+ nodeId: target,
452
+ newState: state,
453
+ },
454
+ },
455
+ ]);
456
+ }
457
+ }, delay);
458
+ });
459
+
460
+ setTimeout(() => setIsRunning(false), 4000);
461
+ };
462
+
463
+ return (
464
+ <div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
465
+ <div
466
+ style={{
467
+ padding: '16px',
468
+ backgroundColor: '#f5f5f5',
469
+ borderBottom: '1px solid #ddd',
470
+ display: 'flex',
471
+ justifyContent: 'space-between',
472
+ alignItems: 'center',
473
+ }}
474
+ >
475
+ <h3 style={{ margin: 0 }}>Pipeline Processing Flow</h3>
476
+ <button
477
+ onClick={runPipeline}
478
+ disabled={isRunning}
479
+ style={{
480
+ padding: '12px 24px',
481
+ backgroundColor: isRunning ? '#ccc' : '#4A90E2',
482
+ color: 'white',
483
+ border: 'none',
484
+ borderRadius: '4px',
485
+ fontSize: '14px',
486
+ fontWeight: 'bold',
487
+ cursor: isRunning ? 'not-allowed' : 'pointer',
488
+ }}
489
+ >
490
+ {isRunning ? 'Running...' : 'Run Pipeline'}
491
+ </button>
492
+ </div>
493
+ <div style={{ flex: 1 }}>
494
+ <GraphRenderer canvas={sampleCanvas} events={events} width="100%" height="100%" />
495
+ </div>
496
+ </div>
497
+ );
498
+ },
499
+ };
@@ -0,0 +1,161 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { EventLog } from '../components/EventLog';
3
+ import type { GraphEvent, Violation } from '@principal-ai/principal-view-core';
4
+
5
+ const meta = {
6
+ title: 'Components/EventLog',
7
+ component: EventLog,
8
+ parameters: {
9
+ layout: 'centered',
10
+ },
11
+ tags: ['autodocs'],
12
+ } satisfies Meta<typeof EventLog>;
13
+
14
+ export default meta;
15
+ type Story = StoryObj<typeof meta>;
16
+
17
+ // Sample events
18
+ const sampleEvents: GraphEvent[] = [
19
+ {
20
+ id: 'evt-1',
21
+ timestamp: Date.now() - 5000,
22
+ type: 'node_added',
23
+ category: 'node',
24
+ operation: 'create',
25
+ data: {
26
+ nodeId: 'node-1',
27
+ nodeType: 'process',
28
+ },
29
+ metadata: {},
30
+ },
31
+ {
32
+ id: 'evt-2',
33
+ timestamp: Date.now() - 4000,
34
+ type: 'node_added',
35
+ category: 'node',
36
+ operation: 'create',
37
+ data: {
38
+ nodeId: 'node-2',
39
+ nodeType: 'data',
40
+ },
41
+ metadata: {},
42
+ },
43
+ {
44
+ id: 'evt-3',
45
+ timestamp: Date.now() - 3000,
46
+ type: 'edge_added',
47
+ category: 'edge',
48
+ operation: 'create',
49
+ data: {
50
+ edgeId: 'edge-1',
51
+ source: 'node-1',
52
+ target: 'node-2',
53
+ },
54
+ metadata: {},
55
+ },
56
+ {
57
+ id: 'evt-4',
58
+ timestamp: Date.now() - 2000,
59
+ type: 'node_updated',
60
+ category: 'node',
61
+ operation: 'update',
62
+ data: {
63
+ nodeId: 'node-1',
64
+ changes: { label: 'Updated Processor' },
65
+ },
66
+ metadata: {},
67
+ },
68
+ {
69
+ id: 'evt-5',
70
+ timestamp: Date.now() - 1000,
71
+ type: 'validation_run',
72
+ category: 'validation',
73
+ operation: 'read',
74
+ data: {
75
+ violations: 2,
76
+ warnings: 1,
77
+ },
78
+ metadata: {},
79
+ },
80
+ ];
81
+
82
+ // Sample violations
83
+ const sampleViolations: Violation[] = [
84
+ {
85
+ id: 'viol-1',
86
+ type: 'connection',
87
+ severity: 'error',
88
+ message: 'Invalid connection between incompatible node types',
89
+ context: {
90
+ nodeId: 'node-1',
91
+ edgeId: 'edge-1',
92
+ },
93
+ ruleId: 'conn-rule-1',
94
+ timestamp: Date.now() - 1000,
95
+ },
96
+ {
97
+ id: 'viol-2',
98
+ type: 'structure',
99
+ severity: 'warning',
100
+ message: 'Node exceeds maximum connection limit',
101
+ context: {
102
+ nodeId: 'node-2',
103
+ },
104
+ ruleId: 'struct-rule-1',
105
+ timestamp: Date.now() - 500,
106
+ },
107
+ ];
108
+
109
+ export const Default: Story = {
110
+ args: {
111
+ events: sampleEvents,
112
+ violations: sampleViolations,
113
+ },
114
+ };
115
+
116
+ export const EventsOnly: Story = {
117
+ args: {
118
+ events: sampleEvents,
119
+ violations: [],
120
+ },
121
+ };
122
+
123
+ export const EmptyLog: Story = {
124
+ args: {
125
+ events: [],
126
+ violations: [],
127
+ },
128
+ };
129
+
130
+ export const ManyEvents: Story = {
131
+ args: {
132
+ events: [
133
+ ...sampleEvents,
134
+ ...Array.from({ length: 20 }, (_, i) => ({
135
+ id: `evt-gen-${i}`,
136
+ timestamp: Date.now() - (i * 500),
137
+ type: 'node_updated',
138
+ category: 'node' as const,
139
+ operation: 'update' as const,
140
+ data: {
141
+ nodeId: `node-${i}`,
142
+ changes: {},
143
+ },
144
+ metadata: {},
145
+ })),
146
+ ],
147
+ violations: sampleViolations,
148
+ maxHeight: '500px',
149
+ },
150
+ };
151
+
152
+ export const WithClickHandler: Story = {
153
+ args: {
154
+ events: sampleEvents,
155
+ violations: sampleViolations,
156
+ onEventClick: (event) => {
157
+ console.log('Event clicked:', event);
158
+ alert(`Event clicked: ${event.type}`);
159
+ },
160
+ },
161
+ };