@principal-ai/subsystems-react 0.56.13 → 0.56.14

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 (38) hide show
  1. package/dist/index.d.ts +6 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/subsystem/C4ProjectionView.d.ts +60 -0
  6. package/dist/subsystem/C4ProjectionView.d.ts.map +1 -0
  7. package/dist/subsystem/C4ProjectionView.js +336 -0
  8. package/dist/subsystem/C4ProjectionView.js.map +1 -0
  9. package/dist/subsystem/C4SketchView.d.ts +23 -0
  10. package/dist/subsystem/C4SketchView.d.ts.map +1 -0
  11. package/dist/subsystem/C4SketchView.js +119 -0
  12. package/dist/subsystem/C4SketchView.js.map +1 -0
  13. package/dist/subsystem/__fixtures__/c4Drilldown.d.ts +4 -27
  14. package/dist/subsystem/__fixtures__/c4Drilldown.d.ts.map +1 -1
  15. package/dist/subsystem/__fixtures__/c4Drilldown.js +4 -56
  16. package/dist/subsystem/__fixtures__/c4Drilldown.js.map +1 -1
  17. package/dist/subsystem/__fixtures__/c4Sketch.d.ts +9 -0
  18. package/dist/subsystem/__fixtures__/c4Sketch.d.ts.map +1 -0
  19. package/dist/subsystem/__fixtures__/c4Sketch.js +89 -0
  20. package/dist/subsystem/__fixtures__/c4Sketch.js.map +1 -0
  21. package/dist/subsystem/c4Sketch.d.ts +74 -0
  22. package/dist/subsystem/c4Sketch.d.ts.map +1 -0
  23. package/dist/subsystem/c4Sketch.js +9 -0
  24. package/dist/subsystem/c4Sketch.js.map +1 -0
  25. package/dist/subsystem/c4TrailFocus.d.ts +30 -0
  26. package/dist/subsystem/c4TrailFocus.d.ts.map +1 -0
  27. package/dist/subsystem/c4TrailFocus.js +57 -0
  28. package/dist/subsystem/c4TrailFocus.js.map +1 -0
  29. package/package.json +1 -1
  30. package/src/index.ts +14 -0
  31. package/src/stories/Canvases/C4/C4Sketch.stories.tsx +36 -0
  32. package/src/stories/Canvases/C4/C4ToComponentDrill.stories.tsx +18 -429
  33. package/src/subsystem/C4ProjectionView.tsx +595 -0
  34. package/src/subsystem/C4SketchView.tsx +241 -0
  35. package/src/subsystem/__fixtures__/c4Drilldown.ts +7 -58
  36. package/src/subsystem/__fixtures__/c4Sketch.ts +94 -0
  37. package/src/subsystem/c4Sketch.ts +78 -0
  38. package/src/subsystem/c4TrailFocus.ts +62 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/subsystems-react",
3
- "version": "0.56.13",
3
+ "version": "0.56.14",
4
4
  "description": "React UI for Subsystem Models — graph, Pierre code views, and session-event feeds",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -298,6 +298,20 @@ export type {
298
298
  // --- C4 projection -----------------------------------------------------
299
299
  export { C4Graph } from './subsystem/C4Graph';
300
300
  export type { C4GraphProps } from './subsystem/C4Graph';
301
+ export { C4ProjectionView } from './subsystem/C4ProjectionView';
302
+ export type { C4ProjectionViewProps } from './subsystem/C4ProjectionView';
303
+ export { C4SketchView } from './subsystem/C4SketchView';
304
+ export type { C4SketchViewProps } from './subsystem/C4SketchView';
305
+ export type {
306
+ C4Sketch,
307
+ C4SketchSummary,
308
+ SubsystemC4Source,
309
+ SubsystemC4Projection,
310
+ } from './subsystem/c4Sketch';
311
+ export {
312
+ deriveLitC4Edges,
313
+ deriveTrailPlayerElements,
314
+ } from './subsystem/c4TrailFocus';
301
315
  export { GraphSidebar } from './subsystem/GraphSidebar';
302
316
  export type { GraphSidebarProps, GraphSidebarTab } from './subsystem/GraphSidebar';
303
317
  export { GraphWithSidebar } from './subsystem/GraphWithSidebar';
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import '@xyflow/react/dist/style.css';
3
+ import type { Meta, StoryObj } from '@storybook/react';
4
+ import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
5
+ import { C4SketchView } from '../../../subsystem/C4SketchView';
6
+ import { SKETCH_FIXTURE } from '../../../subsystem/__fixtures__/c4Sketch';
7
+
8
+ const meta = {
9
+ title: 'Canvases/C4/C4Sketch',
10
+ component: C4SketchView,
11
+ parameters: { layout: 'fullscreen' },
12
+ tags: ['autodocs'],
13
+ decorators: [
14
+ (Story) => (
15
+ <ThemeProvider theme={defaultEditorTheme}>
16
+ <Story />
17
+ </ThemeProvider>
18
+ ),
19
+ ],
20
+ } satisfies Meta<typeof C4SketchView>;
21
+
22
+ export default meta;
23
+ type Story = StoryObj<typeof meta>;
24
+
25
+ function Demo() {
26
+ return (
27
+ <div style={{ width: '100%', height: '100vh' }}>
28
+ <C4SketchView sketch={SKETCH_FIXTURE} />
29
+ </div>
30
+ );
31
+ }
32
+
33
+ /** A standalone authored sketch: no model, no grounding, no promotion. */
34
+ export const Sketch: Story = {
35
+ render: () => <Demo />,
36
+ };
@@ -1,74 +1,19 @@
1
- import { useCallback, useMemo, useRef, useState } from 'react';
1
+ import React from 'react';
2
2
  import '@xyflow/react/dist/style.css';
3
3
  import type { Meta, StoryObj } from '@storybook/react';
4
4
  import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
5
- import { IndustryMarkdownSlide } from 'themed-markdown';
6
- import { C4Graph } from '../../../subsystem/C4Graph';
7
- import { GraphWithSidebar } from '../../../subsystem/GraphWithSidebar';
8
- import { TrailStepsNav } from '../../../subsystem/TrailStepsNav';
9
- import type { C4ConstructGraph } from '../../../subsystem/C4Graph';
10
- import { deriveGraphEdges, trailStepGraphEdgeId } from '../../../subsystem/model';
5
+ import { C4ProjectionView } from '../../../subsystem/C4ProjectionView';
11
6
  import {
12
7
  DRILLDOWN_C4_MODEL,
13
8
  DRILLDOWN_COMPONENTS,
14
9
  DRILLDOWN_DOC,
15
- DRILLDOWN_ELEMENTS,
16
10
  DRILLDOWN_MEMBERS,
17
11
  DRILLDOWN_TRAILS,
18
- deriveLitC4Edges,
19
- deriveTrailPlayerElements,
20
12
  } from '../../../subsystem/__fixtures__/c4Drilldown';
21
13
 
22
- /** The model this story is grounded in — shown in the sidebar header. */
23
- const MODEL_DESCRIPTION =
24
- 'The subsystem this C4 view summarizes. Focusing a trail opens the containers that hold its cast and folds the trail in as real nodes and edges, nested under their C4 component.';
25
-
26
- /**
27
- * The C4 → construct drill-down, in one canvas.
28
- *
29
- * The C4 view is the high-level summary: containers, externals, and the
30
- * rolled-up lines between them. Focusing a trail folds the implementation into
31
- * it — the constructs (the component graph's own `subsystem-component` nodes
32
- * and `subsystem-edge` edges) are laid out by ELK inside the opened containers
33
- * and morphed in with them, so the C4 canvas carries both the summary and the
34
- * implementation. The model is the REAL subsystem model this repo lives under
35
- * (`sg-1788913589907-ieokymqg9` "Gist share from Studio": 19 components, 15/15
36
- * verified claims, 7 walkthroughs checked).
37
- *
38
- * Interaction:
39
- * - click a C4 edge → it selects, and the story focuses the first trail that
40
- * backs the line — the "edge is the summary, the trail is the
41
- * implementation" jump;
42
- * - the C4 edge inspector lists every backing trail; clicking one focuses it;
43
- * - focusing a trail HIDES every C4 element whose components don't appear in
44
- * it, and OPENS the containers that hold the cast, so the participating
45
- * components come in at Level 3 instead of staying a collapsed box;
46
- * - each opened container hosts the focused trail's components as real
47
- * `subsystem-component` nodes and `subsystem-edge` edges. The constructs
48
- * nest under the authored C4 component that claims them (container →
49
- * component → construct); one no component claims sits directly in the
50
- * container. An edge between constructs in the same container draws as a
51
- * subsystem edge; one that would cross containers is left to the C4
52
- * container-level line. Clicking a construct opens its **functionality** —
53
- * the rendered declaration and its purpose — in the bottom-left panel. The
54
- * host resolves it (`constructGraphByContainer`) so the C4 view stays free
55
- * of the document;
56
- * - hovering a *step* row DIMS the constructs down to that single hop: every
57
- * construct except the step's `from`/`to`, and every construct edge except
58
- * the step's own, fades (`dimmedConstructIds` / `dimmedConstructEdgeIds`).
59
- * Transient: falls back to the focused view on leave;
60
- * - the C4 view is composed with its own sidebar through the shared
61
- * `GraphWithSidebar` wrapper: the sidebar carries the **trails**. The C4
62
- * selection inspector stays the graph's own floating overlay; the wrapper
63
- * is the composition point, so the C4 graph itself stays overlay-only.
64
- *
65
- * The status bar below the C4 view shows the current selection, so the wiring
66
- * is visible while it is being argued with.
67
- */
68
-
69
14
  const meta = {
70
15
  title: 'Canvases/C4/C4ToComponentDrill',
71
- component: C4Graph,
16
+ component: C4ProjectionView,
72
17
  parameters: { layout: 'fullscreen' },
73
18
  tags: ['autodocs'],
74
19
  decorators: [
@@ -78,386 +23,30 @@ const meta = {
78
23
  </ThemeProvider>
79
24
  ),
80
25
  ],
81
- } satisfies Meta<typeof C4Graph>;
26
+ } satisfies Meta<typeof C4ProjectionView>;
82
27
 
83
28
  export default meta;
84
29
  type Story = StoryObj<typeof meta>;
85
30
 
86
- function Demo() {
87
- const [openIds, setOpenIds] = useState<string[]>([]);
88
- const [focusedTrailId, setFocusedTrailId] = useState<string | null>(null);
89
- // The trail (or step) currently hovered in the trails panel — its transient
90
- // preview drives a step-scoped dim. `stepIndex: null` means the whole trail
91
- // (a collapsed row hover).
92
- const [hoveredStep, setHoveredStep] = useState<{
93
- trailId: string;
94
- stepIndex: number | null;
95
- } | null>(null);
96
- // The persistently-focused step (a step click in the sidebar). Drives the
97
- // sticky construct spotlight and the C4 graph's code drawer; the hover below
98
- // is the transient preview that shares the same dim.
99
- const [focusedStep, setFocusedStep] = useState<{
100
- trailId: string;
101
- stepIndex: number;
102
- } | null>(null);
103
- /**
104
- * EXPERIMENT: when a trail is focused, remove the elements it does not touch
105
- * from the model outright, so ELK lays the diagram out to the cast alone
106
- * instead of leaving gaps where the hidden boxes were. `null` = nothing
107
- * focused, the whole model.
108
- */
109
- const trailPlayers = useMemo(() => {
110
- if (!focusedTrailId) return null;
111
- const trail = DRILLDOWN_TRAILS.find((t) => t.id === focusedTrailId);
112
- return trail ? deriveTrailPlayerElements(trail.steps, DRILLDOWN_MEMBERS) : null;
113
- }, [focusedTrailId]);
114
-
115
- const model = useMemo(() => {
116
- if (!trailPlayers) return { ...DRILLDOWN_C4_MODEL, openContainerIds: openIds };
117
- const nodes = DRILLDOWN_C4_MODEL.nodes.filter((n) => trailPlayers.has(n.id));
118
- const ids = new Set(nodes.map((n) => n.id));
119
- const edges = DRILLDOWN_C4_MODEL.edges.filter(
120
- (e) => ids.has(e.source) && ids.has(e.target),
121
- );
122
- return { ...DRILLDOWN_C4_MODEL, nodes, edges, openContainerIds: openIds };
123
- }, [openIds, trailPlayers]);
124
-
125
- const toggle = useCallback((id: string) => {
126
- setOpenIds((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id].sort()));
127
- }, []);
128
-
129
- const allElementIds = useMemo(
130
- () => DRILLDOWN_C4_MODEL.nodes.map((n) => n.id),
131
- [],
132
- );
133
- const allEdgeIds = useMemo(
134
- () => DRILLDOWN_C4_MODEL.edges.map((e) => e.id),
135
- [],
136
- );
137
-
138
- /**
139
- * The C4 containers that hold a component the trail touches — opened when the
140
- * trail is focused so its cast comes in at Level 3 instead of staying a
141
- * collapsed container box. A trail whose endpoints no container claims (the
142
- * anti-library keys) opens nothing.
143
- */
144
- const containersForTrail = useCallback((trailId: string): string[] => {
145
- const trail = DRILLDOWN_TRAILS.find((t) => t.id === trailId);
146
- if (!trail) return [];
147
- const aliases = new Set<string>();
148
- for (const s of trail.steps) {
149
- aliases.add(s.from);
150
- aliases.add(s.to);
151
- }
152
- const out: string[] = [];
153
- for (const el of DRILLDOWN_C4_MODEL.nodes) {
154
- if (el.kind !== 'container') continue;
155
- const members = DRILLDOWN_MEMBERS[el.id] ?? [];
156
- if (members.some((a) => aliases.has(a))) out.push(el.id);
157
- }
158
- return out;
159
- }, []);
160
-
161
- /**
162
- * The containers the *current* trail focus opened — remembered so clearing
163
- * the focus (or moving to another trail) can close exactly those and leave any
164
- * the reader opened by hand alone.
165
- */
166
- const trailOpenedRef = useRef<Set<string>>(new Set());
167
-
168
- const openContainersForTrail = useCallback(
169
- (trailId: string) => {
170
- const ids = containersForTrail(trailId);
171
- if (ids.length === 0) return;
172
- trailOpenedRef.current = new Set([...trailOpenedRef.current, ...ids]);
173
- setOpenIds((prev) => [...new Set([...prev, ...ids])].sort());
174
- },
175
- [containersForTrail],
176
- );
177
-
178
- /** Close the containers the current trail focus opened. */
179
- const closeTrailContainers = useCallback(() => {
180
- const opened = trailOpenedRef.current;
181
- trailOpenedRef.current = new Set();
182
- if (opened.size === 0) return;
183
- setOpenIds((prev) => prev.filter((id) => !opened.has(id)));
184
- }, []);
185
-
186
- /**
187
- * The single source of the C4 emphasis, derived from the two focus facts —
188
- * mirroring the component graph:
189
- *
190
- * hidden — an EXPANDED/focused trail removes every element it does not
191
- * touch (the analogue of the component graph's opened-flow hide),
192
- * lines included; `null` when nothing is focused.
193
- * dimmed — the HOVER spotlight (a step, or a whole trail for a collapsed
194
- * row) fades everything outside it; `null` when nothing is hovered.
195
- *
196
- * A step internal to a container folds into no C4 line, so its hover dims
197
- * every edge while the container stays bright — no drawn edge represents it.
198
- *
199
- * The spotlight is the *selected* step when one is focused (sticky), else the
200
- * hovered one (transient) — so a step click keeps its implementation lit.
201
- */
202
- const activeStep = focusedStep ?? hoveredStep;
203
- const emphasis = useMemo(() => {
204
- let hiddenElementIds: ReadonlySet<string> | null = null;
205
- let hiddenEdgeIds: ReadonlySet<string> | null = null;
206
- if (focusedTrailId) {
207
- const trail = DRILLDOWN_TRAILS.find((t) => t.id === focusedTrailId);
208
- const players = trail
209
- ? deriveTrailPlayerElements(trail.steps, DRILLDOWN_MEMBERS)
210
- : new Set<string>();
211
- hiddenElementIds = new Set(allElementIds.filter((id) => !players.has(id)));
212
- const lit = deriveLitC4Edges({ trailId: focusedTrailId }, DRILLDOWN_C4_MODEL.edges);
213
- hiddenEdgeIds = new Set(allEdgeIds.filter((id) => !lit.has(id)));
214
- }
215
-
216
- let dimmedElementIds: ReadonlySet<string> | null = null;
217
- let dimmedEdgeIds: ReadonlySet<string> | null = null;
218
- if (activeStep) {
219
- const trail = DRILLDOWN_TRAILS.find((t) => t.id === activeStep.trailId);
220
- const steps =
221
- !trail
222
- ? []
223
- : activeStep.stepIndex != null
224
- ? trail.steps.slice(activeStep.stepIndex, activeStep.stepIndex + 1)
225
- : trail.steps;
226
- const spotlight = deriveTrailPlayerElements(steps, DRILLDOWN_MEMBERS);
227
- dimmedElementIds = new Set(allElementIds.filter((id) => !spotlight.has(id)));
228
- const lit = deriveLitC4Edges(
229
- {
230
- trailId: activeStep.trailId,
231
- ...(activeStep.stepIndex != null ? { stepIndex: activeStep.stepIndex } : {}),
232
- },
233
- DRILLDOWN_C4_MODEL.edges,
234
- );
235
- dimmedEdgeIds = new Set(allEdgeIds.filter((id) => !lit.has(id)));
236
- }
237
-
238
- return { hiddenElementIds, hiddenEdgeIds, dimmedElementIds, dimmedEdgeIds };
239
- }, [focusedTrailId, activeStep, allElementIds, allEdgeIds]);
240
-
241
- /**
242
- * The construct-level subgraph each opened container hosts: the focused
243
- * trail's referenced components, grouped by the authored C4 component that
244
- * claims each (so constructs nest container → component → construct), with
245
- * the trail's steps as their edges. Scoped per container, so an edge that
246
- * crosses containers is dropped here and stays a C4 container-level line.
247
- */
248
- const constructGraphByContainer = useMemo(() => {
249
- if (!focusedTrailId) return undefined;
250
- const trail = DRILLDOWN_TRAILS.find((t) => t.id === focusedTrailId);
251
- if (!trail) return undefined;
252
- const byAlias = new Map(DRILLDOWN_COMPONENTS.map((c) => [c.alias, c]));
253
- const referenced = new Set<string>();
254
- for (const s of trail.steps) {
255
- referenced.add(s.from);
256
- referenced.add(s.to);
257
- }
258
- const componentOf = new Map<string, string>();
259
- for (const el of DRILLDOWN_ELEMENTS) {
260
- if (el.kind === 'component') for (const a of el.members ?? []) componentOf.set(a, el.id);
261
- }
262
- const out = new Map<string, C4ConstructGraph>();
263
- for (const el of DRILLDOWN_ELEMENTS) {
264
- if (el.kind !== 'container') continue;
265
- const aliases = (DRILLDOWN_MEMBERS[el.id] ?? []).filter(
266
- (a) => referenced.has(a) && byAlias.has(a),
267
- );
268
- if (aliases.length === 0) continue;
269
- out.set(el.id, {
270
- components: aliases.map((a) => byAlias.get(a)!),
271
- trails: [trail],
272
- componentOf,
273
- });
274
- }
275
- return out;
276
- }, [focusedTrailId]);
277
-
278
- /**
279
- * The construct-level spotlight for the active step, matching the component
280
- * graph: every construct except the step's `from`/`to`, and every construct
281
- * edge except the step's own, fades. The C4-element spotlight (`emphasis`)
282
- * only reaches containers, so this is what narrows the constructs to the hop.
283
- */
284
- const constructEmphasis = useMemo(() => {
285
- const none = { ids: null as ReadonlySet<string> | null, edgeIds: null as ReadonlySet<string> | null };
286
- if (!activeStep) return none;
287
- const trail = DRILLDOWN_TRAILS.find((t) => t.id === activeStep.trailId);
288
- if (!trail) return none;
289
- const steps =
290
- activeStep.stepIndex != null ? [trail.steps[activeStep.stepIndex]] : trail.steps;
291
- const spotlight = new Set<string>();
292
- const litEdges = new Set<string>();
293
- for (const s of steps) {
294
- spotlight.add(s.from);
295
- spotlight.add(s.to);
296
- litEdges.add(trailStepGraphEdgeId(s));
297
- }
298
- const allAliases = new Set<string>();
299
- const allEdgeIds = new Set<string>();
300
- for (const cg of constructGraphByContainer?.values() ?? []) {
301
- for (const c of cg.components) allAliases.add(c.alias);
302
- for (const e of deriveGraphEdges({ trails: [...cg.trails] })) allEdgeIds.add(e.id);
303
- }
304
- return {
305
- ids: new Set([...allAliases].filter((a) => !spotlight.has(a))),
306
- edgeIds: new Set([...allEdgeIds].filter((id) => !litEdges.has(id))),
307
- };
308
- }, [activeStep, constructGraphByContainer]);
309
-
310
- /**
311
- * Focus a trail: the C4 hides non-members, opens the containers that hold
312
- * them (so the cast shows at Level 3), and the component graph deep-links.
313
- */
314
- const focusTrail = useCallback(
315
- (trailId: string) => {
316
- setFocusedTrailId(trailId);
317
- setHoveredStep(null);
318
- // Swap the previous trail's opened containers for this one's, so moving
319
- // between trails does not accumulate open boxes.
320
- closeTrailContainers();
321
- openContainersForTrail(trailId);
322
- },
323
- [openContainersForTrail, closeTrailContainers],
324
- );
325
-
326
- const clearFocus = useCallback(() => {
327
- setFocusedTrailId(null);
328
- setHoveredStep(null);
329
- // Collapse the containers the focus opened, so Back leaves the diagram as
330
- // it was rather than with the trail's boxes still expanded.
331
- closeTrailContainers();
332
- }, [closeTrailContainers]);
31
+ const DESCRIPTION =
32
+ 'The subsystem this C4 view summarizes. Focusing a trail opens the containers that hold its cast and folds the trail in as real nodes and edges, nested under their C4 component.';
333
33
 
34
+ function Demo() {
334
35
  return (
335
- <div
336
- style={{
337
- width: '100%',
338
- height: '100vh',
339
- display: 'flex',
340
- flexDirection: 'column',
341
- background: defaultEditorTheme.colors.background,
342
- fontFamily: 'monospace',
343
- }}
344
- >
345
- <header
346
- style={{
347
- flexShrink: 0,
348
- display: 'flex',
349
- alignItems: 'center',
350
- gap: 12,
351
- padding: '12px 16px',
352
- background: defaultEditorTheme.colors.background,
353
- borderBottom: `1px solid ${defaultEditorTheme.colors.border}`,
354
- }}
355
- >
356
- <h2
357
- style={{
358
- margin: 0,
359
- minWidth: 0,
360
- fontSize: defaultEditorTheme.fontSizes[2],
361
- fontWeight: 600,
362
- color: defaultEditorTheme.colors.text,
363
- fontFamily: defaultEditorTheme.fonts.monospace,
364
- overflowWrap: 'anywhere',
365
- }}
366
- >
367
- {DRILLDOWN_DOC.title}
368
- </h2>
369
- </header>
370
- {/* C4 — the summary and, on focus, the construct implementation. */}
371
- <div style={{ flex: 1, minHeight: 0, position: 'relative' }}>
372
- <GraphWithSidebar
373
- persistKey="c4-drill"
374
- defaultWidth={320}
375
- minWidth={240}
376
- maxWidth={480}
377
- headerMaxHeight="30%"
378
- header={
379
- <>
380
- <div style={{ fontSize: 12, lineHeight: 1.5 }}>
381
- <IndustryMarkdownSlide
382
- content={MODEL_DESCRIPTION}
383
- slideIdPrefix="c4-desc"
384
- slideIndex={0}
385
- isVisible={true}
386
- theme={defaultEditorTheme}
387
- disableScroll={true}
388
- disableBasePadding
389
- enableKeyboardScrolling={false}
390
- autoFocusOnVisible={false}
391
- />
392
- </div>
393
- </>
394
- }
395
- sidebarBody={
396
- <TrailStepsNav
397
- trails={DRILLDOWN_TRAILS}
398
- focusedTrailId={focusedTrailId}
399
- focusedStepIndex={
400
- focusedStep && focusedStep.trailId === focusedTrailId
401
- ? focusedStep.stepIndex
402
- : null
403
- }
404
- hoveredTrailStep={hoveredStep}
405
- onFocusFlow={(tl) => {
406
- setFocusedStep(null);
407
- focusTrail(tl.id);
408
- }}
409
- onClearFocus={() => {
410
- setFocusedStep(null);
411
- clearFocus();
412
- }}
413
- onFocusStep={(tl, stepIndex) => {
414
- // Toggle: clicking the selected step again clears it.
415
- setFocusedStep((prev) =>
416
- prev && prev.trailId === tl.id && prev.stepIndex === stepIndex
417
- ? null
418
- : { trailId: tl.id, stepIndex },
419
- );
420
- focusTrail(tl.id);
421
- }}
422
- onHoverStep={(tl, stepIndex) => {
423
- setHoveredStep({ trailId: tl.id, stepIndex });
424
- }}
425
- onHoverFlow={(tl) => {
426
- setHoveredStep({ trailId: tl.id, stepIndex: null });
427
- }}
428
- onLeaveStep={() => {
429
- setHoveredStep(null);
430
- }}
431
- />
432
- }
433
- >
434
- <C4Graph
435
- model={model}
436
- onSelectEdge={(edge) => {
437
- if (edge && (edge.trailRefs ?? []).length > 0) {
438
- // Auto-drill: an edge click focuses its first backing trail.
439
- focusTrail(edge.trailRefs![0]!.trailId);
440
- }
441
- }}
442
- onTrailClick={(ref) => focusTrail(ref.trailId)}
443
- onToggleContainer={toggle}
444
- hiddenElementIds={emphasis.hiddenElementIds}
445
- hiddenEdgeIds={emphasis.hiddenEdgeIds}
446
- dimmedElementIds={emphasis.dimmedElementIds}
447
- dimmedEdgeIds={emphasis.dimmedEdgeIds}
448
- dimmedConstructIds={constructEmphasis.ids}
449
- dimmedConstructEdgeIds={constructEmphasis.edgeIds}
450
- constructGraphByContainer={constructGraphByContainer}
451
- focusedStep={focusedStep}
452
- onCloseStep={() => setFocusedStep(null)}
453
- />
454
- </GraphWithSidebar>
455
- </div>
456
-
36
+ <div style={{ width: '100%', height: '100vh' }}>
37
+ <C4ProjectionView
38
+ model={DRILLDOWN_C4_MODEL}
39
+ components={DRILLDOWN_COMPONENTS}
40
+ trails={DRILLDOWN_TRAILS}
41
+ memberships={DRILLDOWN_MEMBERS}
42
+ title={DRILLDOWN_DOC.title}
43
+ description={DESCRIPTION}
44
+ persistKey="c4-projection-drill"
45
+ />
457
46
  </div>
458
47
  );
459
48
  }
460
49
 
461
50
  export const DrillIntoTrails: Story = {
462
51
  render: () => <Demo />,
463
- };
52
+ };