@rokkit/chart 1.3.14 → 1.4.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 (48) hide show
  1. package/dist/AnimatedPlot.svelte +4 -1
  2. package/dist/Plot.svelte +36 -9
  3. package/dist/Plot.svelte.d.ts +7 -0
  4. package/dist/PlotState.svelte.d.ts +1 -0
  5. package/dist/PlotState.svelte.js +10 -1
  6. package/dist/Spark.svelte +165 -0
  7. package/dist/Spark.svelte.d.ts +31 -0
  8. package/dist/SparkState.svelte.d.ts +138 -0
  9. package/dist/SparkState.svelte.js +331 -0
  10. package/dist/Sparkline.svelte +59 -179
  11. package/dist/Sparkline.svelte.d.ts +2 -0
  12. package/dist/charts/RadarChart.svelte +106 -0
  13. package/dist/charts/RadarChart.svelte.d.ts +43 -0
  14. package/dist/geoms/Area.svelte +17 -2
  15. package/dist/geoms/Area.svelte.d.ts +2 -0
  16. package/dist/geoms/Bar.svelte +2 -9
  17. package/dist/geoms/Highlight.svelte +2 -7
  18. package/dist/geoms/Line.svelte +2 -8
  19. package/dist/geoms/Point.svelte +2 -8
  20. package/dist/geoms/Radar.svelte +352 -0
  21. package/dist/geoms/Radar.svelte.d.ts +42 -0
  22. package/dist/geoms/lib/aesthetics.d.ts +14 -0
  23. package/dist/geoms/lib/aesthetics.js +21 -0
  24. package/dist/geoms/lib/areas.d.ts +9 -2
  25. package/dist/geoms/lib/areas.js +53 -8
  26. package/dist/geoms/lib/marks/area.d.ts +3 -0
  27. package/dist/geoms/lib/marks/area.js +11 -2
  28. package/dist/geoms/lib/marks/radar.d.ts +68 -0
  29. package/dist/geoms/lib/marks/radar.js +153 -0
  30. package/dist/index.d.ts +6 -0
  31. package/dist/index.js +7 -1
  32. package/dist/lib/brewing/marks/boxes.js +19 -30
  33. package/dist/lib/brewing/polar.d.ts +433 -0
  34. package/dist/lib/brewing/polar.js +584 -0
  35. package/dist/lib/plot/scales.js +8 -5
  36. package/dist/lib/plot/types.d.ts +1 -0
  37. package/dist/lib/plot/types.js +1 -0
  38. package/dist/lib/preset.d.ts +1 -0
  39. package/dist/lib/preset.js +2 -1
  40. package/dist/lib/select.d.ts +3 -3
  41. package/dist/lib/select.js +1 -1
  42. package/package.json +1 -1
  43. package/dist/examples/BarChartExample.svelte +0 -78
  44. package/dist/examples/BarChartExample.svelte.d.ts +0 -18
  45. package/dist/lib/brewing/types.d.ts +0 -162
  46. package/dist/lib/brewing/types.js +0 -73
  47. package/dist/lib/xscale.d.ts +0 -11
  48. package/dist/lib/xscale.js +0 -31
@@ -202,7 +202,10 @@
202
202
  })
203
203
 
204
204
  onDestroy(() => {
205
- cancelAnimationFrame(rafId)
205
+ // onDestroy is the one lifecycle hook that also runs during SSR, where
206
+ // cancelAnimationFrame is undefined — guard it so the component can be
207
+ // server-rendered / prerendered (rafId is only ever set in the browser).
208
+ if (typeof cancelAnimationFrame !== 'undefined') cancelAnimationFrame(rafId)
206
209
  clearInterval(reducedInterval)
207
210
  })
208
211
 
package/dist/Plot.svelte CHANGED
@@ -20,6 +20,7 @@
20
20
  import Waterfall from './geoms/Waterfall.svelte'
21
21
  import Hexbin from './geoms/Hexbin.svelte'
22
22
  import Ribbon from './geoms/Ribbon.svelte'
23
+ import Radar from './geoms/Radar.svelte'
23
24
  import Highlight from './geoms/Highlight.svelte'
24
25
  import Trend from './geoms/Trend.svelte'
25
26
 
@@ -58,6 +59,13 @@
58
59
  onselect?: (detail: unknown) => void
59
60
  selectable?: boolean
60
61
  selected?: Row[]
62
+ /** Data-coordinate crossing point of the axes: `[xCross, yCross]`. Drives
63
+ * quadrant mode — where the origin sits relative to the domain makes a chart
64
+ * 1- / 2- / 4-quadrant. Omit a component to leave that axis auto (crosses at 0
65
+ * when the domain spans it, else edge-pinned). Also settable via `spec.axisOrigin`. */
66
+ axisOrigin?: [number | undefined, number | undefined]
67
+ /** Nudge an edge-pinned axis outward by N px (Q1 only). Also via `spec.axisOffset`. */
68
+ axisOffset?: number
61
69
  /** Animate marks on data/flip changes (opt-out for e.g. AnimatedPlot, which
62
70
  * tweens its own frames). Enabled one frame after the width settles so the
63
71
  * initial layout paints un-animated. Default `true`. */
@@ -94,6 +102,8 @@
94
102
  onselect = undefined,
95
103
  selectable = false,
96
104
  selected = $bindable([]),
105
+ axisOrigin = undefined,
106
+ axisOffset = undefined,
97
107
  animate = true,
98
108
  children
99
109
  }: Props = $props()
@@ -103,9 +113,7 @@
103
113
 
104
114
  const gridValue = $derived(spec?.grid ?? grid)
105
115
  const showGrid = $derived(gridValue !== false)
106
- const gridLines = $derived(
107
- gridValue === true || gridValue === false ? 'auto' : gridValue
108
- )
116
+ const gridLines = $derived(gridValue === true || gridValue === false ? 'auto' : gridValue)
109
117
  const showLegend = $derived(spec?.legend ?? legend)
110
118
  const chartTitle = $derived(spec?.title ?? title)
111
119
  const chartSummary = $derived(spec?.summary ?? summary)
@@ -115,9 +123,11 @@
115
123
  const tableColumns = $derived.by(() => {
116
124
  // Dedupe: a channel may repeat (e.g. x === color), and the table's keyed each
117
125
  // requires unique column names.
118
- const cols = [...new Set(
119
- [spec?.x, spec?.y, spec?.color ?? spec?.fill].filter((c): c is string => Boolean(c))
120
- )]
126
+ const cols = [
127
+ ...new Set(
128
+ [spec?.x, spec?.y, spec?.color ?? spec?.fill].filter((c): c is string => Boolean(c))
129
+ )
130
+ ]
121
131
  if (cols.length > 0) return cols
122
132
  const first = tableData[0]
123
133
  return first ? Object.keys(first) : []
@@ -142,6 +152,8 @@
142
152
  colorScheme: spec?.colorScheme,
143
153
  colorMidpoint: spec?.colorMidpoint,
144
154
  orientation: orientation ?? spec?.orientation,
155
+ axisOrigin: spec?.axisOrigin ?? axisOrigin,
156
+ axisOffset: spec?.axisOffset ?? axisOffset,
145
157
  sort: spec?.sort,
146
158
  continuousCategory: spec?.continuousCategory,
147
159
  chartPreset,
@@ -169,7 +181,10 @@
169
181
  candlestick: Candlestick,
170
182
  waterfall: Waterfall,
171
183
  hexbin: Hexbin,
172
- ribbon: Ribbon
184
+ ribbon: Ribbon,
185
+ // Radar reads the generic x/y/color this path passes via its own aliases, and takes
186
+ // its axis order from `options.axes` — see Radar.svelte's Props.
187
+ radar: Radar
173
188
  }
174
189
 
175
190
  function resolveGeomComponent(type: string) {
@@ -224,8 +239,20 @@
224
239
 
225
240
  <!-- Axes -->
226
241
  {#if axes}
227
- <Axis type="x" label={spec?.labels?.[spec?.x ?? ''] ?? ''} format={xFormat} ticks={xTicks} {minorTicks} />
228
- <Axis type="y" label={spec?.labels?.[spec?.y ?? ''] ?? ''} format={yFormat} ticks={yTicks} {minorTicks} />
242
+ <Axis
243
+ type="x"
244
+ label={spec?.labels?.[spec?.x ?? ''] ?? ''}
245
+ format={xFormat}
246
+ ticks={xTicks}
247
+ {minorTicks}
248
+ />
249
+ <Axis
250
+ type="y"
251
+ label={spec?.labels?.[spec?.y ?? ''] ?? ''}
252
+ format={yFormat}
253
+ ticks={yTicks}
254
+ {minorTicks}
255
+ />
229
256
  {/if}
230
257
 
231
258
  {#if trend !== null && trend !== undefined}
@@ -41,6 +41,13 @@ type Props = {
41
41
  onselect?: (detail: unknown) => void;
42
42
  selectable?: boolean;
43
43
  selected?: Row[];
44
+ /** Data-coordinate crossing point of the axes: `[xCross, yCross]`. Drives
45
+ * quadrant mode — where the origin sits relative to the domain makes a chart
46
+ * 1- / 2- / 4-quadrant. Omit a component to leave that axis auto (crosses at 0
47
+ * when the domain spans it, else edge-pinned). Also settable via `spec.axisOrigin`. */
48
+ axisOrigin?: [number | undefined, number | undefined];
49
+ /** Nudge an edge-pinned axis outward by N px (Q1 only). Also via `spec.axisOffset`. */
50
+ axisOffset?: number;
44
51
  /** Animate marks on data/flip changes (opt-out for e.g. AnimatedPlot, which
45
52
  * tweens its own frames). Enabled one frame after the width settles so the
46
53
  * initial layout paints un-animated. Default `true`. */
@@ -95,6 +95,7 @@ export class PlotState {
95
95
  ribbon: number;
96
96
  waterfall: number;
97
97
  rule: number;
98
+ radar: number;
98
99
  };
99
100
  patterns: string[];
100
101
  symbols: string[];
@@ -425,6 +425,7 @@ export class PlotState {
425
425
  this.#selectable = config.selectable ?? false
426
426
  if (config.selected) this.#selected = new SvelteSet(config.selected)
427
427
  this.#axisOffset = config.axisOffset ?? 0
428
+ this.axisOrigin = config.axisOrigin ?? [undefined, undefined]
428
429
  this.#marginOverride = config.margin ?? undefined
429
430
  this.#orientationOverride = config.orientation ?? undefined
430
431
  this.#continuousCategory = config.continuousCategory ?? false
@@ -453,6 +454,7 @@ export class PlotState {
453
454
  if (config.onselect !== undefined) this.#onselect = config.onselect
454
455
  if (config.selectable !== undefined) this.#selectable = config.selectable
455
456
  if (config.axisOffset !== undefined) this.#axisOffset = config.axisOffset
457
+ this.axisOrigin = config.axisOrigin ?? [undefined, undefined]
456
458
  this.#marginOverride = config.margin ?? undefined
457
459
  this.#orientationOverride = config.orientation ?? undefined
458
460
  this.#continuousCategory = config.continuousCategory ?? false
@@ -480,7 +482,14 @@ export class PlotState {
480
482
  if (!geom) return []
481
483
  const stat = geom.stat ?? 'identity'
482
484
  if (stat === 'identity') return this.#rawData
483
- const mergedChannels = { ...this.#channels, ...geom.channels }
485
+ // Strip explicit `undefined` values before spreading: a geom that omits a channel
486
+ // to inherit it from the container sends `{ x: undefined }`, not `{}`, which would
487
+ // otherwise become an own property that clobbers the container's value below (see
488
+ // SparkState.geomData for the fuller writeup — same bug, same fix, both classes).
489
+ const geomChannels = Object.fromEntries(
490
+ Object.entries(geom.channels ?? {}).filter(([, v]) => v !== undefined)
491
+ )
492
+ const mergedChannels = { ...this.#channels, ...geomChannels }
484
493
  return applyGeomStat(this.#rawData, { stat, channels: mergedChannels }, this.#helpers)
485
494
  }
486
495
 
@@ -0,0 +1,165 @@
1
+ <script lang="ts">
2
+ import { setContext, untrack } from 'svelte'
3
+ import type { Snippet } from 'svelte'
4
+ import { SparkState } from './SparkState.svelte.js'
5
+ import DefinePatterns from './patterns/DefinePatterns.svelte'
6
+
7
+ type Row = Record<string, unknown>
8
+
9
+ type Props = {
10
+ data?: Row[]
11
+ x?: string
12
+ y?: string
13
+ /** Categorical color/series channel — same meaning as a geom's `color`/`fill` prop. */
14
+ color?: string
15
+ width?: number
16
+ height?: number
17
+ min?: number
18
+ max?: number
19
+ /** Anchor value: extends the y-domain to include it AND draws a `[data-plot-baseline]`
20
+ * reference line at its pixel. Owned by the container, not a geom — it already owns
21
+ * the domain extension, and it applies to bar sparks too, not just area. */
22
+ baseline?: number
23
+ /** Literal pattern name (e.g. 'diagonal') to texture the composed geom's fill with.
24
+ * A spark is single-series, so unlike a Plot's pattern CHANNEL (which assigns a
25
+ * distinct pattern per distinct field value), there's no grouping to assign — this
26
+ * names the one pattern to use directly. See `SparkState.patterns` for the mechanism. */
27
+ pattern?: string
28
+ /** Accessible name for the glyph. Omit and one is generated from the series — see
29
+ * `summary` below. Pass a real one whenever the surrounding copy doesn't already
30
+ * say what the sparkline is measuring. */
31
+ label?: string
32
+ /** The geom(s) to compose inside — e.g. `<Line />`, `<Area />`, `<Trend />`. */
33
+ children?: Snippet
34
+ }
35
+
36
+ let {
37
+ data = [],
38
+ x = undefined,
39
+ y = undefined,
40
+ color = undefined,
41
+ width = 80,
42
+ height = 24,
43
+ min = undefined,
44
+ max = undefined,
45
+ baseline = undefined,
46
+ pattern = undefined,
47
+ label = undefined,
48
+ children
49
+ }: Props = $props()
50
+
51
+ // A getter thunk (not a plain object) so every reactive prop it closes over is re-read on
52
+ // each call — used both by the untracked initial construction and the live $effect below.
53
+ const config = () => ({
54
+ data,
55
+ channels: { x, y, color },
56
+ width,
57
+ height,
58
+ min,
59
+ max,
60
+ baseline,
61
+ pattern
62
+ })
63
+
64
+ // untrack: the constructor's initial read of config() must not register as a dependency of
65
+ // whatever scope creates <Spark> — mirrors PlotSurface's
66
+ // `untrack(() => new PlotState(fullConfig()))`. The $effect below is the sole mechanism
67
+ // that keeps the state live.
68
+ const state = untrack(() => new SparkState(config()))
69
+
70
+ // SAME context key PlotState publishes on — this is what lets a geom compose inside a
71
+ // Spark with zero changes: it never knows, or branches on, which container it resolved.
72
+ setContext('plot-state', state)
73
+
74
+ // Keeps props live: SparkState.update() is designed to be re-callable (see its doc
75
+ // comment) and fully re-applies config rather than merging deltas, so every prop change
76
+ // here — including ones read only inside a composed geom's channels — flows through.
77
+ $effect(() => {
78
+ state.update(config())
79
+ })
80
+
81
+ /** Round for speech: drops a pointless `.00` without truncating real precision. */
82
+ function speak(n: number) {
83
+ return String(Math.round(n * 100) / 100)
84
+ }
85
+
86
+ // A sparkline carries meaning purely visually, so without this it is not merely
87
+ // unlabelled — it is unreadable to a screen reader. Cycle 1 shipped every spark as a
88
+ // bare <svg> with no role or name at all.
89
+ //
90
+ // Generated from the series rather than a fixed "sparkline" string: a constant would
91
+ // satisfy an axe rule while still telling the user nothing. Count, direction, endpoints
92
+ // and extent are what someone reads off the shape visually, so they are what the text
93
+ // alternative says.
94
+ const summary = $derived.by(() => {
95
+ const values = y ? data.map((row) => Number(row[y])).filter((v) => Number.isFinite(v)) : []
96
+ if (!values.length) return 'Sparkline with no data'
97
+
98
+ const first = values[0]
99
+ const last = values[values.length - 1]
100
+ // reduce rather than Math.min(...values): spread on a long series risks a stack
101
+ // overflow, and a spark is free to be handed thousands of points.
102
+ const lo = values.reduce((a, b) => (b < a ? b : a), first)
103
+ const hi = values.reduce((a, b) => (b > a ? b : a), first)
104
+ const direction = last > first ? 'rising' : last < first ? 'falling' : 'flat'
105
+
106
+ return (
107
+ `Sparkline: ${values.length} values, ${direction} from ${speak(first)} to ` +
108
+ `${speak(last)}, ranging ${speak(lo)} to ${speak(hi)}.`
109
+ )
110
+ })
111
+
112
+ const accessibleName = $derived(label ?? summary)
113
+
114
+ // The baseline line is the container's concern, not a geom's: it already owns the domain
115
+ // extension (SparkState's y-domain widening), and it applies to bar sparks too, not just
116
+ // area — so a single reference line lives here rather than duplicated inside every geom.
117
+ const baselineY = $derived.by(() => {
118
+ if (baseline === undefined) return null
119
+ const scale = state.yScale
120
+ // buildUnifiedYScale's return type is a union (linear | band) because a PlotState y
121
+ // channel *can* be categorical — a spark's never is (SparkState's #yDomain is always
122
+ // numeric), so this is always the linear branch in practice. `'ticks' in scale` narrows
123
+ // the union for the compiler without a bare property access on a member (ScaleBand)
124
+ // that doesn't have it at all (same union Plot/Grid.svelte narrows for xScale/yScale).
125
+ return 'ticks' in scale ? scale(baseline) : null
126
+ })
127
+ </script>
128
+
129
+ <svg
130
+ {width}
131
+ {height}
132
+ data-spark
133
+ role="img"
134
+ aria-label={accessibleName}
135
+ style="overflow: visible; display: block;"
136
+ >
137
+ <!-- role="img" + aria-label is what actually gets announced; title/desc are the
138
+ SVG-native equivalents, kept for tooling and non-ARIA consumers. `desc` always
139
+ holds the generated data summary even when `label` overrides the name, so the
140
+ numbers stay available rather than being replaced by the caller's wording. -->
141
+ <title>{accessibleName}</title>
142
+ <desc>{summary}</desc>
143
+
144
+ <!-- SVG <pattern> defs for texture fills — the same mechanism PlotSurface wires up, so a
145
+ geom's pattern channel resolves inside a Spark exactly as it does inside a Plot. -->
146
+ <defs>
147
+ <DefinePatterns />
148
+ </defs>
149
+
150
+ {#if baselineY !== null}
151
+ <line x1={0} y1={baselineY} x2={width} y2={baselineY} data-plot-baseline />
152
+ {/if}
153
+
154
+ {@render children?.()}
155
+ </svg>
156
+
157
+ <style>
158
+ [data-plot-baseline] {
159
+ stroke: var(--chart-baseline-color, currentColor);
160
+ stroke-width: var(--chart-baseline-width, 1);
161
+ stroke-dasharray: var(--chart-baseline-dash, 4 4);
162
+ opacity: var(--chart-baseline-opacity, 0.5);
163
+ pointer-events: none;
164
+ }
165
+ </style>
@@ -0,0 +1,31 @@
1
+ import type { Snippet } from 'svelte';
2
+ type Row = Record<string, unknown>;
3
+ type Props = {
4
+ data?: Row[];
5
+ x?: string;
6
+ y?: string;
7
+ /** Categorical color/series channel — same meaning as a geom's `color`/`fill` prop. */
8
+ color?: string;
9
+ width?: number;
10
+ height?: number;
11
+ min?: number;
12
+ max?: number;
13
+ /** Anchor value: extends the y-domain to include it AND draws a `[data-plot-baseline]`
14
+ * reference line at its pixel. Owned by the container, not a geom — it already owns
15
+ * the domain extension, and it applies to bar sparks too, not just area. */
16
+ baseline?: number;
17
+ /** Literal pattern name (e.g. 'diagonal') to texture the composed geom's fill with.
18
+ * A spark is single-series, so unlike a Plot's pattern CHANNEL (which assigns a
19
+ * distinct pattern per distinct field value), there's no grouping to assign — this
20
+ * names the one pattern to use directly. See `SparkState.patterns` for the mechanism. */
21
+ pattern?: string;
22
+ /** Accessible name for the glyph. Omit and one is generated from the series — see
23
+ * `summary` below. Pass a real one whenever the surrounding copy doesn't already
24
+ * say what the sparkline is measuring. */
25
+ label?: string;
26
+ /** The geom(s) to compose inside — e.g. `<Line />`, `<Area />`, `<Trend />`. */
27
+ children?: Snippet;
28
+ };
29
+ declare const Spark: import("svelte").Component<Props, {}, "">;
30
+ type Spark = ReturnType<typeof Spark>;
31
+ export default Spark;
@@ -0,0 +1,138 @@
1
+ /**
2
+ * The full set of members a geom reads off its `'plot-state'` context — derived by
3
+ * scanning every geom for `plotState.*` / `plot.*` / `state.*` reads (`geoms/*.svelte`,
4
+ * `geoms/lib/**`, including the `marks/*.js` adapters). `SparkState` must implement
5
+ * every one of these: a `<Spark>` composes the SAME geom components a `<Plot>` does,
6
+ * and no geom branches on which container it's inside — so a member missing here is a
7
+ * member that throws inside a `<Spark>` at runtime, not at the point that grew it.
8
+ *
9
+ * This is the ONE place to update when that surface legitimately changes. Adding a
10
+ * geom (or a new context read inside an existing one) that needs a `PlotState` member
11
+ * means adding it here too. `spec/spark-contract.spec.js` asserts `SparkState` and
12
+ * `PlotState` both satisfy this exact list and agree on each member's kind (function /
13
+ * Map / array / null / primitive). If that test goes red after you add a member here,
14
+ * that is the signal working as intended — it means `SparkState` is missing the
15
+ * member, not that the test is wrong. Do not "fix" a red conformance test by deleting
16
+ * the entry you just added.
17
+ *
18
+ * Deliberately excluded — read only by `Plot`'s chrome (`Axis`/`Legend`/`Tooltip`/
19
+ * `Grid`) or by `PlotSurface`'s own wiring, never by a geom: `xAxisY`, `yAxisX`,
20
+ * `hovered`, `mode`, `colorScaleType`, `geomTypes`, `colorField`, `patternField`,
21
+ * `symbolField`, `format`, `label`, `tooltip`, `margin`, `bandScale`, `valueScale`,
22
+ * `preset`, `geomComponent`. A `<Spark>` has no axis, legend, tooltip or grid to
23
+ * drive, so none of these ever apply.
24
+ *
25
+ * Also excluded: `selectedRows` / `isSelected` / `setSelected` / `clearSelected` /
26
+ * `applyZoom` / `resetZoom`. `geoms/Highlight.svelte` is the one geom that reads
27
+ * `state?.selectedRows`, but it does so defensively (`?? []`) — multi-select and zoom
28
+ * are explicit non-goals for a spark (see
29
+ * docs/superpowers/specs/2026-08-20-spark-plot-geom-architecture-design.md), so their
30
+ * absence degrades Highlight to "nothing selected", not a runtime error. That's a
31
+ * different failure mode than every member below, where absence throws.
32
+ */
33
+ export const GEOM_CONTRACT: string[];
34
+ /**
35
+ * SparkState — the lean, PlotState-compatible context that lets a sparkline compose
36
+ * real geoms (`<Spark><Line /></Spark>`) instead of Sparkline's own hand-rolled
37
+ * line/area/bar rendering. It publishes on the same 'plot-state' context key as
38
+ * PlotState, so geoms (Line/Area/Bar) read scales, channels and data through the
39
+ * identical interface and never know whether they're inside a spark or a full Plot.
40
+ *
41
+ * An 80×24 glyph has no use for most of what PlotState carries, so this is a thin
42
+ * composition of the same pure scale modules PlotState uses — not a re-implementation
43
+ * of PlotState's scope. It publishes the same registerGeom/updateGeom/unregisterGeom/
44
+ * geomData lifecycle as PlotState (see those methods below), at smaller scope, so
45
+ * GeomState — and therefore any geom component — drives a spark unchanged. Deliberately
46
+ * omitted, permanently (a <Spark> container is a later slice, but none of these ever
47
+ * apply to an 80×24 glyph):
48
+ * - zoom/pan transforms — a spark is a static glyph, never panned or zoomed.
49
+ * - crossfilter/selection — nothing to click or brush at this size.
50
+ * - facets — a spark is always a single small panel, never a grid of panels.
51
+ * - axis/legend chrome and margins — width/height ARE the inner box.
52
+ *
53
+ * This class also carries the rest of the surface a geom reads off `plot-state`:
54
+ * `colors` — a real categorical palette, built from the same brewing helpers PlotState
55
+ * uses, keyed off `channels.color ?? channels.fill` — plus a set of members geoms read
56
+ * but a spark has no use for (symbols/isFlipped/orientation/continuousCategory/
57
+ * continuousColorScale/interactive/place/setHovered/clearHovered/handleSelect). Those are
58
+ * kept present and explicit — empty Maps, false/null/identity, no-op methods — rather
59
+ * than simply absent, so a geom never needs a spark-specific branch and a later
60
+ * conformance test can pin the full contract. `patterns` is the one exception with a real,
61
+ * non-empty case — see its own doc comment below for why a literal name is the only
62
+ * sensible spark case.
63
+ *
64
+ * `data` deliberately holds ONE array (`$state.raw`, not a proxying `$state`), unlike
65
+ * PlotState's `#data`/`#rawData` pair. That split gave PlotState's data two distinct
66
+ * proxy identities for the same source array, breaking identity-based lookups like
67
+ * `plotState.data.indexOf(row)` (see journal 2026-08-13). Geoms will later read rows
68
+ * from this same array, so its identity must be exactly what the caller passed in.
69
+ */
70
+ export class SparkState {
71
+ constructor(config?: {});
72
+ xScale: import("d3-scale").ScaleLinear<number, number, never> | import("d3-scale").ScaleBand<string>;
73
+ yScale: import("d3-scale").ScaleLinear<number, number, never> | import("d3-scale").ScaleBand<string>;
74
+ colors: Map<unknown, {
75
+ fill: string;
76
+ stroke: string;
77
+ }>;
78
+ update(config?: {}): void;
79
+ get data(): any[];
80
+ /** @returns {{ x?: string, y?: string }} */
81
+ get channels(): {
82
+ x?: string;
83
+ y?: string;
84
+ };
85
+ get innerWidth(): number;
86
+ get innerHeight(): number;
87
+ get chartPreset(): {
88
+ colors: string[];
89
+ shades: {
90
+ light: {
91
+ fill: string;
92
+ stroke: string;
93
+ };
94
+ dark: {
95
+ fill: string;
96
+ stroke: string;
97
+ };
98
+ };
99
+ opacity: {
100
+ area: number;
101
+ bar: number;
102
+ line: number;
103
+ point: number;
104
+ arc: number;
105
+ box: number;
106
+ violin: number;
107
+ jitter: number;
108
+ heatmap: number;
109
+ candlestick: number;
110
+ hexbin: number;
111
+ ribbon: number;
112
+ waterfall: number;
113
+ rule: number;
114
+ radar: number;
115
+ };
116
+ patterns: string[];
117
+ symbols: string[];
118
+ };
119
+ get patterns(): Map<any, any>;
120
+ get symbols(): Map<any, any>;
121
+ get isFlipped(): boolean;
122
+ get orientation(): string;
123
+ get continuousCategory(): boolean;
124
+ get continuousColorScale(): null;
125
+ get interactive(): boolean;
126
+ place(u: any, v: any): {
127
+ x: any;
128
+ y: any;
129
+ };
130
+ setHovered(): void;
131
+ clearHovered(): void;
132
+ handleSelect(): void;
133
+ registerGeom(config: any): string;
134
+ updateGeom(id: any, config: any): void;
135
+ unregisterGeom(id: any): void;
136
+ geomData(id: any): any[];
137
+ #private;
138
+ }