@principal-ai/principal-view-react 0.14.14 → 0.14.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/SequenceDiagramRenderer.d.ts +61 -0
- package/dist/components/SequenceDiagramRenderer.d.ts.map +1 -0
- package/dist/components/SequenceDiagramRenderer.js +184 -0
- package/dist/components/SequenceDiagramRenderer.js.map +1 -0
- package/dist/components/dashboard/DashboardRenderer.d.ts +9 -0
- package/dist/components/dashboard/DashboardRenderer.d.ts.map +1 -0
- package/dist/components/dashboard/DashboardRenderer.js +179 -0
- package/dist/components/dashboard/DashboardRenderer.js.map +1 -0
- package/dist/components/dashboard/MetricPanel.d.ts +9 -0
- package/dist/components/dashboard/MetricPanel.d.ts.map +1 -0
- package/dist/components/dashboard/MetricPanel.js +103 -0
- package/dist/components/dashboard/MetricPanel.js.map +1 -0
- package/dist/components/dashboard/MockDataProvider.d.ts +30 -0
- package/dist/components/dashboard/MockDataProvider.d.ts.map +1 -0
- package/dist/components/dashboard/MockDataProvider.js +270 -0
- package/dist/components/dashboard/MockDataProvider.js.map +1 -0
- package/dist/components/dashboard/components/BarChart.d.ts +9 -0
- package/dist/components/dashboard/components/BarChart.d.ts.map +1 -0
- package/dist/components/dashboard/components/BarChart.js +167 -0
- package/dist/components/dashboard/components/BarChart.js.map +1 -0
- package/dist/components/dashboard/components/LineChart.d.ts +9 -0
- package/dist/components/dashboard/components/LineChart.d.ts.map +1 -0
- package/dist/components/dashboard/components/LineChart.js +141 -0
- package/dist/components/dashboard/components/LineChart.js.map +1 -0
- package/dist/components/dashboard/components/MetricCard.d.ts +8 -0
- package/dist/components/dashboard/components/MetricCard.d.ts.map +1 -0
- package/dist/components/dashboard/components/MetricCard.js +163 -0
- package/dist/components/dashboard/components/MetricCard.js.map +1 -0
- package/dist/components/dashboard/components/SourceLink.d.ts +8 -0
- package/dist/components/dashboard/components/SourceLink.d.ts.map +1 -0
- package/dist/components/dashboard/components/SourceLink.js +39 -0
- package/dist/components/dashboard/components/SourceLink.js.map +1 -0
- package/dist/components/dashboard/components/TimeRangeSelector.d.ts +8 -0
- package/dist/components/dashboard/components/TimeRangeSelector.d.ts.map +1 -0
- package/dist/components/dashboard/components/TimeRangeSelector.js +167 -0
- package/dist/components/dashboard/components/TimeRangeSelector.js.map +1 -0
- package/dist/components/dashboard/components/index.d.ts +6 -0
- package/dist/components/dashboard/components/index.d.ts.map +1 -0
- package/dist/components/dashboard/components/index.js +6 -0
- package/dist/components/dashboard/components/index.js.map +1 -0
- package/dist/components/dashboard/index.d.ts +6 -0
- package/dist/components/dashboard/index.d.ts.map +1 -0
- package/dist/components/dashboard/index.js +8 -0
- package/dist/components/dashboard/index.js.map +1 -0
- package/dist/components/dashboard/types.d.ts +74 -0
- package/dist/components/dashboard/types.d.ts.map +1 -0
- package/dist/components/dashboard/types.js +8 -0
- package/dist/components/dashboard/types.js.map +1 -0
- package/dist/hooks/useSequenceLayout.d.ts +148 -0
- package/dist/hooks/useSequenceLayout.d.ts.map +1 -0
- package/dist/hooks/useSequenceLayout.js +225 -0
- package/dist/hooks/useSequenceLayout.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/SequenceDiagramRenderer.tsx +459 -0
- package/src/components/dashboard/DashboardRenderer.tsx +317 -0
- package/src/components/dashboard/MetricPanel.tsx +254 -0
- package/src/components/dashboard/MockDataProvider.ts +330 -0
- package/src/components/dashboard/components/BarChart.tsx +299 -0
- package/src/components/dashboard/components/LineChart.tsx +279 -0
- package/src/components/dashboard/components/MetricCard.tsx +270 -0
- package/src/components/dashboard/components/SourceLink.tsx +63 -0
- package/src/components/dashboard/components/TimeRangeSelector.tsx +280 -0
- package/src/components/dashboard/components/index.ts +5 -0
- package/src/components/dashboard/index.ts +47 -0
- package/src/components/dashboard/types.ts +126 -0
- package/src/hooks/useSequenceLayout.ts +413 -0
- package/src/index.ts +62 -0
- package/src/stories/SequenceDiagram.stories.tsx +306 -0
- package/src/stories/dashboard/DashboardRenderer.stories.tsx +263 -0
- package/src/stories/dashboard/sample-dashboards/activity-feed-analytics.dashboard.json +300 -0
- package/src/stories/data/graph-converter-test-execution.json +50 -50
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sequence Diagram Renderer
|
|
3
|
+
*
|
|
4
|
+
* Renders events in swimlane-based sequence diagram layout.
|
|
5
|
+
* Uses namespaces to determine swimlanes and event order for vertical positioning.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useCallback, useMemo } from 'react';
|
|
9
|
+
import {
|
|
10
|
+
ReactFlow,
|
|
11
|
+
Background,
|
|
12
|
+
BackgroundVariant,
|
|
13
|
+
Controls,
|
|
14
|
+
ReactFlowProvider,
|
|
15
|
+
Panel,
|
|
16
|
+
useViewport,
|
|
17
|
+
Handle,
|
|
18
|
+
Position,
|
|
19
|
+
BaseEdge,
|
|
20
|
+
EdgeLabelRenderer,
|
|
21
|
+
getBezierPath,
|
|
22
|
+
type NodeTypes,
|
|
23
|
+
type EdgeTypes,
|
|
24
|
+
type NodeProps,
|
|
25
|
+
type EdgeProps,
|
|
26
|
+
} from '@xyflow/react';
|
|
27
|
+
import '@xyflow/react/dist/style.css';
|
|
28
|
+
import {
|
|
29
|
+
useSequenceLayout,
|
|
30
|
+
type SequenceEvent,
|
|
31
|
+
type SequenceEdge,
|
|
32
|
+
type UseSequenceLayoutOptions,
|
|
33
|
+
type Swimlane,
|
|
34
|
+
} from '../hooks/useSequenceLayout';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Minimal marker node for arrow-centric sequence diagrams
|
|
38
|
+
*/
|
|
39
|
+
function SequenceMarkerNode({ data }: NodeProps) {
|
|
40
|
+
return (
|
|
41
|
+
<div
|
|
42
|
+
style={{
|
|
43
|
+
width: '100%',
|
|
44
|
+
height: '100%',
|
|
45
|
+
backgroundColor: 'var(--sequence-marker-bg, #6495ED)',
|
|
46
|
+
borderRadius: '50%',
|
|
47
|
+
border: '2px solid var(--sequence-marker-border, #4169E1)',
|
|
48
|
+
boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
|
|
49
|
+
}}
|
|
50
|
+
title={data.fullName as string}
|
|
51
|
+
>
|
|
52
|
+
<Handle
|
|
53
|
+
type="target"
|
|
54
|
+
position={Position.Top}
|
|
55
|
+
style={{ visibility: 'hidden' }}
|
|
56
|
+
/>
|
|
57
|
+
<Handle
|
|
58
|
+
type="source"
|
|
59
|
+
position={Position.Bottom}
|
|
60
|
+
style={{ visibility: 'hidden' }}
|
|
61
|
+
/>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Sequence arrow edge with label
|
|
68
|
+
*/
|
|
69
|
+
function SequenceArrowEdge({
|
|
70
|
+
id,
|
|
71
|
+
sourceX,
|
|
72
|
+
sourceY,
|
|
73
|
+
targetX,
|
|
74
|
+
targetY,
|
|
75
|
+
sourcePosition,
|
|
76
|
+
targetPosition,
|
|
77
|
+
label,
|
|
78
|
+
data,
|
|
79
|
+
}: EdgeProps) {
|
|
80
|
+
// Use a straight line for same-lane, bezier for cross-lane
|
|
81
|
+
const isSameLane = !data?.crossesLanes;
|
|
82
|
+
|
|
83
|
+
const [edgePath, labelX, labelY] = getBezierPath({
|
|
84
|
+
sourceX,
|
|
85
|
+
sourceY,
|
|
86
|
+
sourcePosition,
|
|
87
|
+
targetX,
|
|
88
|
+
targetY,
|
|
89
|
+
targetPosition,
|
|
90
|
+
curvature: isSameLane ? 0.5 : 0.25,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<>
|
|
95
|
+
<BaseEdge
|
|
96
|
+
id={id}
|
|
97
|
+
path={edgePath}
|
|
98
|
+
style={{
|
|
99
|
+
stroke: 'var(--sequence-arrow-color, #4169E1)',
|
|
100
|
+
strokeWidth: 2,
|
|
101
|
+
}}
|
|
102
|
+
markerEnd="url(#sequence-arrow)"
|
|
103
|
+
/>
|
|
104
|
+
{label && (
|
|
105
|
+
<EdgeLabelRenderer>
|
|
106
|
+
<div
|
|
107
|
+
style={{
|
|
108
|
+
position: 'absolute',
|
|
109
|
+
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
|
110
|
+
background: 'var(--sequence-label-bg, rgba(255, 255, 255, 0.95))',
|
|
111
|
+
padding: '2px 8px',
|
|
112
|
+
borderRadius: 4,
|
|
113
|
+
fontSize: 12,
|
|
114
|
+
fontWeight: 500,
|
|
115
|
+
color: 'var(--sequence-label-text, #333)',
|
|
116
|
+
border: '1px solid var(--sequence-label-border, #ddd)',
|
|
117
|
+
pointerEvents: 'all',
|
|
118
|
+
whiteSpace: 'nowrap',
|
|
119
|
+
}}
|
|
120
|
+
className="nodrag nopan"
|
|
121
|
+
>
|
|
122
|
+
{label}
|
|
123
|
+
</div>
|
|
124
|
+
</EdgeLabelRenderer>
|
|
125
|
+
)}
|
|
126
|
+
</>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Default node types including sequence marker
|
|
132
|
+
*/
|
|
133
|
+
const defaultSequenceNodeTypes: NodeTypes = {
|
|
134
|
+
sequenceMarker: SequenceMarkerNode,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Default edge types including sequence arrow
|
|
139
|
+
*/
|
|
140
|
+
const defaultSequenceEdgeTypes: EdgeTypes = {
|
|
141
|
+
sequenceArrow: SequenceArrowEdge,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Props for the swimlane layer
|
|
146
|
+
*/
|
|
147
|
+
interface SwimlaneLayerProps {
|
|
148
|
+
swimlanes: Swimlane[];
|
|
149
|
+
laneWidth: number;
|
|
150
|
+
headerHeight: number;
|
|
151
|
+
totalHeight: number;
|
|
152
|
+
onToggleCollapse?: (namespace: string) => void;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Swimlane layer that renders behind nodes and transforms with viewport
|
|
157
|
+
*/
|
|
158
|
+
function SwimlaneLayer({
|
|
159
|
+
swimlanes,
|
|
160
|
+
laneWidth,
|
|
161
|
+
headerHeight,
|
|
162
|
+
totalHeight,
|
|
163
|
+
onToggleCollapse,
|
|
164
|
+
}: SwimlaneLayerProps) {
|
|
165
|
+
const { x, y, zoom } = useViewport();
|
|
166
|
+
|
|
167
|
+
// Calculate the visible area height (use a large value to ensure lanes extend)
|
|
168
|
+
const extendedHeight = Math.max(totalHeight, 2000);
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div
|
|
172
|
+
style={{
|
|
173
|
+
position: 'absolute',
|
|
174
|
+
top: 0,
|
|
175
|
+
left: 0,
|
|
176
|
+
transformOrigin: '0 0',
|
|
177
|
+
transform: `translate(${x}px, ${y}px) scale(${zoom})`,
|
|
178
|
+
pointerEvents: 'none',
|
|
179
|
+
zIndex: -1,
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
{/* Lane backgrounds */}
|
|
183
|
+
{swimlanes.map((lane, index) => {
|
|
184
|
+
const isEven = index % 2 === 0;
|
|
185
|
+
return (
|
|
186
|
+
<div
|
|
187
|
+
key={`bg-${lane.namespace}`}
|
|
188
|
+
style={{
|
|
189
|
+
position: 'absolute',
|
|
190
|
+
left: lane.x - laneWidth / 2,
|
|
191
|
+
top: 0,
|
|
192
|
+
width: laneWidth,
|
|
193
|
+
height: extendedHeight,
|
|
194
|
+
backgroundColor: isEven
|
|
195
|
+
? 'var(--sequence-lane-even, rgba(100, 149, 237, 0.08))'
|
|
196
|
+
: 'var(--sequence-lane-odd, rgba(100, 149, 237, 0.03))',
|
|
197
|
+
borderRight: '1px solid var(--sequence-lane-border, rgba(100, 149, 237, 0.2))',
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
200
|
+
);
|
|
201
|
+
})}
|
|
202
|
+
|
|
203
|
+
{/* Lane headers */}
|
|
204
|
+
{swimlanes.map((lane) => {
|
|
205
|
+
const hasChildren = lane.children.length > 0;
|
|
206
|
+
return (
|
|
207
|
+
<div
|
|
208
|
+
key={`header-${lane.namespace}`}
|
|
209
|
+
style={{
|
|
210
|
+
position: 'absolute',
|
|
211
|
+
left: lane.x - laneWidth / 2,
|
|
212
|
+
top: 0,
|
|
213
|
+
width: laneWidth,
|
|
214
|
+
height: headerHeight,
|
|
215
|
+
display: 'flex',
|
|
216
|
+
alignItems: 'center',
|
|
217
|
+
justifyContent: 'center',
|
|
218
|
+
backgroundColor: 'var(--sequence-header-bg, rgba(100, 149, 237, 0.15))',
|
|
219
|
+
borderBottom: '2px solid var(--sequence-header-border, rgba(100, 149, 237, 0.4))',
|
|
220
|
+
fontWeight: 600,
|
|
221
|
+
fontSize: 13,
|
|
222
|
+
color: 'var(--sequence-header-text, #333)',
|
|
223
|
+
pointerEvents: 'auto',
|
|
224
|
+
cursor: hasChildren ? 'pointer' : 'default',
|
|
225
|
+
userSelect: 'none',
|
|
226
|
+
}}
|
|
227
|
+
onClick={() => hasChildren && onToggleCollapse?.(lane.namespace)}
|
|
228
|
+
>
|
|
229
|
+
{hasChildren && (
|
|
230
|
+
<span style={{ marginRight: 6, fontSize: 10 }}>
|
|
231
|
+
{lane.isCollapsed ? '▶' : '▼'}
|
|
232
|
+
</span>
|
|
233
|
+
)}
|
|
234
|
+
<span>{lane.label}</span>
|
|
235
|
+
{lane.eventIds.length > 1 && (
|
|
236
|
+
<span style={{ marginLeft: 6, fontSize: 11, opacity: 0.6 }}>
|
|
237
|
+
({lane.eventIds.length})
|
|
238
|
+
</span>
|
|
239
|
+
)}
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
})}
|
|
243
|
+
|
|
244
|
+
{/* Vertical lifelines */}
|
|
245
|
+
{swimlanes.map((lane) => (
|
|
246
|
+
<div
|
|
247
|
+
key={`lifeline-${lane.namespace}`}
|
|
248
|
+
style={{
|
|
249
|
+
position: 'absolute',
|
|
250
|
+
left: lane.x,
|
|
251
|
+
top: headerHeight,
|
|
252
|
+
width: 2,
|
|
253
|
+
height: extendedHeight - headerHeight,
|
|
254
|
+
backgroundColor: 'var(--sequence-lifeline, rgba(100, 149, 237, 0.3))',
|
|
255
|
+
transform: 'translateX(-1px)',
|
|
256
|
+
}}
|
|
257
|
+
/>
|
|
258
|
+
))}
|
|
259
|
+
</div>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Props for SequenceDiagramRenderer
|
|
265
|
+
*/
|
|
266
|
+
export interface SequenceDiagramRendererProps {
|
|
267
|
+
/** Events to display in the diagram */
|
|
268
|
+
events: SequenceEvent[];
|
|
269
|
+
|
|
270
|
+
/** Edges connecting events */
|
|
271
|
+
edges: SequenceEdge[];
|
|
272
|
+
|
|
273
|
+
/** Layout options */
|
|
274
|
+
layoutOptions?: UseSequenceLayoutOptions;
|
|
275
|
+
|
|
276
|
+
/** Optional custom node types */
|
|
277
|
+
nodeTypes?: NodeTypes;
|
|
278
|
+
|
|
279
|
+
/** Optional custom edge types */
|
|
280
|
+
edgeTypes?: EdgeTypes;
|
|
281
|
+
|
|
282
|
+
/** Callback when a namespace collapse state is toggled */
|
|
283
|
+
onToggleCollapse?: (namespace: string) => void;
|
|
284
|
+
|
|
285
|
+
/** Callback when a node is clicked */
|
|
286
|
+
onNodeClick?: (nodeId: string, event: React.MouseEvent) => void;
|
|
287
|
+
|
|
288
|
+
/** Optional class name */
|
|
289
|
+
className?: string;
|
|
290
|
+
|
|
291
|
+
/** Optional width */
|
|
292
|
+
width?: number | string;
|
|
293
|
+
|
|
294
|
+
/** Optional height */
|
|
295
|
+
height?: number | string;
|
|
296
|
+
|
|
297
|
+
/** Whether to show controls */
|
|
298
|
+
showControls?: boolean;
|
|
299
|
+
|
|
300
|
+
/** Whether to show background grid */
|
|
301
|
+
showBackground?: boolean;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Inner component that uses React Flow hooks
|
|
306
|
+
*/
|
|
307
|
+
function SequenceDiagramInner({
|
|
308
|
+
events,
|
|
309
|
+
edges: sequenceEdges,
|
|
310
|
+
layoutOptions = {},
|
|
311
|
+
nodeTypes: customNodeTypes,
|
|
312
|
+
edgeTypes: customEdgeTypes,
|
|
313
|
+
onToggleCollapse,
|
|
314
|
+
onNodeClick,
|
|
315
|
+
showControls = true,
|
|
316
|
+
showBackground = false, // Default to false since swimlanes provide visual structure
|
|
317
|
+
}: SequenceDiagramRendererProps) {
|
|
318
|
+
// Extract layout params
|
|
319
|
+
const { laneWidth = 200, headerHeight = 60, arrowCentric = false } = layoutOptions;
|
|
320
|
+
|
|
321
|
+
// Merge custom node/edge types with sequence defaults
|
|
322
|
+
const nodeTypes = useMemo(
|
|
323
|
+
() => ({ ...defaultSequenceNodeTypes, ...customNodeTypes }),
|
|
324
|
+
[customNodeTypes]
|
|
325
|
+
);
|
|
326
|
+
const edgeTypes = useMemo(
|
|
327
|
+
() => ({ ...defaultSequenceEdgeTypes, ...customEdgeTypes }),
|
|
328
|
+
[customEdgeTypes]
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
// Compute layout (pass arrowCentric through layoutOptions)
|
|
332
|
+
const { nodes, edges, swimlanes, totalHeight } = useSequenceLayout(
|
|
333
|
+
events,
|
|
334
|
+
sequenceEdges,
|
|
335
|
+
{ ...layoutOptions, arrowCentric }
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
// Handle node click
|
|
339
|
+
const handleNodeClick = useCallback(
|
|
340
|
+
(_event: React.MouseEvent, node: { id: string }) => {
|
|
341
|
+
onNodeClick?.(node.id, _event);
|
|
342
|
+
},
|
|
343
|
+
[onNodeClick]
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
return (
|
|
347
|
+
<ReactFlow
|
|
348
|
+
nodes={nodes}
|
|
349
|
+
edges={edges}
|
|
350
|
+
nodeTypes={nodeTypes}
|
|
351
|
+
edgeTypes={edgeTypes}
|
|
352
|
+
onNodeClick={handleNodeClick}
|
|
353
|
+
fitView
|
|
354
|
+
fitViewOptions={{
|
|
355
|
+
padding: 0.2,
|
|
356
|
+
minZoom: 0.5,
|
|
357
|
+
maxZoom: 1.5,
|
|
358
|
+
}}
|
|
359
|
+
minZoom={0.1}
|
|
360
|
+
maxZoom={2}
|
|
361
|
+
nodesDraggable={false}
|
|
362
|
+
nodesConnectable={false}
|
|
363
|
+
elementsSelectable={true}
|
|
364
|
+
panOnScroll
|
|
365
|
+
zoomOnScroll
|
|
366
|
+
>
|
|
367
|
+
{/* SVG defs for arrow marker */}
|
|
368
|
+
<svg style={{ position: 'absolute', width: 0, height: 0 }}>
|
|
369
|
+
<defs>
|
|
370
|
+
<marker
|
|
371
|
+
id="sequence-arrow"
|
|
372
|
+
viewBox="0 0 10 10"
|
|
373
|
+
refX="8"
|
|
374
|
+
refY="5"
|
|
375
|
+
markerWidth="6"
|
|
376
|
+
markerHeight="6"
|
|
377
|
+
orient="auto-start-reverse"
|
|
378
|
+
>
|
|
379
|
+
<path
|
|
380
|
+
d="M 0 0 L 10 5 L 0 10 z"
|
|
381
|
+
fill="var(--sequence-arrow-color, #4169E1)"
|
|
382
|
+
/>
|
|
383
|
+
</marker>
|
|
384
|
+
</defs>
|
|
385
|
+
</svg>
|
|
386
|
+
|
|
387
|
+
{showBackground && (
|
|
388
|
+
<Background variant={BackgroundVariant.Dots} gap={16} size={1} />
|
|
389
|
+
)}
|
|
390
|
+
{showControls && <Controls />}
|
|
391
|
+
|
|
392
|
+
{/* Swimlane layer - renders behind nodes */}
|
|
393
|
+
<SwimlaneLayer
|
|
394
|
+
swimlanes={swimlanes}
|
|
395
|
+
laneWidth={laneWidth}
|
|
396
|
+
headerHeight={headerHeight}
|
|
397
|
+
totalHeight={totalHeight}
|
|
398
|
+
onToggleCollapse={onToggleCollapse}
|
|
399
|
+
/>
|
|
400
|
+
|
|
401
|
+
{/* Collapse toggle panel (for namespaces with children) */}
|
|
402
|
+
{swimlanes.some((s) => s.children.length > 0) && (
|
|
403
|
+
<Panel position="top-right">
|
|
404
|
+
<div
|
|
405
|
+
style={{
|
|
406
|
+
background: 'var(--sequence-panel-bg, rgba(255, 255, 255, 0.95))',
|
|
407
|
+
border: '1px solid var(--sequence-panel-border, #ddd)',
|
|
408
|
+
borderRadius: 4,
|
|
409
|
+
padding: '6px 10px',
|
|
410
|
+
fontSize: 11,
|
|
411
|
+
color: '#666',
|
|
412
|
+
}}
|
|
413
|
+
>
|
|
414
|
+
Click lane headers to expand/collapse
|
|
415
|
+
</div>
|
|
416
|
+
</Panel>
|
|
417
|
+
)}
|
|
418
|
+
</ReactFlow>
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Sequence Diagram Renderer
|
|
424
|
+
*
|
|
425
|
+
* Renders events as a sequence diagram with swimlanes based on namespaces.
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```tsx
|
|
429
|
+
* <SequenceDiagramRenderer
|
|
430
|
+
* events={[
|
|
431
|
+
* { id: '1', name: 'auth.validation.started' },
|
|
432
|
+
* { id: '2', name: 'auth.validation.completed' },
|
|
433
|
+
* { id: '3', name: 'database.query.executed' },
|
|
434
|
+
* ]}
|
|
435
|
+
* edges={[
|
|
436
|
+
* { id: 'e1', fromEvent: '1', toEvent: '2' },
|
|
437
|
+
* { id: 'e2', fromEvent: '2', toEvent: '3' },
|
|
438
|
+
* ]}
|
|
439
|
+
* />
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
export function SequenceDiagramRenderer(props: SequenceDiagramRendererProps) {
|
|
443
|
+
const { className, width = '100%', height = 600 } = props;
|
|
444
|
+
|
|
445
|
+
return (
|
|
446
|
+
<div
|
|
447
|
+
className={className}
|
|
448
|
+
style={{
|
|
449
|
+
width,
|
|
450
|
+
height,
|
|
451
|
+
position: 'relative',
|
|
452
|
+
}}
|
|
453
|
+
>
|
|
454
|
+
<ReactFlowProvider>
|
|
455
|
+
<SequenceDiagramInner {...props} />
|
|
456
|
+
</ReactFlowProvider>
|
|
457
|
+
</div>
|
|
458
|
+
);
|
|
459
|
+
}
|