@rokkit/graph 1.8.0 → 1.8.1

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 (119) hide show
  1. package/README.md +59 -0
  2. package/dist/Graph.svelte +108 -134
  3. package/dist/Graph.svelte.d.ts +1 -1
  4. package/dist/GraphLegend.svelte +81 -10
  5. package/dist/GraphLegend.svelte.d.ts +15 -0
  6. package/dist/GraphState.svelte.d.ts +161 -52
  7. package/dist/GraphState.svelte.js +380 -304
  8. package/dist/actions/canvas.d.ts +18 -0
  9. package/dist/actions/canvas.js +59 -0
  10. package/dist/actions/choices.d.ts +12 -0
  11. package/dist/actions/choices.js +23 -0
  12. package/dist/actions/interactions.d.ts +20 -0
  13. package/dist/actions/interactions.js +57 -0
  14. package/dist/actions/listen.d.ts +2 -0
  15. package/dist/actions/listen.js +10 -0
  16. package/dist/canvas/frame.d.ts +41 -0
  17. package/dist/canvas/frame.js +34 -0
  18. package/dist/controls/BundleControl.svelte +11 -10
  19. package/dist/controls/BundleControl.svelte.d.ts +0 -7
  20. package/dist/controls/DensityControl.svelte +17 -9
  21. package/dist/controls/DensityControl.svelte.d.ts +0 -8
  22. package/dist/controls/DepthControl.svelte +11 -4
  23. package/dist/controls/DepthControl.svelte.d.ts +0 -9
  24. package/dist/controls/DrillBar.svelte +63 -0
  25. package/dist/controls/DrillBar.svelte.d.ts +9 -0
  26. package/dist/controls/EdgeStyleControl.svelte +10 -10
  27. package/dist/controls/EdgeStyleControl.svelte.d.ts +0 -6
  28. package/dist/controls/HiddenControl.svelte +27 -0
  29. package/dist/controls/HiddenControl.svelte.d.ts +7 -0
  30. package/dist/controls/MeasureControl.svelte +39 -0
  31. package/dist/controls/MeasureControl.svelte.d.ts +12 -0
  32. package/dist/controls/ToggleControl.svelte +33 -0
  33. package/dist/controls/ToggleControl.svelte.d.ts +13 -0
  34. package/dist/controls/ViolationsControl.svelte +27 -0
  35. package/dist/controls/ViolationsControl.svelte.d.ts +7 -0
  36. package/dist/controls/ZoomControl.svelte +21 -18
  37. package/dist/controls/zoom.d.ts +3 -0
  38. package/dist/controls/zoom.js +6 -0
  39. package/dist/diagrams/ArcDiagram.svelte +116 -0
  40. package/dist/diagrams/ArcDiagram.svelte.d.ts +31 -0
  41. package/dist/diagrams/CallTree.svelte +17 -5
  42. package/dist/diagrams/CallTree.svelte.d.ts +5 -1
  43. package/dist/diagrams/DependencyDiagram.svelte +18 -6
  44. package/dist/diagrams/DependencyDiagram.svelte.d.ts +5 -1
  45. package/dist/diagrams/DependencyMatrix.svelte +11 -32
  46. package/dist/diagrams/ErDiagram.svelte +18 -6
  47. package/dist/diagrams/ErDiagram.svelte.d.ts +5 -1
  48. package/dist/diagrams/LayersDiagram.svelte +124 -0
  49. package/dist/diagrams/LayersDiagram.svelte.d.ts +30 -0
  50. package/dist/diagrams/Neighborhood.svelte +26 -6
  51. package/dist/diagrams/Neighborhood.svelte.d.ts +11 -1
  52. package/dist/diagrams/PolymetricTree.svelte +116 -0
  53. package/dist/diagrams/PolymetricTree.svelte.d.ts +28 -0
  54. package/dist/diagrams/StructureDiagram.svelte +38 -8
  55. package/dist/diagrams/StructureDiagram.svelte.d.ts +9 -1
  56. package/dist/diagrams/Sunburst.svelte +44 -8
  57. package/dist/diagrams/Sunburst.svelte.d.ts +12 -2
  58. package/dist/diagrams/Treemap.svelte +44 -8
  59. package/dist/diagrams/Treemap.svelte.d.ts +12 -2
  60. package/dist/index.d.ts +10 -3
  61. package/dist/index.js +9 -2
  62. package/dist/layout/arcs.d.ts +6 -0
  63. package/dist/layout/arcs.js +141 -0
  64. package/dist/layout/cards.js +9 -2
  65. package/dist/layout/edges.d.ts +13 -1
  66. package/dist/layout/edges.js +26 -2
  67. package/dist/layout/extent.d.ts +17 -0
  68. package/dist/layout/extent.js +27 -0
  69. package/dist/layout/flow.js +3 -1
  70. package/dist/layout/index.d.ts +4 -1
  71. package/dist/layout/index.js +17 -2
  72. package/dist/layout/layers.d.ts +6 -0
  73. package/dist/layout/layers.js +180 -0
  74. package/dist/layout/matrix.d.ts +12 -0
  75. package/dist/layout/matrix.js +20 -0
  76. package/dist/layout/neighborhood.js +16 -7
  77. package/dist/layout/options.d.ts +1 -1
  78. package/dist/layout/options.js +16 -3
  79. package/dist/layout/percentile.d.ts +8 -0
  80. package/dist/layout/percentile.js +13 -0
  81. package/dist/layout/polymetric.d.ts +3 -0
  82. package/dist/layout/polymetric.js +221 -0
  83. package/dist/layout/radial.js +3 -1
  84. package/dist/layout/structure.js +7 -1
  85. package/dist/layout/sunburst.js +11 -4
  86. package/dist/layout/types.d.ts +79 -0
  87. package/dist/layout/world.js +11 -4
  88. package/dist/messages.d.ts +14 -0
  89. package/dist/messages.js +17 -0
  90. package/dist/model/condense.d.ts +8 -0
  91. package/dist/model/condense.js +114 -0
  92. package/dist/model/entities.d.ts +12 -0
  93. package/dist/model/entities.js +12 -0
  94. package/dist/model/normalize.d.ts +2 -6
  95. package/dist/model/normalize.js +64 -2
  96. package/dist/model/relationships.d.ts +30 -0
  97. package/dist/model/relationships.js +51 -0
  98. package/dist/model/share.d.ts +9 -0
  99. package/dist/model/share.js +43 -0
  100. package/dist/model/tree.d.ts +7 -1
  101. package/dist/model/tree.js +11 -4
  102. package/dist/preset.d.ts +18 -0
  103. package/dist/preset.js +34 -3
  104. package/dist/schema/EntitiesView.svelte +8 -6
  105. package/dist/schema/EntityView.svelte +13 -10
  106. package/dist/state/GraphConfig.svelte.d.ts +124 -0
  107. package/dist/state/GraphConfig.svelte.js +253 -0
  108. package/dist/state/GraphDrill.svelte.d.ts +52 -0
  109. package/dist/state/GraphDrill.svelte.js +115 -0
  110. package/dist/state/GraphGroups.svelte.d.ts +35 -0
  111. package/dist/state/GraphGroups.svelte.js +73 -0
  112. package/dist/state/GraphSelection.svelte.d.ts +53 -0
  113. package/dist/state/GraphSelection.svelte.js +94 -0
  114. package/dist/state/intents.d.ts +33 -0
  115. package/dist/state/intents.js +30 -0
  116. package/dist/state/text.d.ts +28 -0
  117. package/dist/state/text.js +56 -0
  118. package/dist/types.d.ts +72 -1
  119. package/package.json +3 -2
@@ -0,0 +1,124 @@
1
+ <script lang="ts">
2
+ /**
3
+ * The intended layers, top to bottom, with every dependency between them (#167).
4
+ *
5
+ * Arrows should only point DOWN. The ones that climb are layer violations and are the output
6
+ * of the view — `ViolationsControl` shows only those. The host assigns each node its `layer`
7
+ * (it knows the architecture it intends); rokkit computes no depth, and marks each edge
8
+ * `down` / `skip` / `up` / `level` from those layers unless the host already said.
9
+ */
10
+ import Graph from '../Graph.svelte'
11
+ import GraphLegend from '../GraphLegend.svelte'
12
+ import DiagramFrame from './DiagramFrame.svelte'
13
+ import DensityControl from '../controls/DensityControl.svelte'
14
+ import DrillBar from '../controls/DrillBar.svelte'
15
+ import ViolationsControl from '../controls/ViolationsControl.svelte'
16
+ import ZoomControl from '../controls/ZoomControl.svelte'
17
+ import { GraphState } from '../GraphState.svelte.js'
18
+ import type { Density } from '../layout/types.js'
19
+ import type { GraphFields, GraphNode } from '../types.js'
20
+ import type { GraphPreset } from '../preset.js'
21
+
22
+ type Props = {
23
+ /** Share one state with another view — see `DependencyDiagram`. */
24
+ state?: GraphState
25
+ nodes?: unknown[]
26
+ edges?: unknown[]
27
+ fields?: GraphFields
28
+ density?: Density
29
+ /** Every edge, or only the ones that climb. Bindable — the control moves it. */
30
+ showEdges?: 'all' | 'violations'
31
+ /** Band names by layer index; an unnamed layer reads "Layer N". */
32
+ layerLabels?: string[]
33
+ controls?: boolean
34
+ legend?: boolean
35
+ value?: string | null
36
+ preset?: GraphPreset
37
+ mode?: 'light' | 'dark'
38
+ arrows?: boolean
39
+ label?: string
40
+ onselect?: (id: string | null) => void
41
+ onexpand?: (id: string, node: GraphNode) => void
42
+ oncollapse?: (id: string, node: GraphNode) => void
43
+ class?: string
44
+ }
45
+
46
+ let {
47
+ state: provided,
48
+ nodes = [],
49
+ edges = [],
50
+ fields = {},
51
+ density = $bindable('names'),
52
+ showEdges = $bindable('all'),
53
+ layerLabels = undefined,
54
+ controls = false,
55
+ legend = false,
56
+ value = $bindable(undefined),
57
+ preset = undefined,
58
+ mode = 'light',
59
+ arrows = true,
60
+ label = undefined,
61
+ onselect = undefined,
62
+ onexpand = undefined,
63
+ oncollapse = undefined,
64
+ class: className = ''
65
+ }: Props = $props()
66
+
67
+ let zoom = $state(1)
68
+
69
+ const config = () => ({
70
+ nodes,
71
+ edges,
72
+ fields,
73
+ layout: 'layers',
74
+ density,
75
+ showEdges,
76
+ layerLabels,
77
+ value,
78
+ preset,
79
+ mode,
80
+ label,
81
+ onselect,
82
+ onexpand,
83
+ oncollapse
84
+ })
85
+
86
+ // svelte-ignore state_referenced_locally
87
+ const graph = provided ?? new GraphState(config())
88
+
89
+ $effect(() => {
90
+ if (!provided) {
91
+ graph.update(config())
92
+ return
93
+ }
94
+ // A supplied state is the caller's — see DependencyDiagram. Only this component's own
95
+ // options are merged in.
96
+ const { nodes: _n, edges: _e, fields: _f, preset: _p, mode: _m, value: _v, ...mine } =
97
+ config()
98
+ graph.apply(mine)
99
+ })
100
+ </script>
101
+
102
+ <!-- Snippets are declared OUTSIDE the frame and passed conditionally — see DependencyDiagram. -->
103
+ {#snippet controlBar()}
104
+ <DrillBar state={graph} />
105
+ {#if controls}
106
+ <ViolationsControl {showEdges} onchange={(v) => (showEdges = v)} />
107
+ <DensityControl {density} onchange={(v) => (density = v)} />
108
+ <ZoomControl {zoom} onchange={(v) => (zoom = v)} />
109
+ {/if}
110
+ {/snippet}
111
+
112
+ {#snippet legendBar()}
113
+ <GraphLegend state={graph} relations />
114
+ {/snippet}
115
+
116
+ <DiagramFrame
117
+ class={className}
118
+ overlay={controls || graph.showsDrillBar ? controlBar : undefined}
119
+ footer={legend ? legendBar : undefined}
120
+ >
121
+ {#snippet canvas()}
122
+ <Graph state={graph} bind:zoom {arrows} />
123
+ {/snippet}
124
+ </DiagramFrame>
@@ -0,0 +1,30 @@
1
+ import { GraphState } from '../GraphState.svelte.js';
2
+ import type { Density } from '../layout/types.js';
3
+ import type { GraphFields, GraphNode } from '../types.js';
4
+ import type { GraphPreset } from '../preset.js';
5
+ type Props = {
6
+ /** Share one state with another view — see `DependencyDiagram`. */
7
+ state?: GraphState;
8
+ nodes?: unknown[];
9
+ edges?: unknown[];
10
+ fields?: GraphFields;
11
+ density?: Density;
12
+ /** Every edge, or only the ones that climb. Bindable — the control moves it. */
13
+ showEdges?: 'all' | 'violations';
14
+ /** Band names by layer index; an unnamed layer reads "Layer N". */
15
+ layerLabels?: string[];
16
+ controls?: boolean;
17
+ legend?: boolean;
18
+ value?: string | null;
19
+ preset?: GraphPreset;
20
+ mode?: 'light' | 'dark';
21
+ arrows?: boolean;
22
+ label?: string;
23
+ onselect?: (id: string | null) => void;
24
+ onexpand?: (id: string, node: GraphNode) => void;
25
+ oncollapse?: (id: string, node: GraphNode) => void;
26
+ class?: string;
27
+ };
28
+ declare const LayersDiagram: import("svelte").Component<Props, {}, "value" | "density" | "showEdges">;
29
+ type LayersDiagram = ReturnType<typeof LayersDiagram>;
30
+ export default LayersDiagram;
@@ -12,11 +12,12 @@
12
12
  import GraphLegend from '../GraphLegend.svelte'
13
13
  import DiagramFrame from './DiagramFrame.svelte'
14
14
  import DepthControl from '../controls/DepthControl.svelte'
15
+ import DrillBar from '../controls/DrillBar.svelte'
15
16
  import EdgeStyleControl from '../controls/EdgeStyleControl.svelte'
16
17
  import ZoomControl from '../controls/ZoomControl.svelte'
17
18
  import { GraphState } from '../GraphState.svelte.js'
18
19
  import type { EdgeStyle } from '../layout/types.js'
19
- import type { GraphFields } from '../types.js'
20
+ import type { GraphFields, GraphNode } from '../types.js'
20
21
  import type { GraphPreset } from '../preset.js'
21
22
 
22
23
  type Props = {
@@ -34,6 +35,12 @@
34
35
  /** How many hops out. One answers "what touches this"; two, "what does this reach". */
35
36
  depth?: number
36
37
  edgeStyle?: EdgeStyle
38
+ /**
39
+ * Mark each card with its group's colour (#172). On by default, as in `ErDiagram`: a
40
+ * neighbourhood has no group boxes either, so without it a table's schema is visible at
41
+ * the root and invisible one click later.
42
+ */
43
+ groupTint?: boolean
37
44
  controls?: boolean
38
45
  maxDepth?: number
39
46
  legend?: boolean
@@ -43,6 +50,10 @@
43
50
  arrows?: boolean
44
51
  label?: string
45
52
  onselect?: (id: string | null) => void
53
+ /** The reader expanded a group node (#166) — with its members in `nodes`, they appear at once. */
54
+ onexpand?: (id: string, node: GraphNode) => void
55
+ /** The reader collapsed a group back into one node. */
56
+ oncollapse?: (id: string, node: GraphNode) => void
46
57
  class?: string
47
58
  }
48
59
 
@@ -54,6 +65,7 @@
54
65
  focus = $bindable(null),
55
66
  depth = $bindable(1),
56
67
  edgeStyle = $bindable('curved'),
68
+ groupTint = true,
57
69
  controls = false,
58
70
  maxDepth = 3,
59
71
  legend = false,
@@ -63,6 +75,8 @@
63
75
  arrows = true,
64
76
  label = undefined,
65
77
  onselect = undefined,
78
+ onexpand = undefined,
79
+ oncollapse = undefined,
66
80
  class: className = ''
67
81
  }: Props = $props()
68
82
 
@@ -76,11 +90,14 @@
76
90
  focus,
77
91
  depth,
78
92
  edgeStyle,
93
+ groupTint,
79
94
  value,
80
95
  preset,
81
96
  mode,
82
97
  label,
83
- onselect
98
+ onselect,
99
+ onexpand,
100
+ oncollapse
84
101
  })
85
102
 
86
103
  // svelte-ignore state_referenced_locally
@@ -107,9 +124,12 @@
107
124
  inside is always defined, so the frame would render an empty control bar over
108
125
  every diagram that asked for none. -->
109
126
  {#snippet controlBar()}
110
- <DepthControl levels={depth} max={maxDepth} onchange={(v) => (depth = v)} />
111
- <EdgeStyleControl {edgeStyle} onchange={(v) => (edgeStyle = v)} />
112
- <ZoomControl {zoom} onchange={(v) => (zoom = v)} />
127
+ <DrillBar state={graph} />
128
+ {#if controls}
129
+ <DepthControl levels={depth} max={maxDepth} onchange={(v) => (depth = v)} />
130
+ <EdgeStyleControl {edgeStyle} onchange={(v) => (edgeStyle = v)} />
131
+ <ZoomControl {zoom} onchange={(v) => (zoom = v)} />
132
+ {/if}
113
133
  {/snippet}
114
134
 
115
135
  {#snippet legendBar()}
@@ -118,7 +138,7 @@
118
138
 
119
139
  <DiagramFrame
120
140
  class={className}
121
- overlay={controls ? controlBar : undefined}
141
+ overlay={controls || graph.showsDrillBar ? controlBar : undefined}
122
142
  footer={legend ? legendBar : undefined}
123
143
  >
124
144
  {#snippet canvas()}
@@ -1,6 +1,6 @@
1
1
  import { GraphState } from '../GraphState.svelte.js';
2
2
  import type { EdgeStyle } from '../layout/types.js';
3
- import type { GraphFields } from '../types.js';
3
+ import type { GraphFields, GraphNode } from '../types.js';
4
4
  import type { GraphPreset } from '../preset.js';
5
5
  type Props = {
6
6
  /**
@@ -17,6 +17,12 @@ type Props = {
17
17
  /** How many hops out. One answers "what touches this"; two, "what does this reach". */
18
18
  depth?: number;
19
19
  edgeStyle?: EdgeStyle;
20
+ /**
21
+ * Mark each card with its group's colour (#172). On by default, as in `ErDiagram`: a
22
+ * neighbourhood has no group boxes either, so without it a table's schema is visible at
23
+ * the root and invisible one click later.
24
+ */
25
+ groupTint?: boolean;
20
26
  controls?: boolean;
21
27
  maxDepth?: number;
22
28
  legend?: boolean;
@@ -26,6 +32,10 @@ type Props = {
26
32
  arrows?: boolean;
27
33
  label?: string;
28
34
  onselect?: (id: string | null) => void;
35
+ /** The reader expanded a group node (#166) — with its members in `nodes`, they appear at once. */
36
+ onexpand?: (id: string, node: GraphNode) => void;
37
+ /** The reader collapsed a group back into one node. */
38
+ oncollapse?: (id: string, node: GraphNode) => void;
29
39
  class?: string;
30
40
  };
31
41
  declare const Neighborhood: import("svelte").Component<Props, {}, "focus" | "value" | "edgeStyle" | "depth">;
@@ -0,0 +1,116 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Lanza & Marinescu's System Complexity view (#168): the containment tree, each file a box
4
+ * whose width, height and shade show three measures at once.
5
+ *
6
+ * `Treemap` encodes ONE measure as area; this encodes three, and the three-at-once is the
7
+ * point — a narrow, tall, dark box reads differently from a wide, short, pale one. Each
8
+ * channel is a prop and a picker, and the legend names them by default, because three
9
+ * encodings are unreadable without a key.
10
+ */
11
+ import Graph from '../Graph.svelte'
12
+ import GraphLegend from '../GraphLegend.svelte'
13
+ import DiagramFrame from './DiagramFrame.svelte'
14
+ import DrillBar from '../controls/DrillBar.svelte'
15
+ import MeasureControl from '../controls/MeasureControl.svelte'
16
+ import ZoomControl from '../controls/ZoomControl.svelte'
17
+ import { GraphState } from '../GraphState.svelte.js'
18
+ import type { GraphFields } from '../types.js'
19
+ import type { GraphPreset } from '../preset.js'
20
+
21
+ type Props = {
22
+ /** Share one state with another view — see `DependencyDiagram`. */
23
+ state?: GraphState
24
+ nodes?: unknown[]
25
+ edges?: unknown[]
26
+ fields?: GraphFields
27
+ /** What box WIDTH encodes: a `measures` key, `weight`, or `degree`. Bindable. */
28
+ widthBy?: string
29
+ /** What box HEIGHT encodes. Bindable. */
30
+ heightBy?: string
31
+ /** What box SHADE encodes; omit for no colour channel. Bindable. */
32
+ colorBy?: string
33
+ controls?: boolean
34
+ /** On by default: three encodings are unreadable without a key. */
35
+ legend?: boolean
36
+ value?: string | null
37
+ preset?: GraphPreset
38
+ mode?: 'light' | 'dark'
39
+ label?: string
40
+ onselect?: (id: string | null) => void
41
+ class?: string
42
+ }
43
+
44
+ let {
45
+ state: provided,
46
+ nodes = [],
47
+ edges = [],
48
+ fields = {},
49
+ widthBy = $bindable('weight'),
50
+ heightBy = $bindable('weight'),
51
+ colorBy = $bindable(undefined),
52
+ controls = false,
53
+ legend = true,
54
+ value = $bindable(undefined),
55
+ preset = undefined,
56
+ mode = 'light',
57
+ label = undefined,
58
+ onselect = undefined,
59
+ class: className = ''
60
+ }: Props = $props()
61
+
62
+ let zoom = $state(1)
63
+
64
+ const config = () => ({
65
+ nodes,
66
+ edges,
67
+ fields,
68
+ layout: 'polymetric',
69
+ widthBy,
70
+ heightBy,
71
+ colorBy,
72
+ value,
73
+ preset,
74
+ mode,
75
+ label,
76
+ onselect
77
+ })
78
+
79
+ // svelte-ignore state_referenced_locally
80
+ const graph = provided ?? new GraphState(config())
81
+
82
+ $effect(() => {
83
+ if (!provided) {
84
+ graph.update(config())
85
+ return
86
+ }
87
+ // A supplied state is the caller's — see DependencyDiagram.
88
+ const { nodes: _n, edges: _e, fields: _f, preset: _p, mode: _m, value: _v, ...mine } =
89
+ config()
90
+ graph.apply(mine)
91
+ })
92
+ </script>
93
+
94
+ {#snippet controlBar()}
95
+ <DrillBar state={graph} />
96
+ {#if controls}
97
+ <MeasureControl channel="width" value={widthBy} options={graph.measureKeys} onchange={(v) => (widthBy = v ?? 'weight')} />
98
+ <MeasureControl channel="height" value={heightBy} options={graph.measureKeys} onchange={(v) => (heightBy = v ?? 'weight')} />
99
+ <MeasureControl channel="color" value={colorBy} options={graph.measureKeys} allowNone onchange={(v) => (colorBy = v)} />
100
+ <ZoomControl {zoom} onchange={(v) => (zoom = v)} />
101
+ {/if}
102
+ {/snippet}
103
+
104
+ {#snippet legendBar()}
105
+ <GraphLegend state={graph} channels />
106
+ {/snippet}
107
+
108
+ <DiagramFrame
109
+ class={className}
110
+ overlay={controls || graph.showsDrillBar ? controlBar : undefined}
111
+ footer={legend ? legendBar : undefined}
112
+ >
113
+ {#snippet canvas()}
114
+ <Graph state={graph} bind:zoom arrows={false} />
115
+ {/snippet}
116
+ </DiagramFrame>
@@ -0,0 +1,28 @@
1
+ import { GraphState } from '../GraphState.svelte.js';
2
+ import type { GraphFields } from '../types.js';
3
+ import type { GraphPreset } from '../preset.js';
4
+ type Props = {
5
+ /** Share one state with another view — see `DependencyDiagram`. */
6
+ state?: GraphState;
7
+ nodes?: unknown[];
8
+ edges?: unknown[];
9
+ fields?: GraphFields;
10
+ /** What box WIDTH encodes: a `measures` key, `weight`, or `degree`. Bindable. */
11
+ widthBy?: string;
12
+ /** What box HEIGHT encodes. Bindable. */
13
+ heightBy?: string;
14
+ /** What box SHADE encodes; omit for no colour channel. Bindable. */
15
+ colorBy?: string;
16
+ controls?: boolean;
17
+ /** On by default: three encodings are unreadable without a key. */
18
+ legend?: boolean;
19
+ value?: string | null;
20
+ preset?: GraphPreset;
21
+ mode?: 'light' | 'dark';
22
+ label?: string;
23
+ onselect?: (id: string | null) => void;
24
+ class?: string;
25
+ };
26
+ declare const PolymetricTree: import("svelte").Component<Props, {}, "value" | "widthBy" | "heightBy" | "colorBy">;
27
+ type PolymetricTree = ReturnType<typeof PolymetricTree>;
28
+ export default PolymetricTree;
@@ -15,10 +15,11 @@
15
15
  import GraphLegend from '../GraphLegend.svelte'
16
16
  import DiagramFrame from './DiagramFrame.svelte'
17
17
  import DepthControl from '../controls/DepthControl.svelte'
18
+ import DrillBar from '../controls/DrillBar.svelte'
18
19
  import BundleControl from '../controls/BundleControl.svelte'
19
20
  import ZoomControl from '../controls/ZoomControl.svelte'
20
21
  import { GraphState } from '../GraphState.svelte.js'
21
- import type { GraphFields } from '../types.js'
22
+ import type { GraphFields, GraphNode } from '../types.js'
22
23
  import type { GraphPreset } from '../preset.js'
23
24
 
24
25
  type Props = {
@@ -44,6 +45,14 @@
44
45
  mode?: 'light' | 'dark'
45
46
  label?: string
46
47
  onselect?: (id: string | null) => void
48
+ /**
49
+ * The reader opened a box — `path` is the new `focusPath`, `node` the declared node there
50
+ * (null for a synthesised container). Return a promise while you load that level; the
51
+ * canvas shows it pending, and a rejection returns to the previous level.
52
+ */
53
+ ondrill?: (path: string[], node: GraphNode | null) => void | Promise<void>
54
+ /** The reader climbed out; `path` is the new, shorter `focusPath`. */
55
+ ondrillup?: (path: string[]) => void | Promise<void>
47
56
  class?: string
48
57
  }
49
58
 
@@ -65,6 +74,8 @@
65
74
  mode = 'light',
66
75
  label = undefined,
67
76
  onselect = undefined,
77
+ ondrill = undefined,
78
+ ondrillup = undefined,
68
79
  class: className = ''
69
80
  }: Props = $props()
70
81
 
@@ -84,7 +95,12 @@
84
95
  preset,
85
96
  mode,
86
97
  label,
87
- onselect
98
+ onselect,
99
+ ondrill,
100
+ ondrillup,
101
+ // Drilling moves focusPath inside the state; keep the bindable prop in step, or the next
102
+ // update() would put the old value back.
103
+ onfocuspath: (path: string[]) => (focusPath = path)
88
104
  })
89
105
 
90
106
  // svelte-ignore state_referenced_locally
@@ -101,8 +117,19 @@
101
117
  // and are never reset here. What this component DOES own is the options its own
102
118
  // controls drive — the layout above all, since choosing this component is choosing it.
103
119
  // `apply` merges; `update` would revert everything it does not name.
104
- const { nodes: _n, edges: _e, fields: _f, preset: _p, mode: _m, value: _v, ...mine } =
105
- config()
120
+ // The drill path too: a caller's state holds where the reader drilled, and pushing this
121
+ // component's own focusPath prop back over it undid the drill on any prop change.
122
+ const {
123
+ nodes: _n,
124
+ edges: _e,
125
+ fields: _f,
126
+ preset: _p,
127
+ mode: _m,
128
+ value: _v,
129
+ focusPath: _fp,
130
+ onfocuspath: _sync,
131
+ ...mine
132
+ } = config()
106
133
  graph.apply(mine)
107
134
  })
108
135
  </script>
@@ -111,9 +138,12 @@
111
138
  inside is always defined, so the frame would render an empty control bar over
112
139
  every diagram that asked for none. -->
113
140
  {#snippet controlBar()}
114
- <DepthControl {levels} max={maxLevels} onchange={(v) => (levels = v)} />
115
- <BundleControl {bundleTension} onchange={(v) => (bundleTension = v)} />
116
- <ZoomControl {zoom} onchange={(v) => (zoom = v)} />
141
+ <DrillBar state={graph} />
142
+ {#if controls}
143
+ <DepthControl {levels} max={maxLevels} onchange={(v) => (levels = v)} />
144
+ <BundleControl {bundleTension} onchange={(v) => (bundleTension = v)} />
145
+ <ZoomControl {zoom} onchange={(v) => (zoom = v)} />
146
+ {/if}
117
147
  {/snippet}
118
148
 
119
149
  {#snippet legendBar()}
@@ -122,7 +152,7 @@
122
152
 
123
153
  <DiagramFrame
124
154
  class={className}
125
- overlay={controls ? controlBar : undefined}
155
+ overlay={controls || graph.showsDrillBar ? controlBar : undefined}
126
156
  footer={legend ? legendBar : undefined}
127
157
  >
128
158
  {#snippet canvas()}
@@ -1,5 +1,5 @@
1
1
  import { GraphState } from '../GraphState.svelte.js';
2
- import type { GraphFields } from '../types.js';
2
+ import type { GraphFields, GraphNode } from '../types.js';
3
3
  import type { GraphPreset } from '../preset.js';
4
4
  type Props = {
5
5
  /** Share one state with views beside this one. See `ErDiagram` for the contract. */
@@ -24,6 +24,14 @@ type Props = {
24
24
  mode?: 'light' | 'dark';
25
25
  label?: string;
26
26
  onselect?: (id: string | null) => void;
27
+ /**
28
+ * The reader opened a box — `path` is the new `focusPath`, `node` the declared node there
29
+ * (null for a synthesised container). Return a promise while you load that level; the
30
+ * canvas shows it pending, and a rejection returns to the previous level.
31
+ */
32
+ ondrill?: (path: string[], node: GraphNode | null) => void | Promise<void>;
33
+ /** The reader climbed out; `path` is the new, shorter `focusPath`. */
34
+ ondrillup?: (path: string[]) => void | Promise<void>;
27
35
  class?: string;
28
36
  };
29
37
  declare const StructureDiagram: import("svelte").Component<Props, {}, "value" | "focusPath" | "levels" | "bundleTension">;
@@ -13,9 +13,11 @@
13
13
  import GraphLegend from '../GraphLegend.svelte'
14
14
  import DiagramFrame from './DiagramFrame.svelte'
15
15
  import DepthControl from '../controls/DepthControl.svelte'
16
+ import DrillBar from '../controls/DrillBar.svelte'
17
+ import MeasureControl from '../controls/MeasureControl.svelte'
16
18
  import ZoomControl from '../controls/ZoomControl.svelte'
17
19
  import { GraphState } from '../GraphState.svelte.js'
18
- import type { GraphFields } from '../types.js'
20
+ import type { GraphFields, GraphNode } from '../types.js'
19
21
  import type { GraphPreset } from '../preset.js'
20
22
 
21
23
  type Props = {
@@ -30,6 +32,8 @@
30
32
  fields?: GraphFields
31
33
  /** What area encodes. A `measures` key, or `weight`, or `degree`. */
32
34
  sizeBy?: string
35
+ /** The measure — a 0..1 share — each box is shaded by (#164). Bindable; the picker moves it. */
36
+ shadeBy?: string
33
37
  /** How many levels below the focus to materialise. */
34
38
  levels?: number
35
39
  /** The subtree to render as the whole canvas. `[]` is the root. Drilling, not zooming. */
@@ -42,6 +46,14 @@
42
46
  mode?: 'light' | 'dark'
43
47
  label?: string
44
48
  onselect?: (id: string | null) => void
49
+ /**
50
+ * The reader opened a box — `path` is the new `focusPath`, `node` the declared node there
51
+ * (null for a synthesised container). Return a promise while you load that level; the
52
+ * canvas shows it pending, and a rejection returns to the previous level.
53
+ */
54
+ ondrill?: (path: string[], node: GraphNode | null) => void | Promise<void>
55
+ /** The reader climbed out; `path` is the new, shorter `focusPath`. */
56
+ ondrillup?: (path: string[]) => void | Promise<void>
45
57
  class?: string
46
58
  }
47
59
 
@@ -51,6 +63,7 @@
51
63
  edges = [],
52
64
  fields = {},
53
65
  sizeBy = 'weight',
66
+ shadeBy = $bindable(undefined),
54
67
  levels = $bindable(2),
55
68
  focusPath = $bindable([]),
56
69
  controls = false,
@@ -61,6 +74,8 @@
61
74
  mode = 'light',
62
75
  label = undefined,
63
76
  onselect = undefined,
77
+ ondrill = undefined,
78
+ ondrillup = undefined,
64
79
  class: className = ''
65
80
  }: Props = $props()
66
81
 
@@ -72,13 +87,19 @@
72
87
  fields,
73
88
  layout: 'sunburst',
74
89
  sizeBy,
90
+ shadeBy,
75
91
  levels,
76
92
  focusPath,
77
93
  value,
78
94
  preset,
79
95
  mode,
80
96
  label,
81
- onselect
97
+ onselect,
98
+ ondrill,
99
+ ondrillup,
100
+ // Drilling moves focusPath inside the state; keep the bindable prop in step, or the next
101
+ // update() would put the old value back.
102
+ onfocuspath: (path: string[]) => (focusPath = path)
82
103
  })
83
104
 
84
105
  // svelte-ignore state_referenced_locally
@@ -95,8 +116,19 @@
95
116
  // and are never reset here. What this component DOES own is the options its own
96
117
  // controls drive — the layout above all, since choosing this component is choosing it.
97
118
  // `apply` merges; `update` would revert everything it does not name.
98
- const { nodes: _n, edges: _e, fields: _f, preset: _p, mode: _m, value: _v, ...mine } =
99
- config()
119
+ // The drill path too: a caller's state holds where the reader drilled, and pushing this
120
+ // component's own focusPath prop back over it undid the drill on any prop change.
121
+ const {
122
+ nodes: _n,
123
+ edges: _e,
124
+ fields: _f,
125
+ preset: _p,
126
+ mode: _m,
127
+ value: _v,
128
+ focusPath: _fp,
129
+ onfocuspath: _sync,
130
+ ...mine
131
+ } = config()
100
132
  graph.apply(mine)
101
133
  })
102
134
  </script>
@@ -105,17 +137,21 @@
105
137
  inside is always defined, so the frame would render an empty control bar over
106
138
  every diagram that asked for none. -->
107
139
  {#snippet controlBar()}
108
- <DepthControl {levels} max={maxLevels} onchange={(v) => (levels = v)} />
109
- <ZoomControl {zoom} onchange={(v) => (zoom = v)} />
140
+ <DrillBar state={graph} />
141
+ {#if controls}
142
+ <DepthControl {levels} max={maxLevels} onchange={(v) => (levels = v)} />
143
+ <MeasureControl channel="color" value={shadeBy} options={graph.measureKeys} allowNone onchange={(v) => (shadeBy = v)} />
144
+ <ZoomControl {zoom} onchange={(v) => (zoom = v)} />
145
+ {/if}
110
146
  {/snippet}
111
147
 
112
148
  {#snippet legendBar()}
113
- <GraphLegend state={graph} groups />
149
+ <GraphLegend state={graph} groups shade />
114
150
  {/snippet}
115
151
 
116
152
  <DiagramFrame
117
153
  class={className}
118
- overlay={controls ? controlBar : undefined}
154
+ overlay={controls || graph.showsDrillBar ? controlBar : undefined}
119
155
  footer={legend ? legendBar : undefined}
120
156
  >
121
157
  {#snippet canvas()}