@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.
- package/dist/AnimatedPlot.svelte +4 -1
- package/dist/Plot.svelte +36 -9
- package/dist/Plot.svelte.d.ts +7 -0
- package/dist/PlotState.svelte.d.ts +1 -0
- package/dist/PlotState.svelte.js +10 -1
- package/dist/Spark.svelte +165 -0
- package/dist/Spark.svelte.d.ts +31 -0
- package/dist/SparkState.svelte.d.ts +138 -0
- package/dist/SparkState.svelte.js +331 -0
- package/dist/Sparkline.svelte +59 -179
- package/dist/Sparkline.svelte.d.ts +2 -0
- package/dist/charts/RadarChart.svelte +106 -0
- package/dist/charts/RadarChart.svelte.d.ts +43 -0
- package/dist/geoms/Area.svelte +17 -2
- package/dist/geoms/Area.svelte.d.ts +2 -0
- package/dist/geoms/Bar.svelte +2 -9
- package/dist/geoms/Highlight.svelte +2 -7
- package/dist/geoms/Line.svelte +2 -8
- package/dist/geoms/Point.svelte +2 -8
- package/dist/geoms/Radar.svelte +352 -0
- package/dist/geoms/Radar.svelte.d.ts +42 -0
- package/dist/geoms/lib/aesthetics.d.ts +14 -0
- package/dist/geoms/lib/aesthetics.js +21 -0
- package/dist/geoms/lib/areas.d.ts +9 -2
- package/dist/geoms/lib/areas.js +53 -8
- package/dist/geoms/lib/marks/area.d.ts +3 -0
- package/dist/geoms/lib/marks/area.js +11 -2
- package/dist/geoms/lib/marks/radar.d.ts +68 -0
- package/dist/geoms/lib/marks/radar.js +153 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -1
- package/dist/lib/brewing/marks/boxes.js +19 -30
- package/dist/lib/brewing/polar.d.ts +433 -0
- package/dist/lib/brewing/polar.js +584 -0
- package/dist/lib/plot/scales.js +8 -5
- package/dist/lib/plot/types.d.ts +1 -0
- package/dist/lib/plot/types.js +1 -0
- package/dist/lib/preset.d.ts +1 -0
- package/dist/lib/preset.js +2 -1
- package/dist/lib/select.d.ts +3 -3
- package/dist/lib/select.js +1 -1
- package/package.json +1 -1
- package/dist/examples/BarChartExample.svelte +0 -78
- package/dist/examples/BarChartExample.svelte.d.ts +0 -18
- package/dist/lib/brewing/types.d.ts +0 -162
- package/dist/lib/brewing/types.js +0 -73
- package/dist/lib/xscale.d.ts +0 -11
- package/dist/lib/xscale.js +0 -31
package/dist/AnimatedPlot.svelte
CHANGED
|
@@ -202,7 +202,10 @@
|
|
|
202
202
|
})
|
|
203
203
|
|
|
204
204
|
onDestroy(() => {
|
|
205
|
-
|
|
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 = [
|
|
119
|
-
|
|
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
|
|
228
|
-
|
|
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}
|
package/dist/Plot.svelte.d.ts
CHANGED
|
@@ -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`. */
|
package/dist/PlotState.svelte.js
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|