@rokkit/chart 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +13 -0
  2. package/dist/Plot.svelte +38 -10
  3. package/dist/Plot.svelte.d.ts +7 -2
  4. package/dist/PlotState.svelte.d.ts +115 -71
  5. package/dist/PlotState.svelte.js +174 -547
  6. package/dist/SparkState.svelte.d.ts +3 -1
  7. package/dist/SparkState.svelte.js +11 -41
  8. package/dist/geoms/Contour.svelte +88 -0
  9. package/dist/geoms/Contour.svelte.d.ts +19 -0
  10. package/dist/geoms/Hull.svelte +89 -0
  11. package/dist/geoms/Hull.svelte.d.ts +17 -0
  12. package/dist/geoms/Region.svelte +107 -0
  13. package/dist/geoms/Region.svelte.d.ts +27 -0
  14. package/dist/geoms/Rule.svelte +88 -26
  15. package/dist/geoms/Rule.svelte.d.ts +8 -0
  16. package/dist/geoms/lib/PlotAreaClip.svelte +18 -0
  17. package/dist/geoms/lib/PlotAreaClip.svelte.d.ts +6 -0
  18. package/dist/geoms/lib/bars.js +13 -20
  19. package/dist/geoms/lib/grouping.d.ts +35 -0
  20. package/dist/geoms/lib/grouping.js +59 -0
  21. package/dist/geoms/lib/marks/contour.d.ts +26 -0
  22. package/dist/geoms/lib/marks/contour.js +67 -0
  23. package/dist/geoms/lib/marks/hull.d.ts +26 -0
  24. package/dist/geoms/lib/marks/hull.js +36 -0
  25. package/dist/geoms/lib/marks/waterfall.js +15 -20
  26. package/dist/index.d.ts +9 -0
  27. package/dist/index.js +9 -0
  28. package/dist/lib/abline.d.ts +41 -0
  29. package/dist/lib/abline.js +51 -0
  30. package/dist/lib/hull.d.ts +18 -0
  31. package/dist/lib/hull.js +64 -0
  32. package/dist/lib/plot/domains.d.ts +61 -0
  33. package/dist/lib/plot/domains.js +130 -0
  34. package/dist/lib/plot/running.d.ts +23 -0
  35. package/dist/lib/plot/running.js +32 -0
  36. package/dist/lib/plot/stacking.d.ts +32 -0
  37. package/dist/lib/plot/stacking.js +34 -0
  38. package/dist/lib/plot/types.d.ts +8 -1
  39. package/dist/lib/plot/types.js +5 -1
  40. package/dist/lib/preset.d.ts +2 -0
  41. package/dist/lib/preset.js +76 -41
  42. package/dist/lib/region.d.ts +63 -0
  43. package/dist/lib/region.js +127 -0
  44. package/dist/state/AestheticState.svelte.d.ts +43 -0
  45. package/dist/state/AestheticState.svelte.js +84 -0
  46. package/dist/state/AxisState.svelte.d.ts +26 -0
  47. package/dist/state/AxisState.svelte.js +56 -0
  48. package/dist/state/ChannelState.svelte.d.ts +34 -0
  49. package/dist/state/ChannelState.svelte.js +67 -0
  50. package/dist/state/GeomRegistry.svelte.d.ts +44 -0
  51. package/dist/state/GeomRegistry.svelte.js +86 -0
  52. package/dist/state/InteractionState.svelte.d.ts +30 -0
  53. package/dist/state/InteractionState.svelte.js +69 -0
  54. package/dist/state/OrientationState.svelte.d.ts +34 -0
  55. package/dist/state/OrientationState.svelte.js +64 -0
  56. package/dist/state/PlotConfig.svelte.d.ts +179 -0
  57. package/dist/state/PlotConfig.svelte.js +195 -0
  58. package/dist/state/PlotFrame.svelte.d.ts +17 -0
  59. package/dist/state/PlotFrame.svelte.js +18 -0
  60. package/dist/state/ScaleState.svelte.d.ts +36 -0
  61. package/dist/state/ScaleState.svelte.js +121 -0
  62. package/package.json +5 -3
package/README.md CHANGED
@@ -100,6 +100,19 @@ const dimensions = brewer.getDimensions()
100
100
  | `Plot.Grid` | Background grid lines derived from axis scales. |
101
101
  | `Plot.Legend` | Color legend; clicking items filters the visible series. |
102
102
 
103
+ Every geom is on the namespace too — `Line`, `Area`, `Point`, `Arc`, `Box`, `Violin`, `Jitter`,
104
+ `Heatmap`, `Hexbin`, `Candlestick`, `Waterfall`, `Ribbon`, `Radar`, `Highlight`, `Trend` — plus four
105
+ that annotate the metric plane rather than draw rows:
106
+
107
+ | Component | Description |
108
+ | -------------- | --------------------------------------------------------------------------------------------- |
109
+ | `Plot.Rule` | Reference lines at `x` / `y` values, or `slope` + `intercept`, clipped to the plot. |
110
+ | `Plot.Region` | Shaded band (`[lo, hi]`, `null` = open end) or polygon (`points`) in data coordinates. |
111
+ | `Plot.Hull` | Padded convex hull around each group of the `fill` / `color` field. |
112
+ | `Plot.Contour` | Kernel-density contours per group — rings, or `filled` bands. |
113
+
114
+ Together they draw architecture charts such as Robert Martin's main sequence — see the Charts guide.
115
+
103
116
  ### `Plot.Root` props
104
117
 
105
118
  | Prop | Type | Default | Description |
package/dist/Plot.svelte CHANGED
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { getContext } from 'svelte'
3
- import type { Snippet } from 'svelte'
3
+ import type { Component, Snippet } from 'svelte'
4
4
  import { defaultPreset } from './lib/preset.js'
5
5
  import type { PlotSpec, PlotHelpers } from './lib/plot/types.js'
6
6
  import PlotSurface from './PlotSurface.svelte'
@@ -21,6 +21,10 @@
21
21
  import Hexbin from './geoms/Hexbin.svelte'
22
22
  import Ribbon from './geoms/Ribbon.svelte'
23
23
  import Radar from './geoms/Radar.svelte'
24
+ import Rule from './geoms/Rule.svelte'
25
+ import Region from './geoms/Region.svelte'
26
+ import Hull from './geoms/Hull.svelte'
27
+ import Contour from './geoms/Contour.svelte'
24
28
  import Highlight from './geoms/Highlight.svelte'
25
29
  import Trend from './geoms/Trend.svelte'
26
30
 
@@ -66,6 +70,11 @@
66
70
  axisOrigin?: [number | undefined, number | undefined]
67
71
  /** Nudge an edge-pinned axis outward by N px (Q1 only). Also via `spec.axisOffset`. */
68
72
  axisOffset?: number
73
+ /** Fix the x domain instead of inferring it from the data — e.g. `[0, 1]` for a metric
74
+ * plane defined by theory, not by where the rows fall. Also via `spec.xDomain`. */
75
+ xDomain?: unknown[]
76
+ /** Fix the y domain. Also via `spec.yDomain`. */
77
+ yDomain?: unknown[]
69
78
  /** Animate marks on data/flip changes (opt-out for e.g. AnimatedPlot, which
70
79
  * tweens its own frames). Enabled one frame after the width settles so the
71
80
  * initial layout paints un-animated. Default `true`. */
@@ -104,6 +113,8 @@
104
113
  selected = $bindable([]),
105
114
  axisOrigin = undefined,
106
115
  axisOffset = undefined,
116
+ xDomain = undefined,
117
+ yDomain = undefined,
107
118
  animate = true,
108
119
  children
109
120
  }: Props = $props()
@@ -145,8 +156,8 @@
145
156
  channels: spec ? { x: spec.x, y: spec.y, color: spec.color ?? spec.fill } : {},
146
157
  labels: spec?.labels ?? {},
147
158
  helpers,
148
- xDomain: spec?.xDomain,
149
- yDomain: spec?.yDomain,
159
+ xDomain: spec?.xDomain ?? xDomain,
160
+ yDomain: spec?.yDomain ?? yDomain,
150
161
  colorDomain: spec?.colorDomain,
151
162
  colorScale: spec?.colorScale,
152
163
  colorScheme: spec?.colorScheme,
@@ -168,8 +179,13 @@
168
179
  const overlayX = $derived(spec?.x ?? x)
169
180
  const overlayY = $derived(spec?.y ?? y)
170
181
 
171
- // Geom component resolver for spec-driven mode
172
- const GEOM_COMPONENTS = {
182
+ // Geom component resolver for spec-driven mode. Typed as a lookup table, not the union of
183
+ // its members: a spec names geoms by string at runtime, exactly like `helpers.geoms`, and the
184
+ // union of every geom's Props has no single shape a generic call site can satisfy.
185
+ // `any` is the honest type for a heterogeneous table resolved by name — the same one the
186
+ // public `PlotHelpers.geoms` already declares in lib/plot/types.js.
187
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
188
+ const GEOM_COMPONENTS: Record<string, Component<any>> = {
173
189
  bar: Bar,
174
190
  line: Line,
175
191
  area: Area,
@@ -184,11 +200,18 @@
184
200
  ribbon: Ribbon,
185
201
  // Radar reads the generic x/y/color this path passes via its own aliases, and takes
186
202
  // its axis order from `options.axes` — see Radar.svelte's Props.
187
- radar: Radar
203
+ radar: Radar,
204
+ hull: Hull,
205
+ contour: Contour
188
206
  }
189
207
 
208
+ // Plane annotations read no data, so they take ONLY their own `props` — never the spec's
209
+ // field channels: a Region's `x`/`y` are ranges, and handing it the field name 'instability'
210
+ // would be a range of one string.
211
+ const ANNOTATIONS = { rule: Rule, region: Region }
212
+
190
213
  function resolveGeomComponent(type: string) {
191
- return helpers?.geoms?.[type] ?? GEOM_COMPONENTS[type as keyof typeof GEOM_COMPONENTS]
214
+ return helpers?.geoms?.[type] ?? GEOM_COMPONENTS[type]
192
215
  }
193
216
  </script>
194
217
 
@@ -215,10 +238,14 @@
215
238
  <!-- Declarative children (geom components) -->
216
239
  {@render children?.()}
217
240
 
218
- <!-- Spec-driven geoms -->
219
- {#each specGeoms as geomSpec (geomSpec.type)}
241
+ <!-- Spec-driven geoms. Keyed by position, not type: two regions in one spec are normal, and
242
+ a type key would be a duplicate that aborts the whole render. -->
243
+ {#each specGeoms as geomSpec, i (`${geomSpec.type}-${i}`)}
244
+ {@const Annotation = ANNOTATIONS[geomSpec.type as keyof typeof ANNOTATIONS]}
220
245
  {@const GeomComponent = resolveGeomComponent(geomSpec.type)}
221
- {#if GeomComponent}
246
+ {#if Annotation && !helpers?.geoms?.[geomSpec.type]}
247
+ <Annotation {...geomSpec.props ?? {}} />
248
+ {:else if GeomComponent}
222
249
  <GeomComponent
223
250
  x={geomSpec.x ?? spec?.x}
224
251
  y={geomSpec.y ?? spec?.y}
@@ -233,6 +260,7 @@
233
260
  ...(spec?.orientation !== undefined ? { orientation: spec.orientation } : {}),
234
261
  ...(geomSpec.options ?? {})
235
262
  }}
263
+ {...geomSpec.props ?? {}}
236
264
  />
237
265
  {/if}
238
266
  {/each}
@@ -1,4 +1,4 @@
1
- import type { Snippet } from 'svelte';
1
+ import type { Component, Snippet } from 'svelte';
2
2
  import type { PlotSpec, PlotHelpers } from './lib/plot/types.js';
3
3
  type Row = Record<string, unknown>;
4
4
  type Method = string | number | {
@@ -48,12 +48,17 @@ type Props = {
48
48
  axisOrigin?: [number | undefined, number | undefined];
49
49
  /** Nudge an edge-pinned axis outward by N px (Q1 only). Also via `spec.axisOffset`. */
50
50
  axisOffset?: number;
51
+ /** Fix the x domain instead of inferring it from the data — e.g. `[0, 1]` for a metric
52
+ * plane defined by theory, not by where the rows fall. Also via `spec.xDomain`. */
53
+ xDomain?: unknown[];
54
+ /** Fix the y domain. Also via `spec.yDomain`. */
55
+ yDomain?: unknown[];
51
56
  /** Animate marks on data/flip changes (opt-out for e.g. AnimatedPlot, which
52
57
  * tweens its own frames). Enabled one frame after the width settles so the
53
58
  * initial layout paints un-animated. Default `true`. */
54
59
  animate?: boolean;
55
60
  children?: Snippet;
56
61
  };
57
- declare const Plot: import("svelte").Component<Props, {}, "selected">;
62
+ declare const Plot: Component<Props, {}, "selected">;
58
63
  type Plot = ReturnType<typeof Plot>;
59
64
  export default Plot;
@@ -1,73 +1,41 @@
1
+ /**
2
+ * The state a `PlotChart` publishes on the `'plot-state'` context — a COMPOSITION of job
3
+ * classes (`./state/`), each owning one concern and reading only the ones built before it:
4
+ *
5
+ * config → geoms → frame → channels → orientation → interaction → scales → aesthetics → axes
6
+ *
7
+ * It derives nothing itself. Everything below the constructor is the contract geoms, axes, the
8
+ * legend and the tooltip read (`GEOM_CONTRACT` in SparkState.svelte.js is the geom-facing part),
9
+ * kept as one-line delegations so no consumer changed when the monolith was taken apart. New
10
+ * code can depend on a job directly — `plotState.scales`, `plotState.interactionState` — instead.
11
+ */
1
12
  export class PlotState {
2
13
  constructor(config?: {});
3
- axisOrigin: undefined[];
4
- orientation: string;
5
- colorScaleType: any;
6
- continuousColorScale: {
7
- scale: (v: number) => string;
8
- domain: [number, number];
9
- type: "sequential";
10
- } | {
11
- scale: (v: number) => string;
12
- domain: [number, number, number];
13
- type: "diverging";
14
- } | null;
15
- xScale: any;
16
- yScale: any;
17
- colors: Map<unknown, {
18
- fill: string;
19
- stroke: string;
20
- }>;
21
- patterns: Map<unknown, string> | SvelteMap<any, any>;
22
- symbols: Map<unknown, string> | SvelteMap<any, any>;
23
- colorField: any;
24
- fillField: any;
25
- patternField: any;
26
- symbolField: any;
27
- geomTypes: SvelteSet<any>;
28
- xAxisY: any;
29
- yAxisX: any;
14
+ /** The plot's inputs — the only thing `update()` writes. */
15
+ config: PlotConfig;
16
+ /** The mounted geoms and the rows each draws. */
17
+ geoms: GeomRegistry;
18
+ /** The drawing area: size less margin. */
19
+ frame: PlotFrame;
20
+ /** Which field feeds each aesthetic. Suffixed: `channels` is the contract's raw map. */
21
+ channelState: ChannelState;
22
+ /** Which way the chart reads. Suffixed: `orientation` is the contract's string. */
23
+ orientationState: OrientationState;
24
+ /** Hover, selection, zoom. */
25
+ interactionState: InteractionState;
26
+ /** The x / y position scales. */
27
+ scales: ScaleState;
28
+ /** Palette, patterns, symbols, continuous colour. */
29
+ aesthetics: AestheticState;
30
+ /** Where the axes cross. */
31
+ axes: AxisState;
32
+ /** Re-apply inputs. Which omitted fields keep and which reset: `CONFIG_FIELDS`. */
30
33
  update(config: any): void;
31
- registerGeom(config: any): string;
32
- updateGeom(id: any, config: any): void;
33
- unregisterGeom(id: any): void;
34
- geomData(id: any): any[];
35
- label(field: any): any;
36
- format(field: any): any;
37
- tooltip(): any;
38
- geomComponent(type: any): any;
39
- preset(): {
40
- colors: any;
41
- patterns: any;
42
- symbols: any;
43
- };
44
- get data(): any[];
45
- /** @returns {{ x?: string, y?: string, color?: string, fill?: string, pattern?: string, symbol?: string }} */
46
- get channels(): {
47
- x?: string;
48
- y?: string;
49
- color?: string;
50
- fill?: string;
51
- pattern?: string;
52
- symbol?: string;
53
- };
54
- get isFlipped(): boolean;
55
- get continuousCategory(): boolean;
56
- place(u: any, v: any): {
57
- x: any;
58
- y: any;
59
- };
60
- get bandScale(): any;
61
- get valueScale(): any;
62
- get margin(): {
63
- top: number;
64
- right: number;
65
- bottom: number;
66
- left: number;
67
- };
68
- get innerWidth(): number;
69
- get innerHeight(): number;
70
- get mode(): string;
34
+ /** The rows — the same objects every identity geom is handed, so `indexOf(row)` works. */
35
+ get data(): import("./state/PlotConfig.svelte.js").Row[];
36
+ /** The caller's channel map, unmerged. `channelState.effective` is the merged one. */
37
+ get channels(): import("./state/PlotConfig.svelte.js").Channels;
38
+ get mode(): "light" | "dark";
71
39
  get chartPreset(): {
72
40
  colors: string[];
73
41
  shades: {
@@ -95,15 +63,85 @@ export class PlotState {
95
63
  ribbon: number;
96
64
  waterfall: number;
97
65
  rule: number;
66
+ hull: number;
67
+ contour: number;
98
68
  radar: number;
99
69
  };
100
70
  patterns: string[];
101
71
  symbols: string[];
102
72
  };
73
+ /** A continuous category axis (a bar race's tweened rank) rather than a band. */
74
+ get continuousCategory(): boolean;
75
+ set axisOrigin(value: [number | undefined, number | undefined]);
76
+ /** Pin the axis crossing directly — also reachable through `update({ axisOrigin })`. */
77
+ get axisOrigin(): [number | undefined, number | undefined];
78
+ label(field: any): string;
79
+ format(field: any): any;
80
+ tooltip(): any;
81
+ geomComponent(type: any): any;
82
+ preset(): {
83
+ colors: any;
84
+ patterns: any;
85
+ symbols: any;
86
+ };
87
+ registerGeom(config: any): string;
88
+ updateGeom(id: any, config: any): void;
89
+ unregisterGeom(id: any): void;
90
+ geomData(id: any): Object[];
91
+ /** The legend picks a swatch style from these. */
92
+ get geomTypes(): import("svelte/reactivity").SvelteSet<any>;
93
+ get margin(): Readonly<{
94
+ top: 20;
95
+ right: 20;
96
+ bottom: 40;
97
+ left: 50;
98
+ }> | import("./state/PlotConfig.svelte.js").Margin;
99
+ get innerWidth(): number;
100
+ get innerHeight(): number;
101
+ /** Null for a literal CSS colour — it maps to no field. */
102
+ get colorField(): any;
103
+ get fillField(): any;
104
+ get patternField(): any;
105
+ get symbolField(): any;
106
+ get orientation(): string;
107
+ /** Rendered horizontally: the band axis stands up the screen. Channels are unchanged. */
108
+ get isFlipped(): boolean;
109
+ /** Channel space to screen: `const { x, y } = plotState.place(xScale(d[x]), yScale(d[y]))`. */
110
+ place(u: any, v: any): {
111
+ x: any;
112
+ y: any;
113
+ };
114
+ get xScale(): any;
115
+ get yScale(): any;
116
+ /** The categorical scale, whichever screen axis it is on. */
117
+ get bandScale(): any;
118
+ /** The value scale, whichever screen axis it is on. */
119
+ get valueScale(): any;
120
+ get colors(): Map<unknown, {
121
+ fill: string;
122
+ stroke: string;
123
+ }> | Map<null, {
124
+ fill: any;
125
+ stroke: any;
126
+ }>;
127
+ get patterns(): Map<unknown, string> | import("svelte/reactivity").SvelteMap<any, any>;
128
+ get symbols(): Map<unknown, string> | import("svelte/reactivity").SvelteMap<any, any>;
129
+ get colorScaleType(): any;
130
+ get continuousColorScale(): {
131
+ scale: (v: number) => string;
132
+ domain: [number, number];
133
+ type: "sequential";
134
+ } | {
135
+ scale: (v: number) => string;
136
+ domain: [number, number, number];
137
+ type: "diverging";
138
+ } | null;
139
+ get xAxisY(): any;
140
+ get yAxisX(): any;
103
141
  get hovered(): null;
104
142
  get interactive(): boolean;
105
143
  get selectedRows(): any[];
106
- setHovered(data: any): void;
144
+ setHovered(row: any): void;
107
145
  clearHovered(): void;
108
146
  isSelected(row: any): boolean;
109
147
  setSelected(rows: any): void;
@@ -111,7 +149,13 @@ export class PlotState {
111
149
  handleSelect(detail: any): void;
112
150
  applyZoom(transform: any): void;
113
151
  resetZoom(): void;
114
- #private;
115
152
  }
116
- import { SvelteMap } from 'svelte/reactivity';
117
- import { SvelteSet } from 'svelte/reactivity';
153
+ import { PlotConfig } from './state/PlotConfig.svelte.js';
154
+ import { GeomRegistry } from './state/GeomRegistry.svelte.js';
155
+ import { PlotFrame } from './state/PlotFrame.svelte.js';
156
+ import { ChannelState } from './state/ChannelState.svelte.js';
157
+ import { OrientationState } from './state/OrientationState.svelte.js';
158
+ import { InteractionState } from './state/InteractionState.svelte.js';
159
+ import { ScaleState } from './state/ScaleState.svelte.js';
160
+ import { AestheticState } from './state/AestheticState.svelte.js';
161
+ import { AxisState } from './state/AxisState.svelte.js';