graphein 0.3.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/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/chunk-V6TY7KAL.js +7 -0
- package/dist/chunk-V6TY7KAL.js.map +1 -0
- package/dist/index.d.ts +2392 -0
- package/dist/index.js +10555 -0
- package/dist/index.js.map +1 -0
- package/dist/patrickHand-ITDQJJE2.js +9 -0
- package/dist/patrickHand-ITDQJJE2.js.map +1 -0
- package/package.json +60 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared primitive types used across Graphein core.
|
|
3
|
+
*
|
|
4
|
+
* These are intentionally tiny and dependency-free so every module
|
|
5
|
+
* (scales, shapes, layout, charts, tables) can share a common vocabulary.
|
|
6
|
+
*/
|
|
7
|
+
/** A 2D point in pixel space. */
|
|
8
|
+
interface Point {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
}
|
|
12
|
+
/** Width/height in pixels. */
|
|
13
|
+
interface Size {
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
}
|
|
17
|
+
/** An axis-aligned rectangle in pixel space (top-left origin). */
|
|
18
|
+
interface Rect {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
}
|
|
24
|
+
/** Padding/margins around a box. */
|
|
25
|
+
interface Insets {
|
|
26
|
+
top: number;
|
|
27
|
+
right: number;
|
|
28
|
+
bottom: number;
|
|
29
|
+
left: number;
|
|
30
|
+
}
|
|
31
|
+
/** A single data record (row). Agent-supplied data is an array of these. */
|
|
32
|
+
type Datum = Record<string, unknown>;
|
|
33
|
+
/** Primitive field value types Graphein understands. */
|
|
34
|
+
type FieldValue = number | string | boolean | Date | null | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Encoding channel data types (Vega-Lite-like):
|
|
37
|
+
* - quantitative: continuous numbers (linear/log scales)
|
|
38
|
+
* - temporal: dates/times (time scale)
|
|
39
|
+
* - ordinal: ordered categories
|
|
40
|
+
* - nominal: unordered categories
|
|
41
|
+
*/
|
|
42
|
+
type FieldType = 'quantitative' | 'temporal' | 'ordinal' | 'nominal';
|
|
43
|
+
/** Normalized RGBA color, channels in [0,255], alpha in [0,1]. */
|
|
44
|
+
interface RGBA {
|
|
45
|
+
r: number;
|
|
46
|
+
g: number;
|
|
47
|
+
b: number;
|
|
48
|
+
a: number;
|
|
49
|
+
}
|
|
50
|
+
/** A [min, max] numeric interval. */
|
|
51
|
+
type Extent = [number, number];
|
|
52
|
+
/** Anything with a numeric-or-categorical comparable value. */
|
|
53
|
+
type Comparable = number | string | Date;
|
|
54
|
+
|
|
55
|
+
interface ContinuousScale {
|
|
56
|
+
map(value: number): number;
|
|
57
|
+
invert(pixel: number): number;
|
|
58
|
+
ticks(count?: number): number[];
|
|
59
|
+
tickFormat(count?: number): (v: number) => string;
|
|
60
|
+
nice(count?: number): ContinuousScale;
|
|
61
|
+
copy(): ContinuousScale;
|
|
62
|
+
readonly domain: readonly [number, number];
|
|
63
|
+
readonly range: readonly [number, number];
|
|
64
|
+
}
|
|
65
|
+
interface BandScale {
|
|
66
|
+
map(value: string): number | undefined;
|
|
67
|
+
readonly bandwidth: number;
|
|
68
|
+
readonly step: number;
|
|
69
|
+
readonly domain: readonly string[];
|
|
70
|
+
readonly range: readonly [number, number];
|
|
71
|
+
}
|
|
72
|
+
interface PointScale {
|
|
73
|
+
map(value: string): number | undefined;
|
|
74
|
+
readonly step: number;
|
|
75
|
+
readonly domain: readonly string[];
|
|
76
|
+
readonly range: readonly [number, number];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface BandScaleOptions {
|
|
80
|
+
domain: string[];
|
|
81
|
+
range: [number, number];
|
|
82
|
+
paddingInner?: number;
|
|
83
|
+
paddingOuter?: number;
|
|
84
|
+
align?: number;
|
|
85
|
+
}
|
|
86
|
+
interface PointScaleOptions {
|
|
87
|
+
domain: string[];
|
|
88
|
+
range: [number, number];
|
|
89
|
+
padding?: number;
|
|
90
|
+
align?: number;
|
|
91
|
+
}
|
|
92
|
+
declare function bandScale(opts: BandScaleOptions): BandScale;
|
|
93
|
+
declare function pointScale(opts: PointScaleOptions): PointScale;
|
|
94
|
+
|
|
95
|
+
interface LinearScaleOptions {
|
|
96
|
+
domain: [number, number];
|
|
97
|
+
range: [number, number];
|
|
98
|
+
clamp?: boolean;
|
|
99
|
+
}
|
|
100
|
+
declare function linearScale(opts: LinearScaleOptions): ContinuousScale;
|
|
101
|
+
|
|
102
|
+
interface LogScaleOptions {
|
|
103
|
+
domain: [number, number];
|
|
104
|
+
range: [number, number];
|
|
105
|
+
base?: number;
|
|
106
|
+
clamp?: boolean;
|
|
107
|
+
}
|
|
108
|
+
declare function logScale(opts: LogScaleOptions): ContinuousScale;
|
|
109
|
+
|
|
110
|
+
interface TimeScaleOptions {
|
|
111
|
+
domain: [number, number] | [Date, Date];
|
|
112
|
+
range: [number, number];
|
|
113
|
+
clamp?: boolean;
|
|
114
|
+
}
|
|
115
|
+
declare function timeScale(opts: TimeScaleOptions): ContinuousScale;
|
|
116
|
+
|
|
117
|
+
declare function tickStep(start: number, stop: number, count: number): number;
|
|
118
|
+
declare function tickIncrement(start: number, stop: number, count: number): number;
|
|
119
|
+
declare function niceDomain(start: number, stop: number, count: number): [number, number];
|
|
120
|
+
declare function ticks(start: number, stop: number, count: number): number[];
|
|
121
|
+
|
|
122
|
+
declare function timeTicks(start: number, stop: number, count: number): number[];
|
|
123
|
+
declare function timeTickFormat(values: number[]): (t: number) => string;
|
|
124
|
+
|
|
125
|
+
/** Color-space conversions: sRGB <-> linear <-> OKLab <-> OKLCH. */
|
|
126
|
+
declare const clamp01: (x: number) => number;
|
|
127
|
+
declare const clamp255: (x: number) => number;
|
|
128
|
+
/** sRGB channel (0..1) -> linear-light (0..1). */
|
|
129
|
+
declare function srgbToLinear(c: number): number;
|
|
130
|
+
/** linear-light (0..1) -> sRGB channel (0..1). */
|
|
131
|
+
declare function linearToSrgb(c: number): number;
|
|
132
|
+
interface OKLab {
|
|
133
|
+
L: number;
|
|
134
|
+
a: number;
|
|
135
|
+
b: number;
|
|
136
|
+
}
|
|
137
|
+
interface OKLCH {
|
|
138
|
+
L: number;
|
|
139
|
+
C: number;
|
|
140
|
+
/** Hue in radians. */
|
|
141
|
+
h: number;
|
|
142
|
+
}
|
|
143
|
+
/** RGBA (0..255) -> OKLab. */
|
|
144
|
+
declare function rgbToOklab(color: RGBA): OKLab;
|
|
145
|
+
/** OKLab -> RGBA (0..255), gamut-clamped. Alpha defaults to 1. */
|
|
146
|
+
declare function oklabToRgb(lab: OKLab, alpha?: number): RGBA;
|
|
147
|
+
declare function oklabToOklch(lab: OKLab): OKLCH;
|
|
148
|
+
declare function oklchToOklab(lch: OKLCH): OKLab;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Parse a CSS color string into RGBA (r,g,b in 0..255, a in 0..1).
|
|
152
|
+
* Supports hex (#rgb/#rgba/#rrggbb/#rrggbbaa), rgb()/rgba(), hsl()/hsla(),
|
|
153
|
+
* and a small set of named colors. Returns null when unparseable.
|
|
154
|
+
*/
|
|
155
|
+
declare function parseColor(input: string): RGBA | null;
|
|
156
|
+
|
|
157
|
+
/** Format an RGBA as a CSS rgb()/rgba() string. */
|
|
158
|
+
declare function rgbaToCss(c: RGBA): string;
|
|
159
|
+
/**
|
|
160
|
+
* Return `color` as a CSS rgba() string at the given alpha (0..1). Accepts any
|
|
161
|
+
* parseable CSS color; falls back to the input string when unparseable.
|
|
162
|
+
*/
|
|
163
|
+
declare function withAlpha(color: string, alpha: number): string;
|
|
164
|
+
/** Format an RGBA as a hex string (#rrggbb, or #rrggbbaa when includeAlpha). */
|
|
165
|
+
declare function toHex(c: RGBA, includeAlpha?: boolean): string;
|
|
166
|
+
|
|
167
|
+
type Interpolator = (t: number) => RGBA;
|
|
168
|
+
/** Interpolate two colors in sRGB space. Endpoints are exact. */
|
|
169
|
+
declare function interpolateRgb(a: RGBA, b: RGBA): Interpolator;
|
|
170
|
+
/**
|
|
171
|
+
* Interpolate two colors through OKLab for perceptually smooth ramps.
|
|
172
|
+
* Endpoints are returned exactly (no round-trip drift).
|
|
173
|
+
*/
|
|
174
|
+
declare function interpolateOklab(a: RGBA, b: RGBA): Interpolator;
|
|
175
|
+
|
|
176
|
+
/** Get a categorical palette by name (defaults to the 'graphein' palette). */
|
|
177
|
+
declare function categorical(name?: string): string[];
|
|
178
|
+
/**
|
|
179
|
+
* Build a continuous ramp from ordered color stops, interpolating each segment
|
|
180
|
+
* in OKLab. Endpoints (t=0,1) return the first/last stop exactly.
|
|
181
|
+
*/
|
|
182
|
+
declare function rampFromStops(stops: string[]): Interpolator;
|
|
183
|
+
/** A named sequential interpolator (blues, teal, viridis, magma, greys). */
|
|
184
|
+
declare function sequential(name: string): Interpolator;
|
|
185
|
+
/** A named diverging interpolator (redBlue, spectral, blueRed). */
|
|
186
|
+
declare function diverging(name: string): Interpolator;
|
|
187
|
+
declare const sequentialSchemes: string[];
|
|
188
|
+
declare const divergingSchemes: string[];
|
|
189
|
+
|
|
190
|
+
interface ColorScale<T> {
|
|
191
|
+
map(value: T): RGBA;
|
|
192
|
+
}
|
|
193
|
+
/** Map a continuous numeric domain through a sequential interpolator. */
|
|
194
|
+
declare function sequentialColorScale(opts: {
|
|
195
|
+
domain: [number, number];
|
|
196
|
+
interpolator: Interpolator;
|
|
197
|
+
clamp?: boolean;
|
|
198
|
+
}): ColorScale<number>;
|
|
199
|
+
/** Map a [min, mid, max] domain symmetrically through a diverging interpolator. */
|
|
200
|
+
declare function divergingColorScale(opts: {
|
|
201
|
+
domain: [number, number, number];
|
|
202
|
+
interpolator: Interpolator;
|
|
203
|
+
clamp?: boolean;
|
|
204
|
+
}): ColorScale<number>;
|
|
205
|
+
/** Map categorical keys to palette colors (cycled by first-seen index). */
|
|
206
|
+
declare function ordinalColorScale(opts: {
|
|
207
|
+
domain?: string[];
|
|
208
|
+
palette?: string[];
|
|
209
|
+
}): ColorScale<string>;
|
|
210
|
+
|
|
211
|
+
/** WCAG relative luminance of a color. */
|
|
212
|
+
declare function relativeLuminance(c: RGBA): number;
|
|
213
|
+
/** WCAG contrast ratio between two colors (1..21). */
|
|
214
|
+
declare function contrastRatio(a: RGBA, b: RGBA): number;
|
|
215
|
+
/**
|
|
216
|
+
* Pick whichever of two candidate text colors (default white/black) has the
|
|
217
|
+
* higher contrast against the given background — for legible labels on marks.
|
|
218
|
+
*/
|
|
219
|
+
declare function readableTextColor(bg: RGBA, options?: {
|
|
220
|
+
light?: RGBA;
|
|
221
|
+
dark?: RGBA;
|
|
222
|
+
}): RGBA;
|
|
223
|
+
|
|
224
|
+
interface PathSink {
|
|
225
|
+
moveTo(x: number, y: number): void;
|
|
226
|
+
lineTo(x: number, y: number): void;
|
|
227
|
+
bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
|
|
228
|
+
quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void;
|
|
229
|
+
arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void;
|
|
230
|
+
closePath(): void;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
type Curve = (points: Point[], sink: PathSink) => void;
|
|
234
|
+
declare function curveLinear(points: Point[], sink: PathSink): void;
|
|
235
|
+
declare function curveStepAfter(points: Point[], sink: PathSink): void;
|
|
236
|
+
declare function curveStepBefore(points: Point[], sink: PathSink): void;
|
|
237
|
+
declare const curveStep: Curve;
|
|
238
|
+
declare function monotoneXTangents(points: Point[]): number[];
|
|
239
|
+
declare function curveMonotoneX(points: Point[], sink: PathSink): void;
|
|
240
|
+
declare function curveCatmullRom(points: Point[], sink: PathSink): void;
|
|
241
|
+
|
|
242
|
+
interface LineOptions {
|
|
243
|
+
curve?: Curve;
|
|
244
|
+
}
|
|
245
|
+
declare function line(opts?: LineOptions): (points: Point[], sink: PathSink) => void;
|
|
246
|
+
|
|
247
|
+
interface AreaPoint {
|
|
248
|
+
x: number;
|
|
249
|
+
y0: number;
|
|
250
|
+
y1: number;
|
|
251
|
+
}
|
|
252
|
+
interface AreaOptions {
|
|
253
|
+
curve?: Curve;
|
|
254
|
+
}
|
|
255
|
+
declare function area(opts?: AreaOptions): (points: AreaPoint[], sink: PathSink) => void;
|
|
256
|
+
|
|
257
|
+
interface ArcOptions {
|
|
258
|
+
innerRadius: number;
|
|
259
|
+
outerRadius: number;
|
|
260
|
+
startAngle: number;
|
|
261
|
+
endAngle: number;
|
|
262
|
+
cornerRadius?: number;
|
|
263
|
+
padAngle?: number;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Creates a pie/donut path generator.
|
|
267
|
+
*
|
|
268
|
+
* Angles are radians with 0 at 12 o'clock and positive angles moving clockwise.
|
|
269
|
+
* `padAngle` symmetrically trims the angular span. `cornerRadius` is accepted for
|
|
270
|
+
* API compatibility and clamped, but corners remain circular arc/radial joins.
|
|
271
|
+
*/
|
|
272
|
+
declare function arc(opts: ArcOptions): (sink: PathSink, cx: number, cy: number) => void;
|
|
273
|
+
|
|
274
|
+
declare function roundedRect(sink: PathSink, x: number, y: number, w: number, h: number, radii: number | [number, number, number, number]): void;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Type vocabulary for the rough (hand-drawn) drawing engine.
|
|
278
|
+
*
|
|
279
|
+
* `RoughStyle` is the resolved set of knobs that controls how sketchy a chart
|
|
280
|
+
* looks; it is produced once per chart (see `resolveSketch`) and handed to a
|
|
281
|
+
* `RoughPen`. `MarkOptions` carries the per-mark colors plus any local overrides.
|
|
282
|
+
*/
|
|
283
|
+
/** How closed shapes are filled. */
|
|
284
|
+
type FillStyle = 'hachure' | 'solid' | 'cross-hatch';
|
|
285
|
+
/** Resolved, fully-defaulted sketch knobs shared by every mark in a chart. */
|
|
286
|
+
interface RoughStyle {
|
|
287
|
+
/** Magnitude of the random wobble. 0 ≈ clean, 1 = default, >1 = wilder. */
|
|
288
|
+
roughness: number;
|
|
289
|
+
/** How much straight lines bow/curve between endpoints. */
|
|
290
|
+
bowing: number;
|
|
291
|
+
/** Fill treatment for closed shapes. */
|
|
292
|
+
fillStyle: FillStyle;
|
|
293
|
+
/** Spacing (px) between hachure lines. <= 0 derives from stroke width. */
|
|
294
|
+
hachureGap: number;
|
|
295
|
+
/** Hachure line angle in degrees. */
|
|
296
|
+
hachureAngle: number;
|
|
297
|
+
/** Outline width in px. */
|
|
298
|
+
strokeWidth: number;
|
|
299
|
+
/** PRNG seed — the source of all deterministic jitter. */
|
|
300
|
+
seed: number;
|
|
301
|
+
}
|
|
302
|
+
/** Per-mark drawing options (colors + optional local style overrides). */
|
|
303
|
+
interface MarkOptions extends Partial<RoughStyle> {
|
|
304
|
+
/** Outline color. Omit to skip the outline. */
|
|
305
|
+
stroke?: string;
|
|
306
|
+
/** Fill color. Omit to skip the fill. */
|
|
307
|
+
fill?: string;
|
|
308
|
+
/** Hachure line width (defaults to a thin fraction of strokeWidth). */
|
|
309
|
+
fillWeight?: number;
|
|
310
|
+
/** Opacity applied to the fill only (0..1). */
|
|
311
|
+
fillAlpha?: number;
|
|
312
|
+
/** Opacity applied to the outline only (0..1). */
|
|
313
|
+
strokeAlpha?: number;
|
|
314
|
+
}
|
|
315
|
+
declare const DEFAULT_ROUGH_STYLE: RoughStyle;
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* The rough (hand-drawn) drawing engine.
|
|
319
|
+
*
|
|
320
|
+
* A `RoughPen` wraps a Canvas2D-like context and a single seeded PRNG, exposing
|
|
321
|
+
* the handful of primitives charts need — polylines, polygons, rects, circles,
|
|
322
|
+
* and pie wedges — rendered as wobbly multi-pass strokes with hachure / solid /
|
|
323
|
+
* cross-hatch fills. The shared PRNG means marks differ from one another yet the
|
|
324
|
+
* whole chart is deterministic given its seed (call order is stable).
|
|
325
|
+
*
|
|
326
|
+
* The line model is a faithful port of rough.js's: each segment is a cubic with
|
|
327
|
+
* two randomly displaced control points, drawn twice for the sketched, doubled
|
|
328
|
+
* look; `bowing` bends the midpoint, `roughness` scales the jitter.
|
|
329
|
+
*/
|
|
330
|
+
|
|
331
|
+
/** The minimal Canvas2D surface the engine draws onto (CRC2D satisfies it). */
|
|
332
|
+
interface RoughContext {
|
|
333
|
+
save(): void;
|
|
334
|
+
restore(): void;
|
|
335
|
+
beginPath(): void;
|
|
336
|
+
closePath(): void;
|
|
337
|
+
moveTo(x: number, y: number): void;
|
|
338
|
+
lineTo(x: number, y: number): void;
|
|
339
|
+
bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
|
|
340
|
+
quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void;
|
|
341
|
+
stroke(): void;
|
|
342
|
+
fill(): void;
|
|
343
|
+
lineWidth: number;
|
|
344
|
+
lineCap: CanvasLineCap;
|
|
345
|
+
lineJoin: CanvasLineJoin;
|
|
346
|
+
strokeStyle: string | CanvasGradient | CanvasPattern;
|
|
347
|
+
fillStyle: string | CanvasGradient | CanvasPattern;
|
|
348
|
+
globalAlpha: number;
|
|
349
|
+
}
|
|
350
|
+
declare class RoughPen {
|
|
351
|
+
private readonly ctx;
|
|
352
|
+
private readonly base;
|
|
353
|
+
private readonly rng;
|
|
354
|
+
constructor(ctx: RoughContext, style?: Partial<RoughStyle>);
|
|
355
|
+
private resolve;
|
|
356
|
+
/** A symmetric random offset in [-x, x], scaled by a length-based gain. */
|
|
357
|
+
private off;
|
|
358
|
+
/**
|
|
359
|
+
* Append one hand-drawn cubic from (x1,y1) to (x2,y2) to the current path.
|
|
360
|
+
* `overlay` is the second, tighter pass that creates the doubled-stroke look.
|
|
361
|
+
*/
|
|
362
|
+
private emitLine;
|
|
363
|
+
/** Build a doubled hand-drawn stroke through `points` into the current path. */
|
|
364
|
+
private tracePolyline;
|
|
365
|
+
/**
|
|
366
|
+
* Trace a smooth *closed* loop through `points` (quadratic spline through the
|
|
367
|
+
* segment midpoints, with each point as a control). Used for circles so they
|
|
368
|
+
* read as round hand-drawn blobs rather than angular polygons.
|
|
369
|
+
*/
|
|
370
|
+
private traceSmoothClosed;
|
|
371
|
+
private applyStroke;
|
|
372
|
+
/** Fill a closed polygon with hachure / cross-hatch / solid. */
|
|
373
|
+
private fillPolygon;
|
|
374
|
+
/** Hand-drawn open polyline (axis rules, box-plot whiskers, short features). */
|
|
375
|
+
polyline(points: readonly Point[], opts?: MarkOptions): void;
|
|
376
|
+
/**
|
|
377
|
+
* Hand-drawn open stroke for data *trend* lines — line/area tops and KPI
|
|
378
|
+
* sparklines. A trend line is many short segments, and both the bowing
|
|
379
|
+
* (length-scaled) and the per-segment endpoint jitter stay tiny on short
|
|
380
|
+
* segments, so a plain {@link polyline} reads almost ruler-straight — out of
|
|
381
|
+
* keeping with the confidently wobbly bars/wedges. This amplifies the wobble
|
|
382
|
+
* so trend lines read as clearly hand-drawn. Axis rules and box-plot features
|
|
383
|
+
* keep the subtler {@link polyline} (a long axis rule would over-bow here).
|
|
384
|
+
*/
|
|
385
|
+
trendStroke(points: readonly Point[], opts?: MarkOptions): void;
|
|
386
|
+
/** Hand-drawn closed polygon with optional fill (drawn behind the outline). */
|
|
387
|
+
polygon(points: readonly Point[], opts?: MarkOptions): void;
|
|
388
|
+
/** Hand-drawn rectangle (bars, cells, table chips). */
|
|
389
|
+
rect(x: number, y: number, w: number, h: number, opts?: MarkOptions): void;
|
|
390
|
+
/** Hand-drawn circle (scatter points, line markers). Filled solid for clarity. */
|
|
391
|
+
circle(cx: number, cy: number, r: number, opts?: MarkOptions): void;
|
|
392
|
+
/**
|
|
393
|
+
* Hand-drawn pie/donut wedge. Angles match `shape/arc`: 0 at 12 o'clock,
|
|
394
|
+
* positive clockwise. `innerRadius` 0 draws a full pie slice to the center.
|
|
395
|
+
*/
|
|
396
|
+
wedge(cx: number, cy: number, innerRadius: number, outerRadius: number, startAngle: number, endAngle: number, opts?: MarkOptions): void;
|
|
397
|
+
}
|
|
398
|
+
/** Convenience factory mirroring the class constructor. */
|
|
399
|
+
declare function createRoughPen(ctx: RoughContext, style?: Partial<RoughStyle>): RoughPen;
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Deterministic randomness for the hand-drawn ("sketch") renderer.
|
|
403
|
+
*
|
|
404
|
+
* Every wobble in a sketched chart comes from this PRNG, seeded from the spec so
|
|
405
|
+
* a given chart renders pixel-identically on every paint — a hard requirement
|
|
406
|
+
* for the screenshot harness (which waits on a stable `data-graphein-ready`).
|
|
407
|
+
*/
|
|
408
|
+
/** A seeded pseudo-random source returning floats in [0, 1). */
|
|
409
|
+
type Rng = () => number;
|
|
410
|
+
/**
|
|
411
|
+
* mulberry32 — a tiny, fast, well-distributed 32-bit PRNG. Same seed ⇒ same
|
|
412
|
+
* stream, which is exactly what deterministic sketching needs.
|
|
413
|
+
*/
|
|
414
|
+
declare function mulberry32(seed: number): Rng;
|
|
415
|
+
/**
|
|
416
|
+
* FNV-1a string hash → 32-bit unsigned int. Used to derive a stable seed from a
|
|
417
|
+
* spec's identity (type/title/data size) when the author doesn't pass one.
|
|
418
|
+
*/
|
|
419
|
+
declare function hashString(input: string): number;
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Geometry helpers for the rough engine: polygon hachure (parallel fill lines)
|
|
423
|
+
* and the rotation math it needs. Kept pure and allocation-light so fills stay
|
|
424
|
+
* cheap even on dense charts (heatmaps, stacked bars).
|
|
425
|
+
*/
|
|
426
|
+
|
|
427
|
+
/** Rotate `p` by `angle` radians around `center`. */
|
|
428
|
+
declare function rotatePoint(p: Point, center: Point, angle: number): Point;
|
|
429
|
+
/** A filled hachure stroke: a single straight segment from a to b. */
|
|
430
|
+
type HachureSegment = [Point, Point];
|
|
431
|
+
/**
|
|
432
|
+
* Generate parallel scan-line segments that fill `polygon`, angled at
|
|
433
|
+
* `angleDeg`. The classic rough.js fill: rotate the polygon flat, sweep
|
|
434
|
+
* horizontal scan lines spaced `gap` apart, intersect each with the edges, pair
|
|
435
|
+
* the crossings, then rotate the resulting segments back.
|
|
436
|
+
*
|
|
437
|
+
* `maxLines` caps density so a huge/under-spaced shape can't explode the segment
|
|
438
|
+
* count (it widens the step instead) — important for performance on dense charts.
|
|
439
|
+
*/
|
|
440
|
+
declare function polygonHachureLines(polygon: readonly Point[], gap: number, angleDeg: number, maxLines?: number): HachureSegment[];
|
|
441
|
+
/**
|
|
442
|
+
* Sample a circular arc into a polyline. Angles use the same convention as
|
|
443
|
+
* `shape/arc`: 0 at 12 o'clock, positive clockwise. Inclusive of both ends.
|
|
444
|
+
*/
|
|
445
|
+
declare function sampleArc(cx: number, cy: number, radius: number, startAngle: number, endAngle: number, segments: number): Point[];
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Largest-Triangle-Three-Buckets (LTTB) downsampling.
|
|
449
|
+
*
|
|
450
|
+
* LTTB reduces a dense series to a target point count while preserving the
|
|
451
|
+
* visual shape far better than naive every-Nth sampling — it keeps peaks,
|
|
452
|
+
* troughs, and the first/last points. We run it draw-side on pixel-projected
|
|
453
|
+
* points so a 100k-point line still strokes in ~plot-width segments.
|
|
454
|
+
*
|
|
455
|
+
* The decimator is generic over the point type (line uses {x,y}; area uses
|
|
456
|
+
* {x,y0,y1}) via x/y accessors, and is gap-aware: runs separated by a
|
|
457
|
+
* non-finite point are decimated independently and rejoined with a gap, so
|
|
458
|
+
* missing-value breaks survive downsampling.
|
|
459
|
+
*/
|
|
460
|
+
/** Core LTTB over a fully-finite run. Returns at most `threshold` points. */
|
|
461
|
+
declare function lttbRun<T>(run: readonly T[], threshold: number, getX: (p: T) => number, getY: (p: T) => number): T[];
|
|
462
|
+
interface DecimateOptions<T> {
|
|
463
|
+
getX: (p: T) => number;
|
|
464
|
+
getY: (p: T) => number;
|
|
465
|
+
/** Produce a gap marker placed between decimated runs. */
|
|
466
|
+
gap: () => T;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Gap-aware LTTB. Splits `points` on non-finite (x or y) markers, decimates each
|
|
470
|
+
* finite run to a share of `threshold` proportional to its length, and rejoins
|
|
471
|
+
* the runs with a single gap marker. When the finite total already fits within
|
|
472
|
+
* `threshold`, the input is returned unchanged (gaps preserved).
|
|
473
|
+
*/
|
|
474
|
+
declare function decimate<T>(points: readonly T[], threshold: number, opts: DecimateOptions<T>): T[];
|
|
475
|
+
|
|
476
|
+
interface AnimateOptions {
|
|
477
|
+
duration: number;
|
|
478
|
+
easing?: (t: number) => number;
|
|
479
|
+
onUpdate: (t: number) => void;
|
|
480
|
+
onComplete?: () => void;
|
|
481
|
+
now?: () => number;
|
|
482
|
+
raf?: (cb: (t: number) => void) => number;
|
|
483
|
+
cancelRaf?: (handle: number) => void;
|
|
484
|
+
}
|
|
485
|
+
interface AnimationHandle {
|
|
486
|
+
cancel(): void;
|
|
487
|
+
}
|
|
488
|
+
declare function animate(opts: AnimateOptions): AnimationHandle;
|
|
489
|
+
|
|
490
|
+
type EasingFunction = (t: number) => number;
|
|
491
|
+
declare function linear(t: number): number;
|
|
492
|
+
declare function quadIn(t: number): number;
|
|
493
|
+
declare function quadOut(t: number): number;
|
|
494
|
+
declare function quadInOut(t: number): number;
|
|
495
|
+
declare function cubicIn(t: number): number;
|
|
496
|
+
declare function cubicOut(t: number): number;
|
|
497
|
+
declare function cubicInOut(t: number): number;
|
|
498
|
+
declare function expoOut(t: number): number;
|
|
499
|
+
declare function expoInOut(t: number): number;
|
|
500
|
+
declare function sinInOut(t: number): number;
|
|
501
|
+
declare function backOut(t: number): number;
|
|
502
|
+
declare function bounceOut(t: number): number;
|
|
503
|
+
declare function elasticOut(t: number): number;
|
|
504
|
+
declare const easings: Record<string, EasingFunction>;
|
|
505
|
+
|
|
506
|
+
type AggOp = 'sum' | 'mean' | 'avg' | 'min' | 'max' | 'count' | 'countDistinct' | 'median' | 'first' | 'last';
|
|
507
|
+
interface PivotValueDef {
|
|
508
|
+
field: string;
|
|
509
|
+
op: AggOp;
|
|
510
|
+
label?: string;
|
|
511
|
+
}
|
|
512
|
+
interface PivotOptions {
|
|
513
|
+
rows?: string[];
|
|
514
|
+
columns?: string[];
|
|
515
|
+
values: PivotValueDef[];
|
|
516
|
+
includeRowSubtotals?: boolean;
|
|
517
|
+
includeColumnSubtotals?: boolean;
|
|
518
|
+
includeGrandTotals?: boolean;
|
|
519
|
+
sort?: 'asc' | 'desc' | 'none';
|
|
520
|
+
}
|
|
521
|
+
interface PivotHeaderNode {
|
|
522
|
+
key: string;
|
|
523
|
+
path: string[];
|
|
524
|
+
depth: number;
|
|
525
|
+
children: PivotHeaderNode[];
|
|
526
|
+
isSubtotal?: boolean;
|
|
527
|
+
isGrandTotal?: boolean;
|
|
528
|
+
span: number;
|
|
529
|
+
}
|
|
530
|
+
interface PivotCell {
|
|
531
|
+
values: (number | null)[];
|
|
532
|
+
}
|
|
533
|
+
interface PivotFlatRow {
|
|
534
|
+
path: string[];
|
|
535
|
+
depth: number;
|
|
536
|
+
isSubtotal?: boolean;
|
|
537
|
+
isGrandTotal?: boolean;
|
|
538
|
+
label: string;
|
|
539
|
+
cellsByColumnKey: Map<string, PivotCell>;
|
|
540
|
+
}
|
|
541
|
+
interface PivotResult {
|
|
542
|
+
rowTree: PivotHeaderNode[];
|
|
543
|
+
columnTree: PivotHeaderNode[];
|
|
544
|
+
columnLeaves: PivotHeaderNode[];
|
|
545
|
+
rows: PivotFlatRow[];
|
|
546
|
+
values: PivotValueDef[];
|
|
547
|
+
valueAt(rowPath: string[], colPath: string[], valueIndex: number): number | null;
|
|
548
|
+
}
|
|
549
|
+
type GroupedMap<T> = Map<string, GroupedMap<T> | T[]>;
|
|
550
|
+
declare function aggregateValues(values: unknown[], op: AggOp): number | null;
|
|
551
|
+
declare function groupBy<T extends Record<string, unknown>>(rows: T[], keyFields: string[]): GroupedMap<T>;
|
|
552
|
+
declare function pivot(data: Datum[], options: PivotOptions): PivotResult;
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Graphein design tokens.
|
|
556
|
+
*
|
|
557
|
+
* Flat, modern aesthetic: solid fills, minimal shadows, restrained radii, and a
|
|
558
|
+
* teal accent. Two built-in themes (light/dark). Token values are literals so
|
|
559
|
+
* this module stays dependency-free; charts combine these with the color module
|
|
560
|
+
* for derived ramps and contrast.
|
|
561
|
+
*/
|
|
562
|
+
interface ThemeColors {
|
|
563
|
+
/** Page/plot background. */
|
|
564
|
+
background: string;
|
|
565
|
+
/** Cards / elevated surfaces (KPI cards, tooltips, table headers). */
|
|
566
|
+
surface: string;
|
|
567
|
+
/** Primary text. */
|
|
568
|
+
text: string;
|
|
569
|
+
/** Secondary / muted text (axis labels, captions). */
|
|
570
|
+
textMuted: string;
|
|
571
|
+
/** Axis baseline color. */
|
|
572
|
+
axis: string;
|
|
573
|
+
/** Gridline color (lighter than the axis). */
|
|
574
|
+
grid: string;
|
|
575
|
+
/** Hairline borders. */
|
|
576
|
+
border: string;
|
|
577
|
+
/** Brand accent (selection, focus, primary series). */
|
|
578
|
+
accent: string;
|
|
579
|
+
/** Categorical series palette. */
|
|
580
|
+
palette: string[];
|
|
581
|
+
/** Positive / up semantics. */
|
|
582
|
+
positive: string;
|
|
583
|
+
/** Negative / down semantics. */
|
|
584
|
+
negative: string;
|
|
585
|
+
}
|
|
586
|
+
interface ThemeFont {
|
|
587
|
+
family: string;
|
|
588
|
+
size: {
|
|
589
|
+
tiny: number;
|
|
590
|
+
small: number;
|
|
591
|
+
base: number;
|
|
592
|
+
large: number;
|
|
593
|
+
title: number;
|
|
594
|
+
};
|
|
595
|
+
weight: {
|
|
596
|
+
normal: number;
|
|
597
|
+
medium: number;
|
|
598
|
+
bold: number;
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
interface ThemeTokens {
|
|
602
|
+
name: string;
|
|
603
|
+
dark: boolean;
|
|
604
|
+
color: ThemeColors;
|
|
605
|
+
font: ThemeFont;
|
|
606
|
+
spacing: {
|
|
607
|
+
xs: number;
|
|
608
|
+
sm: number;
|
|
609
|
+
md: number;
|
|
610
|
+
lg: number;
|
|
611
|
+
xl: number;
|
|
612
|
+
};
|
|
613
|
+
radius: {
|
|
614
|
+
sm: number;
|
|
615
|
+
md: number;
|
|
616
|
+
lg: number;
|
|
617
|
+
};
|
|
618
|
+
stroke: {
|
|
619
|
+
thin: number;
|
|
620
|
+
base: number;
|
|
621
|
+
thick: number;
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
declare const lightTheme: ThemeTokens;
|
|
625
|
+
declare const darkTheme: ThemeTokens;
|
|
626
|
+
declare const themes: Record<string, ThemeTokens>;
|
|
627
|
+
type ThemeInput = string | (Partial<ThemeTokens> & {
|
|
628
|
+
base?: string;
|
|
629
|
+
});
|
|
630
|
+
/**
|
|
631
|
+
* Resolve a theme reference into concrete tokens.
|
|
632
|
+
* - a name ('light' | 'dark') selects a built-in theme
|
|
633
|
+
* - an object deep-merges onto a base theme (override.base or override.dark picks it)
|
|
634
|
+
*/
|
|
635
|
+
declare function resolveTheme(theme?: ThemeInput): ThemeTokens;
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Theme-side support for the hand-drawn ("sketch") style.
|
|
639
|
+
*
|
|
640
|
+
* Two responsibilities:
|
|
641
|
+
* 1. `ensureSketchFont()` lazily loads the bundled handwriting font (Patrick
|
|
642
|
+
* Hand, SIL OFL) as a data URL — no network, works offline. The base64 blob
|
|
643
|
+
* lives in its own module that we `import()` dynamically so it is code-split
|
|
644
|
+
* out of the main bundle and only fetched when a chart actually sketches.
|
|
645
|
+
* 2. `withSketchFont()` swaps a theme's font family to the handwriting stack so
|
|
646
|
+
* every bit of text (titles, axes, legends, KPI/table/matrix) reads as drawn.
|
|
647
|
+
*/
|
|
648
|
+
|
|
649
|
+
/** The font-family name the injected `@font-face` registers. */
|
|
650
|
+
declare const SKETCH_FONT_NAME = "Patrick Hand";
|
|
651
|
+
/** Handwriting stack with graceful fallbacks for SSR / pre-load / odd hosts. */
|
|
652
|
+
declare const SKETCH_FONT_FAMILY = "'Patrick Hand', 'Segoe Print', 'Bradley Hand', 'Comic Sans MS', ui-rounded, cursive";
|
|
653
|
+
/**
|
|
654
|
+
* Ensure the handwriting font is available. Resolves `true` only when it was
|
|
655
|
+
* newly loaded this call (so the caller can trigger a single corrective redraw
|
|
656
|
+
* once real glyph metrics exist); resolves `false` if already present or if the
|
|
657
|
+
* environment can't load fonts (Node/SSR).
|
|
658
|
+
*/
|
|
659
|
+
declare function ensureSketchFont(): Promise<boolean>;
|
|
660
|
+
/** Return a copy of `tokens` whose text uses the handwriting font. */
|
|
661
|
+
declare function withSketchFont(tokens: ThemeTokens): ThemeTokens;
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Selection model — the declarative vocabulary for interactivity.
|
|
665
|
+
*
|
|
666
|
+
* A *selection* is the unit of interactivity: a named, JSON-serializable value
|
|
667
|
+
* that a visual publishes (by clicking marks, brushing, or changing a slicer)
|
|
668
|
+
* and that other visuals consume as either a **highlight** (emphasize matches,
|
|
669
|
+
* dim the rest) or a **filter** (subset the rows). Selections are plain data —
|
|
670
|
+
* no functions — so specs still round-trip through `JSON.stringify`.
|
|
671
|
+
*
|
|
672
|
+
* Inspired by Vega-Lite params/selections, but pared down to what coding agents
|
|
673
|
+
* need to wire dashboards: a `point`/`interval` definition, and four concrete
|
|
674
|
+
* resolved value shapes (point, set, range, text).
|
|
675
|
+
*/
|
|
676
|
+
/** How a selection is captured from user interaction. */
|
|
677
|
+
interface SelectionDef {
|
|
678
|
+
/**
|
|
679
|
+
* - `point`: discrete picks (click marks, choose categories). Toggling builds
|
|
680
|
+
* a set of selected tuples.
|
|
681
|
+
* - `interval`: a continuous range (brush on an axis, a min/max slider, a date
|
|
682
|
+
* range).
|
|
683
|
+
*/
|
|
684
|
+
type: 'point' | 'interval';
|
|
685
|
+
/** Pointer event that updates the selection. Default `'click'`. */
|
|
686
|
+
on?: 'click' | 'hover';
|
|
687
|
+
/**
|
|
688
|
+
* Data fields that identify the selection. Defaults to the chart's key channel
|
|
689
|
+
* (e.g. the `x`/`series` field for cartesian charts, the `field` of a slicer).
|
|
690
|
+
*/
|
|
691
|
+
fields?: string[];
|
|
692
|
+
/**
|
|
693
|
+
* Click an already-selected mark to deselect, and accumulate multiple picks.
|
|
694
|
+
* Default `true` for `on:'click'`.
|
|
695
|
+
*/
|
|
696
|
+
toggle?: boolean;
|
|
697
|
+
/**
|
|
698
|
+
* What an *empty* selection means for consumers: `'all'` (the default) treats
|
|
699
|
+
* "nothing selected" as "match everything" (no dimming / no filtering);
|
|
700
|
+
* `'none'` treats it as "match nothing".
|
|
701
|
+
*/
|
|
702
|
+
empty?: 'all' | 'none';
|
|
703
|
+
}
|
|
704
|
+
/** A named selection a visual defines. */
|
|
705
|
+
interface SelectionParam {
|
|
706
|
+
/** Unique name within a chart or dashboard. */
|
|
707
|
+
name: string;
|
|
708
|
+
select: SelectionDef;
|
|
709
|
+
/** Optional initial value. */
|
|
710
|
+
value?: SelectionValue;
|
|
711
|
+
}
|
|
712
|
+
/** Discrete picks: rows whose `fields` equal one of the selected `tuples`. */
|
|
713
|
+
interface PointSelection {
|
|
714
|
+
kind: 'point';
|
|
715
|
+
fields: string[];
|
|
716
|
+
/** Each tuple is one selected combination, aligned to `fields`. */
|
|
717
|
+
tuples: unknown[][];
|
|
718
|
+
}
|
|
719
|
+
/** Membership: rows whose `field` value is one of `values` (dropdown / list). */
|
|
720
|
+
interface SetSelection {
|
|
721
|
+
kind: 'set';
|
|
722
|
+
field: string;
|
|
723
|
+
values: unknown[];
|
|
724
|
+
}
|
|
725
|
+
/** A continuous span over `field` (numeric or temporal). Bounds are inclusive. */
|
|
726
|
+
interface RangeSelection {
|
|
727
|
+
kind: 'range';
|
|
728
|
+
field: string;
|
|
729
|
+
min?: number | string;
|
|
730
|
+
max?: number | string;
|
|
731
|
+
}
|
|
732
|
+
/** A case-insensitive substring match over `field` (search box). */
|
|
733
|
+
interface TextSelection {
|
|
734
|
+
kind: 'text';
|
|
735
|
+
field: string;
|
|
736
|
+
query: string;
|
|
737
|
+
}
|
|
738
|
+
/** The resolved value of a selection — what lives in the store. */
|
|
739
|
+
type SelectionValue = PointSelection | SetSelection | RangeSelection | TextSelection;
|
|
740
|
+
/** Reference a named selection for highlighting. */
|
|
741
|
+
interface HighlightConfig {
|
|
742
|
+
/** The param whose current value drives the emphasis. */
|
|
743
|
+
param: string;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* A literal, param-free predicate usable directly in `filter`. Each form maps to
|
|
747
|
+
* a {@link SelectionValue}, so the same matcher handles params and literals.
|
|
748
|
+
*/
|
|
749
|
+
type LiteralPredicate = {
|
|
750
|
+
field: string;
|
|
751
|
+
equals: unknown;
|
|
752
|
+
} | {
|
|
753
|
+
field: string;
|
|
754
|
+
oneOf: unknown[];
|
|
755
|
+
} | {
|
|
756
|
+
field: string;
|
|
757
|
+
range: [number | string, number | string];
|
|
758
|
+
} | {
|
|
759
|
+
field: string;
|
|
760
|
+
contains: string;
|
|
761
|
+
};
|
|
762
|
+
/** One clause of a `filter`: a named param or a literal predicate. */
|
|
763
|
+
type FilterClause = {
|
|
764
|
+
param: string;
|
|
765
|
+
} | LiteralPredicate;
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Graphein declarative chart specs.
|
|
769
|
+
*
|
|
770
|
+
* One chart = one JSON-serializable `ChartSpec`. The shape is intentionally
|
|
771
|
+
* Vega-Lite-flavored (data + encoding channels) so coding agents can generate
|
|
772
|
+
* charts reliably. Every field is plain JSON (no functions) so specs round-trip
|
|
773
|
+
* through `JSON.stringify`.
|
|
774
|
+
*/
|
|
775
|
+
/** How a data field maps onto a visual channel. */
|
|
776
|
+
interface FieldDef {
|
|
777
|
+
/** Column name in each datum. */
|
|
778
|
+
field: string;
|
|
779
|
+
/** Data type; inferred from the data when omitted. */
|
|
780
|
+
type?: FieldType;
|
|
781
|
+
/** Optional aggregation applied when grouping (e.g. sum of sales). */
|
|
782
|
+
aggregate?: AggOp;
|
|
783
|
+
/** Axis/legend title override. */
|
|
784
|
+
title?: string;
|
|
785
|
+
/** Number/date format hint (see format module), e.g. ',.0f' or '%b %Y'. */
|
|
786
|
+
format?: string;
|
|
787
|
+
/** Per-channel scale overrides. */
|
|
788
|
+
scale?: ScaleConfig;
|
|
789
|
+
}
|
|
790
|
+
type ScaleType = 'linear' | 'log' | 'time' | 'band' | 'point';
|
|
791
|
+
interface ScaleConfig {
|
|
792
|
+
type?: ScaleType;
|
|
793
|
+
/** Explicit domain; numbers for continuous, strings for categorical. */
|
|
794
|
+
domain?: [number, number] | string[];
|
|
795
|
+
/** Round the domain to nice values (continuous only). */
|
|
796
|
+
nice?: boolean;
|
|
797
|
+
/** Force the domain to include zero (continuous only). */
|
|
798
|
+
zero?: boolean;
|
|
799
|
+
/** Clamp out-of-domain values into range. */
|
|
800
|
+
clamp?: boolean;
|
|
801
|
+
/** Band/point padding (0..1). */
|
|
802
|
+
padding?: number;
|
|
803
|
+
/** Log base. */
|
|
804
|
+
base?: number;
|
|
805
|
+
/** Reverse the range. */
|
|
806
|
+
reverse?: boolean;
|
|
807
|
+
}
|
|
808
|
+
/** Visual encoding channels. Not every chart uses every channel. */
|
|
809
|
+
interface Encoding {
|
|
810
|
+
x?: FieldDef;
|
|
811
|
+
y?: FieldDef;
|
|
812
|
+
/** Upper bound for ranged area / band marks. */
|
|
813
|
+
y2?: FieldDef;
|
|
814
|
+
/** Color channel (series color or continuous color). */
|
|
815
|
+
color?: FieldDef;
|
|
816
|
+
/** Size channel (bubble radius). */
|
|
817
|
+
size?: FieldDef;
|
|
818
|
+
/** Splits data into multiple series (multi-line, grouped/stacked bars). */
|
|
819
|
+
series?: FieldDef;
|
|
820
|
+
/** Pie/donut angular measure. */
|
|
821
|
+
theta?: FieldDef;
|
|
822
|
+
/** Text/label channel. */
|
|
823
|
+
label?: FieldDef;
|
|
824
|
+
/** Sankey link source node. */
|
|
825
|
+
source?: FieldDef;
|
|
826
|
+
/** Sankey link target node. */
|
|
827
|
+
target?: FieldDef;
|
|
828
|
+
/** Magnitude channel (Sankey link value / generic measure). */
|
|
829
|
+
value?: FieldDef;
|
|
830
|
+
/** Geographic key matching a GeoJSON feature (choropleth). */
|
|
831
|
+
key?: FieldDef;
|
|
832
|
+
/** Funnel stage (ordered category, one bar per stage). */
|
|
833
|
+
stage?: FieldDef;
|
|
834
|
+
}
|
|
835
|
+
interface Dimensions {
|
|
836
|
+
/** Fixed width in px. Omit for responsive (fills container). */
|
|
837
|
+
width?: number;
|
|
838
|
+
/** Fixed height in px. Omit for responsive (fills container). */
|
|
839
|
+
height?: number;
|
|
840
|
+
/** Re-render on container resize (default true when width/height omitted). */
|
|
841
|
+
autoResize?: boolean;
|
|
842
|
+
}
|
|
843
|
+
interface AxisConfig {
|
|
844
|
+
show?: boolean;
|
|
845
|
+
title?: string;
|
|
846
|
+
grid?: boolean;
|
|
847
|
+
/** Approximate tick count. */
|
|
848
|
+
ticks?: number;
|
|
849
|
+
/** Explicit tick values. */
|
|
850
|
+
tickValues?: number[];
|
|
851
|
+
format?: string;
|
|
852
|
+
/** Show tick labels. */
|
|
853
|
+
labels?: boolean;
|
|
854
|
+
}
|
|
855
|
+
interface AxesConfig {
|
|
856
|
+
x?: AxisConfig;
|
|
857
|
+
y?: AxisConfig;
|
|
858
|
+
}
|
|
859
|
+
type LegendPosition = 'top' | 'right' | 'bottom' | 'left';
|
|
860
|
+
interface LegendConfig {
|
|
861
|
+
show?: boolean;
|
|
862
|
+
position?: LegendPosition;
|
|
863
|
+
title?: string;
|
|
864
|
+
}
|
|
865
|
+
interface TooltipConfig {
|
|
866
|
+
show?: boolean;
|
|
867
|
+
}
|
|
868
|
+
interface TitleConfig {
|
|
869
|
+
text?: string;
|
|
870
|
+
subtitle?: string;
|
|
871
|
+
align?: 'left' | 'center' | 'right';
|
|
872
|
+
}
|
|
873
|
+
interface AnimationConfig {
|
|
874
|
+
enabled?: boolean;
|
|
875
|
+
duration?: number;
|
|
876
|
+
easing?: string;
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Hand-drawn ("sketch") rendering options. Set `sketch: true` on any spec for
|
|
880
|
+
* the defaults, or pass this object to tune the look. All fields are plain JSON
|
|
881
|
+
* so specs still round-trip through `JSON.stringify`.
|
|
882
|
+
*/
|
|
883
|
+
interface SketchConfig {
|
|
884
|
+
/** Wobble magnitude. 0 ≈ clean, 1 = default, >1 = wilder. */
|
|
885
|
+
roughness?: number;
|
|
886
|
+
/** How much straight strokes bow between their endpoints. */
|
|
887
|
+
bowing?: number;
|
|
888
|
+
/** Fill treatment for closed marks (bars, wedges, cells). Default 'hachure'. */
|
|
889
|
+
fillStyle?: FillStyle;
|
|
890
|
+
/** Spacing (px) between hachure lines. Omit to derive from stroke width. */
|
|
891
|
+
hachureGap?: number;
|
|
892
|
+
/** Hachure angle in degrees (default -41). */
|
|
893
|
+
hachureAngle?: number;
|
|
894
|
+
/** Outline width multiplier (default 1). */
|
|
895
|
+
strokeWidth?: number;
|
|
896
|
+
/** Explicit PRNG seed; omit to derive a stable one from the spec. */
|
|
897
|
+
seed?: number;
|
|
898
|
+
/** Apply the hand-drawn font to titles/labels/axes (default true). */
|
|
899
|
+
font?: boolean;
|
|
900
|
+
}
|
|
901
|
+
/** Fields shared by all spec types. */
|
|
902
|
+
interface BaseSpec {
|
|
903
|
+
/** Row-oriented (tidy) data. Required for all charts/tables. */
|
|
904
|
+
data?: Datum[];
|
|
905
|
+
/** Theme name ('light' | 'dark') or a partial override object. */
|
|
906
|
+
theme?: ThemeInput;
|
|
907
|
+
dimensions?: Dimensions;
|
|
908
|
+
title?: string | TitleConfig;
|
|
909
|
+
/**
|
|
910
|
+
* Accessible description (alt text) for the chart. Used verbatim as the
|
|
911
|
+
* chart's `aria-label`; when omitted, a concise label is synthesized from the
|
|
912
|
+
* type, title, and data. Agents should set this to convey the chart's intent.
|
|
913
|
+
*/
|
|
914
|
+
description?: string;
|
|
915
|
+
legend?: LegendConfig | boolean;
|
|
916
|
+
tooltip?: TooltipConfig | boolean;
|
|
917
|
+
axes?: AxesConfig;
|
|
918
|
+
animation?: AnimationConfig | boolean;
|
|
919
|
+
/** Extra padding around the plot area. */
|
|
920
|
+
padding?: Partial<Insets>;
|
|
921
|
+
/** Background override (defaults to the theme background). */
|
|
922
|
+
background?: string;
|
|
923
|
+
/**
|
|
924
|
+
* Render the chart in a hand-drawn ("sketch") style — wobbly outlines, hachure
|
|
925
|
+
* fills, and a handwriting font. `true` uses the defaults; pass a `SketchConfig`
|
|
926
|
+
* to tune it. Omit (or `false`) for the default clean rendering.
|
|
927
|
+
*/
|
|
928
|
+
sketch?: boolean | SketchConfig;
|
|
929
|
+
/**
|
|
930
|
+
* Named selections this visual publishes. Clicking marks, brushing, or
|
|
931
|
+
* changing a slicer updates the param's value on the shared selection store,
|
|
932
|
+
* which other visuals can consume via {@link BaseSpec.highlight} or
|
|
933
|
+
* {@link BaseSpec.filter}. Pure data — no callbacks.
|
|
934
|
+
*/
|
|
935
|
+
params?: SelectionParam[];
|
|
936
|
+
/**
|
|
937
|
+
* Emphasize rows matching a selection and dim the rest. References a param by
|
|
938
|
+
* name (typically published by another visual for cross-highlighting). An array
|
|
939
|
+
* unions several sources: a row is emphasized if it matches *any* active one.
|
|
940
|
+
*/
|
|
941
|
+
highlight?: HighlightConfig | HighlightConfig[];
|
|
942
|
+
/**
|
|
943
|
+
* Subset this visual's rows to those matching every clause (logical AND).
|
|
944
|
+
* Each clause is a named param (cross-filter) or a literal predicate.
|
|
945
|
+
*/
|
|
946
|
+
filter?: FilterClause[];
|
|
947
|
+
}
|
|
948
|
+
type CurveType = 'linear' | 'monotone' | 'step' | 'stepBefore' | 'stepAfter' | 'catmullRom';
|
|
949
|
+
interface LineSpec extends BaseSpec {
|
|
950
|
+
type: 'line';
|
|
951
|
+
encoding: Encoding & {
|
|
952
|
+
x: FieldDef;
|
|
953
|
+
y: FieldDef;
|
|
954
|
+
};
|
|
955
|
+
curve?: CurveType;
|
|
956
|
+
/** Show point markers on the line. */
|
|
957
|
+
points?: boolean;
|
|
958
|
+
/** Fill the area under the line. */
|
|
959
|
+
area?: boolean;
|
|
960
|
+
}
|
|
961
|
+
interface AreaSpec extends BaseSpec {
|
|
962
|
+
type: 'area';
|
|
963
|
+
encoding: Encoding & {
|
|
964
|
+
x: FieldDef;
|
|
965
|
+
y: FieldDef;
|
|
966
|
+
};
|
|
967
|
+
curve?: CurveType;
|
|
968
|
+
/** Stack multiple series. */
|
|
969
|
+
stack?: boolean;
|
|
970
|
+
}
|
|
971
|
+
interface BarSpec extends BaseSpec {
|
|
972
|
+
type: 'bar';
|
|
973
|
+
encoding: Encoding & {
|
|
974
|
+
x: FieldDef;
|
|
975
|
+
y: FieldDef;
|
|
976
|
+
};
|
|
977
|
+
orientation?: 'vertical' | 'horizontal';
|
|
978
|
+
/** Stack series (default false). */
|
|
979
|
+
stack?: boolean;
|
|
980
|
+
/** Group series side-by-side (default when `series` present and not stacked). */
|
|
981
|
+
group?: boolean;
|
|
982
|
+
cornerRadius?: number;
|
|
983
|
+
}
|
|
984
|
+
interface ScatterSpec extends BaseSpec {
|
|
985
|
+
type: 'scatter';
|
|
986
|
+
encoding: Encoding & {
|
|
987
|
+
x: FieldDef;
|
|
988
|
+
y: FieldDef;
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
interface PieSpec extends BaseSpec {
|
|
992
|
+
type: 'pie';
|
|
993
|
+
encoding: Encoding & {
|
|
994
|
+
theta: FieldDef;
|
|
995
|
+
color: FieldDef;
|
|
996
|
+
};
|
|
997
|
+
/** true for a default donut, or a 0..1 inner-radius ratio. */
|
|
998
|
+
donut?: boolean | number;
|
|
999
|
+
/**
|
|
1000
|
+
* Value/percent labels. `true` (default) / `false` toggles them; pass a
|
|
1001
|
+
* {@link PieLabels} object to control placement (inside vs. outside callouts),
|
|
1002
|
+
* what each label says, and the connector style.
|
|
1003
|
+
*/
|
|
1004
|
+
labels?: boolean | PieLabels;
|
|
1005
|
+
}
|
|
1006
|
+
/** Fine-grained control over pie/donut slice labels. */
|
|
1007
|
+
interface PieLabels {
|
|
1008
|
+
/** Master toggle (default true). */
|
|
1009
|
+
show?: boolean;
|
|
1010
|
+
/**
|
|
1011
|
+
* Where labels sit:
|
|
1012
|
+
* - 'auto' (default): inside the slice when the text fits, otherwise an
|
|
1013
|
+
* outside callout with a leader line.
|
|
1014
|
+
* - 'inside': always inside (small slices are dropped if they don't fit).
|
|
1015
|
+
* - 'outside': always an outside callout with a leader line.
|
|
1016
|
+
*/
|
|
1017
|
+
placement?: 'inside' | 'outside' | 'auto';
|
|
1018
|
+
/**
|
|
1019
|
+
* What each label says (default: 'percent' inside, 'category-percent' for
|
|
1020
|
+
* outside callouts).
|
|
1021
|
+
*/
|
|
1022
|
+
content?: 'percent' | 'value' | 'category' | 'category-percent' | 'category-value';
|
|
1023
|
+
/** Hide labels for slices below this share of the total (0..1, default 0.01). */
|
|
1024
|
+
minShare?: number;
|
|
1025
|
+
/** Leader-line colour for outside callouts: the slice colour or a muted grey. */
|
|
1026
|
+
connector?: 'slice' | 'muted';
|
|
1027
|
+
}
|
|
1028
|
+
interface HeatmapSpec extends BaseSpec {
|
|
1029
|
+
type: 'heatmap';
|
|
1030
|
+
encoding: Encoding & {
|
|
1031
|
+
x: FieldDef;
|
|
1032
|
+
y: FieldDef;
|
|
1033
|
+
color: FieldDef;
|
|
1034
|
+
};
|
|
1035
|
+
/** Sequential/diverging ramp name. */
|
|
1036
|
+
scheme?: string;
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* A funnel chart: ordered `stage`s drawn as stacked, centered trapezoids whose
|
|
1040
|
+
* width encodes `value`. Ideal for conversion / drop-off across a pipeline. Each
|
|
1041
|
+
* stage shows its value and the share retained relative to the first (or the
|
|
1042
|
+
* previous) stage.
|
|
1043
|
+
*/
|
|
1044
|
+
interface FunnelSpec extends BaseSpec {
|
|
1045
|
+
type: 'funnel';
|
|
1046
|
+
encoding: Encoding & {
|
|
1047
|
+
stage: FieldDef;
|
|
1048
|
+
value: FieldDef;
|
|
1049
|
+
};
|
|
1050
|
+
/** Show stage labels + value/percent inside the funnel (default true). */
|
|
1051
|
+
labels?: boolean;
|
|
1052
|
+
/**
|
|
1053
|
+
* What the per-stage percentage is measured against:
|
|
1054
|
+
* - 'first' (default): share retained vs. the top of the funnel.
|
|
1055
|
+
* - 'previous': step conversion vs. the stage above.
|
|
1056
|
+
*/
|
|
1057
|
+
percent?: 'first' | 'previous';
|
|
1058
|
+
}
|
|
1059
|
+
type ValueRef = number | {
|
|
1060
|
+
field: string;
|
|
1061
|
+
aggregate?: AggOp;
|
|
1062
|
+
};
|
|
1063
|
+
interface KpiSpec extends BaseSpec {
|
|
1064
|
+
type: 'kpi';
|
|
1065
|
+
/** Literal value or a field (optionally aggregated over data). */
|
|
1066
|
+
value: ValueRef;
|
|
1067
|
+
label?: string;
|
|
1068
|
+
/** Delta vs. a comparison, drives the up/down indicator. */
|
|
1069
|
+
delta?: ValueRef;
|
|
1070
|
+
format?: string;
|
|
1071
|
+
/** Inline sparkline from a numeric field. */
|
|
1072
|
+
sparkline?: boolean | {
|
|
1073
|
+
field: string;
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
type ConditionalFormat = {
|
|
1077
|
+
type: 'colorScale';
|
|
1078
|
+
scheme?: string;
|
|
1079
|
+
domain?: [number, number];
|
|
1080
|
+
midpoint?: number;
|
|
1081
|
+
diverging?: boolean;
|
|
1082
|
+
target?: 'background' | 'text';
|
|
1083
|
+
} | {
|
|
1084
|
+
type: 'bar';
|
|
1085
|
+
color?: string;
|
|
1086
|
+
negativeColor?: string;
|
|
1087
|
+
domain?: [number, number];
|
|
1088
|
+
baseline?: 'zero' | 'min';
|
|
1089
|
+
showValue?: boolean;
|
|
1090
|
+
} | {
|
|
1091
|
+
type: 'icon';
|
|
1092
|
+
set?: 'arrows' | 'triangles' | 'dots' | 'trafficLights';
|
|
1093
|
+
rules?: IconRule[];
|
|
1094
|
+
position?: 'left' | 'right';
|
|
1095
|
+
} | {
|
|
1096
|
+
type: 'rules';
|
|
1097
|
+
rules: ValueRule[];
|
|
1098
|
+
};
|
|
1099
|
+
interface ValueRule {
|
|
1100
|
+
when: 'gt' | 'gte' | 'lt' | 'lte' | 'eq' | 'ne' | 'between';
|
|
1101
|
+
value: number | string;
|
|
1102
|
+
to?: number;
|
|
1103
|
+
background?: string;
|
|
1104
|
+
color?: string;
|
|
1105
|
+
weight?: 'bold' | 'normal';
|
|
1106
|
+
icon?: string;
|
|
1107
|
+
}
|
|
1108
|
+
interface IconRule {
|
|
1109
|
+
when: ValueRule['when'];
|
|
1110
|
+
value: number;
|
|
1111
|
+
to?: number;
|
|
1112
|
+
icon?: string;
|
|
1113
|
+
color?: string;
|
|
1114
|
+
}
|
|
1115
|
+
interface TableColumn {
|
|
1116
|
+
field: string;
|
|
1117
|
+
title?: string;
|
|
1118
|
+
type?: FieldType;
|
|
1119
|
+
format?: string;
|
|
1120
|
+
align?: 'left' | 'center' | 'right';
|
|
1121
|
+
width?: number;
|
|
1122
|
+
conditionalFormat?: ConditionalFormat;
|
|
1123
|
+
prefix?: string;
|
|
1124
|
+
suffix?: string;
|
|
1125
|
+
negativeStyle?: 'sign' | 'parens' | 'red' | 'parens-red';
|
|
1126
|
+
hidden?: boolean;
|
|
1127
|
+
sortable?: boolean;
|
|
1128
|
+
wrap?: boolean;
|
|
1129
|
+
group?: string;
|
|
1130
|
+
total?: AggOp | false;
|
|
1131
|
+
}
|
|
1132
|
+
interface TableSpec extends BaseSpec {
|
|
1133
|
+
type: 'table';
|
|
1134
|
+
/** Explicit columns; inferred from data keys when omitted. */
|
|
1135
|
+
columns?: TableColumn[];
|
|
1136
|
+
sort?: {
|
|
1137
|
+
field: string;
|
|
1138
|
+
order?: 'asc' | 'desc';
|
|
1139
|
+
};
|
|
1140
|
+
density?: 'comfortable' | 'standard' | 'compact';
|
|
1141
|
+
totals?: boolean | {
|
|
1142
|
+
label?: string;
|
|
1143
|
+
};
|
|
1144
|
+
/** Zebra striping (off by default — flat aesthetic). */
|
|
1145
|
+
striped?: boolean;
|
|
1146
|
+
/** Sticky header (default true). */
|
|
1147
|
+
stickyHeader?: boolean;
|
|
1148
|
+
}
|
|
1149
|
+
interface MatrixValueDef {
|
|
1150
|
+
field: string;
|
|
1151
|
+
op: AggOp;
|
|
1152
|
+
label?: string;
|
|
1153
|
+
format?: string;
|
|
1154
|
+
conditionalFormat?: ConditionalFormat;
|
|
1155
|
+
prefix?: string;
|
|
1156
|
+
suffix?: string;
|
|
1157
|
+
negativeStyle?: TableColumn['negativeStyle'];
|
|
1158
|
+
showAs?: 'value' | 'percentOfRow' | 'percentOfColumn' | 'percentOfTotal';
|
|
1159
|
+
}
|
|
1160
|
+
interface MatrixSpec extends BaseSpec {
|
|
1161
|
+
type: 'matrix';
|
|
1162
|
+
/** Row grouping fields (hierarchical). */
|
|
1163
|
+
rows: string[];
|
|
1164
|
+
/** Column grouping fields (hierarchical). */
|
|
1165
|
+
columns?: string[];
|
|
1166
|
+
values: MatrixValueDef[];
|
|
1167
|
+
subtotals?: boolean;
|
|
1168
|
+
grandTotals?: boolean;
|
|
1169
|
+
density?: 'comfortable' | 'standard' | 'compact';
|
|
1170
|
+
columnSort?: {
|
|
1171
|
+
by: 'value' | 'label';
|
|
1172
|
+
valueIndex?: number;
|
|
1173
|
+
order?: 'asc' | 'desc';
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
interface BoxSpec extends BaseSpec {
|
|
1177
|
+
type: 'box';
|
|
1178
|
+
/**
|
|
1179
|
+
* `x` is the category axis (one box per category); `y` holds the raw
|
|
1180
|
+
* observations that are summarized into quartiles. Add `series` to draw
|
|
1181
|
+
* grouped boxes side-by-side within each category.
|
|
1182
|
+
*/
|
|
1183
|
+
encoding: Encoding & {
|
|
1184
|
+
x: FieldDef;
|
|
1185
|
+
y: FieldDef;
|
|
1186
|
+
};
|
|
1187
|
+
/**
|
|
1188
|
+
* Whisker rule:
|
|
1189
|
+
* - 'tukey' (default): whiskers reach the furthest points within 1.5×IQR of
|
|
1190
|
+
* the quartiles; points beyond are drawn as outliers.
|
|
1191
|
+
* - 'minMax': whiskers span the full data range (no outliers).
|
|
1192
|
+
*/
|
|
1193
|
+
whisker?: 'tukey' | 'minMax';
|
|
1194
|
+
/** Draw outlier points beyond the whiskers (tukey only; default true). */
|
|
1195
|
+
outliers?: boolean;
|
|
1196
|
+
}
|
|
1197
|
+
interface SankeySpec extends BaseSpec {
|
|
1198
|
+
type: 'sankey';
|
|
1199
|
+
/**
|
|
1200
|
+
* Each data row is one link. Nodes are derived from the distinct `source`
|
|
1201
|
+
* and `target` values; `value` is the flow magnitude (link/node thickness).
|
|
1202
|
+
*/
|
|
1203
|
+
encoding: Encoding & {
|
|
1204
|
+
source: FieldDef;
|
|
1205
|
+
target: FieldDef;
|
|
1206
|
+
value: FieldDef;
|
|
1207
|
+
};
|
|
1208
|
+
/** Node block width in px (default 16). */
|
|
1209
|
+
nodeWidth?: number;
|
|
1210
|
+
/** Vertical gap between stacked nodes in px (default 14). */
|
|
1211
|
+
nodePadding?: number;
|
|
1212
|
+
/** Show the node total alongside its label (default true). */
|
|
1213
|
+
nodeValues?: boolean;
|
|
1214
|
+
}
|
|
1215
|
+
/** A GeoJSON position: [longitude, latitude] (or projected [x, y]). */
|
|
1216
|
+
type GeoPosition = [number, number];
|
|
1217
|
+
interface GeoPolygon {
|
|
1218
|
+
type: 'Polygon';
|
|
1219
|
+
/** Rings: the first is the exterior, the rest are holes. */
|
|
1220
|
+
coordinates: GeoPosition[][];
|
|
1221
|
+
}
|
|
1222
|
+
interface GeoMultiPolygon {
|
|
1223
|
+
type: 'MultiPolygon';
|
|
1224
|
+
coordinates: GeoPosition[][][];
|
|
1225
|
+
}
|
|
1226
|
+
type GeoGeometry = GeoPolygon | GeoMultiPolygon;
|
|
1227
|
+
interface GeoFeature {
|
|
1228
|
+
type: 'Feature';
|
|
1229
|
+
id?: string | number;
|
|
1230
|
+
properties?: Record<string, unknown> | null;
|
|
1231
|
+
geometry: GeoGeometry | null;
|
|
1232
|
+
}
|
|
1233
|
+
interface GeoFeatureCollection {
|
|
1234
|
+
type: 'FeatureCollection';
|
|
1235
|
+
features: GeoFeature[];
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* How feature coordinates are mapped to the screen:
|
|
1239
|
+
* - 'mercator' / 'equirectangular': geographic [lon, lat] projections.
|
|
1240
|
+
* - 'identity': coordinates are already planar [x, y] in screen orientation
|
|
1241
|
+
* (y increases downward), e.g. data pre-projected with Albers USA. Useful for
|
|
1242
|
+
* composite projections or non-geographic polygon maps.
|
|
1243
|
+
*/
|
|
1244
|
+
type MapProjection = 'mercator' | 'equirectangular' | 'identity';
|
|
1245
|
+
interface ChoroplethSpec extends BaseSpec {
|
|
1246
|
+
type: 'choropleth';
|
|
1247
|
+
/** Map geometry: a GeoJSON FeatureCollection of Polygon/MultiPolygon features. */
|
|
1248
|
+
geo: GeoFeatureCollection;
|
|
1249
|
+
/**
|
|
1250
|
+
* `key` joins each data row to a feature; `color` is the numeric value that
|
|
1251
|
+
* drives the fill via a sequential color scale.
|
|
1252
|
+
*/
|
|
1253
|
+
encoding: Encoding & {
|
|
1254
|
+
key: FieldDef;
|
|
1255
|
+
color: FieldDef;
|
|
1256
|
+
};
|
|
1257
|
+
/**
|
|
1258
|
+
* Where to read a feature's join id: a property name (e.g. 'name' for
|
|
1259
|
+
* `feature.properties.name'), or 'id' for the top-level `feature.id`.
|
|
1260
|
+
* Defaults to trying `feature.id`, then `properties.id`, then `properties.name`.
|
|
1261
|
+
*/
|
|
1262
|
+
featureId?: string;
|
|
1263
|
+
/** Geographic projection (default 'mercator'). */
|
|
1264
|
+
projection?: MapProjection;
|
|
1265
|
+
/** Sequential color scheme name (e.g. 'teal', 'blues', 'viridis'). */
|
|
1266
|
+
scheme?: string;
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* Slicer visuals — interactive controls that *publish* a selection (rather than
|
|
1270
|
+
* plotting marks). A slicer cross-filters (or cross-highlights) every visual that
|
|
1271
|
+
* consumes its param. They are ordinary DOM widgets, so they validate and render
|
|
1272
|
+
* standalone, and slot into a dashboard like any other view.
|
|
1273
|
+
*
|
|
1274
|
+
* Every slicer reads a single `field` and writes to a named param (defaulting to
|
|
1275
|
+
* the field name, so a chart's `filter: [{ param: 'region' }]` auto-connects to a
|
|
1276
|
+
* `field: 'region'` slicer). Options/bounds are derived from the *unfiltered*
|
|
1277
|
+
* data so a slicer never hides its own choices.
|
|
1278
|
+
*/
|
|
1279
|
+
interface BaseSlicerSpec extends BaseSpec {
|
|
1280
|
+
/** The data field this slicer reads its options/bounds from and filters on. */
|
|
1281
|
+
field: string;
|
|
1282
|
+
/**
|
|
1283
|
+
* The param name this slicer publishes to. Defaults to `field`, which makes a
|
|
1284
|
+
* slicer auto-wire to any visual filtering/highlighting on that param name.
|
|
1285
|
+
*/
|
|
1286
|
+
param?: string;
|
|
1287
|
+
/** Label shown above the control. Defaults to `title` or the field name. */
|
|
1288
|
+
label?: string;
|
|
1289
|
+
/** How consumers should react: emphasize matches or subset rows. Default 'filter'. */
|
|
1290
|
+
as?: 'filter' | 'highlight';
|
|
1291
|
+
}
|
|
1292
|
+
/** Choose one (single) or several (multi) of a field's distinct values. */
|
|
1293
|
+
interface DropdownSlicerSpec extends BaseSlicerSpec {
|
|
1294
|
+
type: 'dropdown';
|
|
1295
|
+
/** Allow choosing multiple values (emits a set). Default false (single-select). */
|
|
1296
|
+
multiple?: boolean;
|
|
1297
|
+
/** Placeholder shown when nothing is selected. */
|
|
1298
|
+
placeholder?: string;
|
|
1299
|
+
}
|
|
1300
|
+
/** A debounced case-insensitive substring filter over a text field. */
|
|
1301
|
+
interface SearchSlicerSpec extends BaseSlicerSpec {
|
|
1302
|
+
type: 'search';
|
|
1303
|
+
placeholder?: string;
|
|
1304
|
+
/** Debounce in ms before publishing the query (default 200). */
|
|
1305
|
+
debounce?: number;
|
|
1306
|
+
}
|
|
1307
|
+
/** A scrollable checkbox list of a field's distinct values (multi-select). */
|
|
1308
|
+
interface ListSlicerSpec extends BaseSlicerSpec {
|
|
1309
|
+
type: 'list';
|
|
1310
|
+
/** Show a search-within box once options exceed this count (default 8). */
|
|
1311
|
+
searchThreshold?: number;
|
|
1312
|
+
/** Show the "Select all" / "Clear" row (default true). */
|
|
1313
|
+
selectAll?: boolean;
|
|
1314
|
+
}
|
|
1315
|
+
/** A numeric min/max range over a quantitative field (dual-thumb slider). */
|
|
1316
|
+
interface RangeSlicerSpec extends BaseSlicerSpec {
|
|
1317
|
+
type: 'range';
|
|
1318
|
+
/** Lower bound; defaults to the data minimum of `field`. */
|
|
1319
|
+
min?: number;
|
|
1320
|
+
/** Upper bound; defaults to the data maximum of `field`. */
|
|
1321
|
+
max?: number;
|
|
1322
|
+
/** Thumb step; defaults to a sensible fraction of the range. */
|
|
1323
|
+
step?: number;
|
|
1324
|
+
/** Number format hint for the value labels (e.g. ',.0f'). */
|
|
1325
|
+
format?: string;
|
|
1326
|
+
}
|
|
1327
|
+
/** A temporal min/max range over a date field, with relative presets. */
|
|
1328
|
+
interface DateRangeSlicerSpec extends BaseSlicerSpec {
|
|
1329
|
+
type: 'dateRange';
|
|
1330
|
+
/** Show relative presets (last 7 / 30 / 90 days, all). Default true. */
|
|
1331
|
+
presets?: boolean;
|
|
1332
|
+
/** Date format hint for the value labels (e.g. '%b %e, %Y'). */
|
|
1333
|
+
format?: string;
|
|
1334
|
+
}
|
|
1335
|
+
type SlicerSpec = DropdownSlicerSpec | SearchSlicerSpec | ListSlicerSpec | RangeSlicerSpec | DateRangeSlicerSpec;
|
|
1336
|
+
type SlicerType = SlicerSpec['type'];
|
|
1337
|
+
declare const SLICER_TYPES: readonly SlicerType[];
|
|
1338
|
+
type ChartSpec = LineSpec | AreaSpec | BarSpec | ScatterSpec | PieSpec | HeatmapSpec | FunnelSpec | KpiSpec | TableSpec | MatrixSpec | BoxSpec | SankeySpec | ChoroplethSpec | DropdownSlicerSpec | SearchSlicerSpec | ListSlicerSpec | RangeSlicerSpec | DateRangeSlicerSpec;
|
|
1339
|
+
type ChartType = ChartSpec['type'];
|
|
1340
|
+
declare const CHART_TYPES: readonly ChartType[];
|
|
1341
|
+
|
|
1342
|
+
/**
|
|
1343
|
+
* Entrance-animation resolution.
|
|
1344
|
+
*
|
|
1345
|
+
* Pure policy that turns a spec's `animation` field (plus environment signals:
|
|
1346
|
+
* `prefers-reduced-motion` and a global screenshot kill-switch) into concrete
|
|
1347
|
+
* `{ enabled, duration, easing }`. Kept DOM-free so it's trivially unit-tested;
|
|
1348
|
+
* the runtime drives the actual frames.
|
|
1349
|
+
*/
|
|
1350
|
+
|
|
1351
|
+
interface ResolvedEntrance {
|
|
1352
|
+
enabled: boolean;
|
|
1353
|
+
/** Milliseconds. */
|
|
1354
|
+
duration: number;
|
|
1355
|
+
easing: EasingFunction;
|
|
1356
|
+
}
|
|
1357
|
+
/** Default entrance duration (ms) when a spec enables animation without one. */
|
|
1358
|
+
declare const DEFAULT_ENTRANCE_DURATION = 480;
|
|
1359
|
+
interface EntranceContext {
|
|
1360
|
+
/** Honor the OS "reduce motion" setting (disables animation). */
|
|
1361
|
+
reducedMotion?: boolean;
|
|
1362
|
+
/** Global kill-switch (e.g. screenshot/automation harnesses). */
|
|
1363
|
+
disabled?: boolean;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Resolve the effective entrance animation. Motion is suppressed when reduced
|
|
1367
|
+
* motion is requested, when globally disabled, or when the spec opts out
|
|
1368
|
+
* (`animation: false` / `{ enabled: false }` / non-positive duration).
|
|
1369
|
+
*/
|
|
1370
|
+
declare function resolveEntrance(animation: AnimationConfig | boolean | undefined, ctx?: EntranceContext): ResolvedEntrance;
|
|
1371
|
+
|
|
1372
|
+
declare function prefersReducedMotion(): boolean;
|
|
1373
|
+
|
|
1374
|
+
declare function interpolateNumber(a: number, b: number): (t: number) => number;
|
|
1375
|
+
declare function interpolateArray(a: number[], b: number[]): (t: number) => number[];
|
|
1376
|
+
declare function interpolateObject<T extends Record<string, number>>(a: T, b: T): (t: number) => T;
|
|
1377
|
+
declare function interpolateNumberArray(a: ArrayLike<number>, b: ArrayLike<number>): (t: number) => number[];
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* Update-transition resolution.
|
|
1381
|
+
*
|
|
1382
|
+
* Pure policy (mirrors {@link resolveEntrance}) that turns a spec's `animation`
|
|
1383
|
+
* field — plus environment signals (`prefers-reduced-motion` and the global
|
|
1384
|
+
* screenshot kill-switch) — into concrete `{ enabled, duration, easing }` for the
|
|
1385
|
+
* cross-fade played when a chart's data/config changes via `update()`. Kept
|
|
1386
|
+
* DOM-free so it's trivially unit-tested; the runtime drives the actual frames.
|
|
1387
|
+
*/
|
|
1388
|
+
|
|
1389
|
+
/** Default update cross-fade duration (ms). Snappier than the entrance. */
|
|
1390
|
+
declare const DEFAULT_UPDATE_DURATION = 360;
|
|
1391
|
+
interface UpdateContext {
|
|
1392
|
+
/** Honor the OS "reduce motion" setting (disables animation). */
|
|
1393
|
+
reducedMotion?: boolean;
|
|
1394
|
+
/** Global kill-switch (e.g. screenshot/automation harnesses). */
|
|
1395
|
+
disabled?: boolean;
|
|
1396
|
+
}
|
|
1397
|
+
/**
|
|
1398
|
+
* Resolve the effective update transition. Motion is suppressed when reduced
|
|
1399
|
+
* motion is requested, when globally disabled, or when the spec opts out
|
|
1400
|
+
* (`animation: false` / `{ enabled: false }` / non-positive duration). A custom
|
|
1401
|
+
* `easing` defaults to `cubicInOut` (symmetric — reads well for a cross-fade).
|
|
1402
|
+
*/
|
|
1403
|
+
declare function resolveUpdate(animation: AnimationConfig | boolean | undefined, ctx?: UpdateContext): ResolvedEntrance;
|
|
1404
|
+
|
|
1405
|
+
/**
|
|
1406
|
+
* Accessible descriptions for charts.
|
|
1407
|
+
*
|
|
1408
|
+
* Canvas marks are invisible to assistive technology, so every chart needs a
|
|
1409
|
+
* concise accessible *name* (what it is) plus, for canvas-drawn charts, a
|
|
1410
|
+
* visually-hidden data table (the numbers behind it — see `./table`). This
|
|
1411
|
+
* module owns the pure, DOM-free summarization logic.
|
|
1412
|
+
*/
|
|
1413
|
+
|
|
1414
|
+
/** Human-readable label for a chart type (e.g. `'bar'` → `'Bar chart'`). */
|
|
1415
|
+
declare function chartTypeLabel(type: string): string;
|
|
1416
|
+
/** Extract the chart's title text from a `string | TitleConfig` title field. */
|
|
1417
|
+
declare function chartTitleText(spec: ChartSpec): string | undefined;
|
|
1418
|
+
interface ChartSummary {
|
|
1419
|
+
/** Concise accessible name for the chart root (used as `aria-label`). */
|
|
1420
|
+
label: string;
|
|
1421
|
+
/** Number of data rows backing the chart. */
|
|
1422
|
+
rowCount: number;
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Build a concise accessible name for a chart. An explicit `spec.description`
|
|
1426
|
+
* always wins (agents can supply precise alt text); otherwise we synthesize
|
|
1427
|
+
* `"<Type>: <title>. <n> data points."` from the spec.
|
|
1428
|
+
*/
|
|
1429
|
+
declare function summarizeChart(spec: ChartSpec): ChartSummary;
|
|
1430
|
+
|
|
1431
|
+
/**
|
|
1432
|
+
* Visually-hidden data-table fallback.
|
|
1433
|
+
*
|
|
1434
|
+
* For canvas-drawn charts (line/area/bar/scatter/pie/heatmap) the values live
|
|
1435
|
+
* only in pixels, so we mirror the chart's `data` into an off-screen semantic
|
|
1436
|
+
* `<table>`. Screen-reader users get the real numbers; sighted users see
|
|
1437
|
+
* nothing. Charts that already render real DOM text (table/matrix/kpi) don't
|
|
1438
|
+
* need this.
|
|
1439
|
+
*/
|
|
1440
|
+
|
|
1441
|
+
/** Canonical "visually hidden" style — present to AT, invisible on screen. */
|
|
1442
|
+
declare const SR_ONLY_STYLE: string;
|
|
1443
|
+
/**
|
|
1444
|
+
* Build a visually-hidden `<table>` mirroring the chart's data. Returns `null`
|
|
1445
|
+
* when there is nothing useful to tabulate (no rows or no columns).
|
|
1446
|
+
*/
|
|
1447
|
+
declare function buildDataTableFallback(spec: ChartSpec): HTMLTableElement | null;
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* A single hi-DPI Canvas2D layer. Drawing is done in CSS pixels — the context
|
|
1451
|
+
* transform is scaled by the device pixel ratio so output stays crisp.
|
|
1452
|
+
*/
|
|
1453
|
+
declare class CanvasLayer {
|
|
1454
|
+
readonly canvas: HTMLCanvasElement;
|
|
1455
|
+
readonly ctx: CanvasRenderingContext2D;
|
|
1456
|
+
/** Logical (CSS px) width. */
|
|
1457
|
+
width: number;
|
|
1458
|
+
/** Logical (CSS px) height. */
|
|
1459
|
+
height: number;
|
|
1460
|
+
dpr: number;
|
|
1461
|
+
constructor(canvas?: HTMLCanvasElement);
|
|
1462
|
+
/** Resize the layer to a CSS size; updates the backing store + DPR transform. */
|
|
1463
|
+
resize(width: number, height: number, dpr?: number): void;
|
|
1464
|
+
/** Clear the entire backing store regardless of the current transform. */
|
|
1465
|
+
clear(): void;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/**
|
|
1469
|
+
* A rendering Surface mounted inside a user-provided container. It owns a stack
|
|
1470
|
+
* of layers:
|
|
1471
|
+
* - `marks` : static Canvas2D layer for data marks + gridlines
|
|
1472
|
+
* - `interaction` : Canvas2D layer for hover/crosshair (redrawn cheaply)
|
|
1473
|
+
* - `overlay` : HTML layer for crisp text (axis labels, legend, tooltip)
|
|
1474
|
+
*
|
|
1475
|
+
* The Surface creates a single wrapper element it fully owns, so it never
|
|
1476
|
+
* clobbers other content in the container.
|
|
1477
|
+
*/
|
|
1478
|
+
declare class Surface {
|
|
1479
|
+
readonly container: HTMLElement;
|
|
1480
|
+
readonly root: HTMLDivElement;
|
|
1481
|
+
readonly marks: CanvasLayer;
|
|
1482
|
+
readonly interaction: CanvasLayer;
|
|
1483
|
+
readonly overlay: HTMLDivElement;
|
|
1484
|
+
/** Visually-hidden container for the screen-reader data-table fallback. */
|
|
1485
|
+
readonly a11y: HTMLDivElement;
|
|
1486
|
+
width: number;
|
|
1487
|
+
height: number;
|
|
1488
|
+
dpr: number;
|
|
1489
|
+
constructor(container: HTMLElement);
|
|
1490
|
+
resize(width: number, height: number, dpr?: number): void;
|
|
1491
|
+
/** Clear both canvas layers and empty the HTML overlay. */
|
|
1492
|
+
clear(): void;
|
|
1493
|
+
/** Remove all DOM created by this Surface. */
|
|
1494
|
+
destroy(): void;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
/**
|
|
1498
|
+
* Apply accessibility semantics to a rendered chart Surface.
|
|
1499
|
+
*
|
|
1500
|
+
* Called by the runtime after each draw. Idempotent: safe to re-run on every
|
|
1501
|
+
* update/resize.
|
|
1502
|
+
*/
|
|
1503
|
+
|
|
1504
|
+
declare function applyA11y(surface: Surface, spec: ChartSpec): void;
|
|
1505
|
+
|
|
1506
|
+
/** Environment helpers — safe in both browser and Node (SSR/test). */
|
|
1507
|
+
declare const isBrowser: boolean;
|
|
1508
|
+
/** Current device pixel ratio (defaults to 1 outside the browser). */
|
|
1509
|
+
declare function getDevicePixelRatio(): number;
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
* Pure sizing math for hi-DPI canvases (no DOM — unit testable).
|
|
1513
|
+
*/
|
|
1514
|
+
interface BackingSize {
|
|
1515
|
+
/** Logical CSS pixel width. */
|
|
1516
|
+
cssWidth: number;
|
|
1517
|
+
/** Logical CSS pixel height. */
|
|
1518
|
+
cssHeight: number;
|
|
1519
|
+
/** Backing-store (device) pixel width. */
|
|
1520
|
+
pixelWidth: number;
|
|
1521
|
+
/** Backing-store (device) pixel height. */
|
|
1522
|
+
pixelHeight: number;
|
|
1523
|
+
/** Effective device pixel ratio used. */
|
|
1524
|
+
dpr: number;
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Compute the backing-store pixel size for a canvas of the given CSS size at a
|
|
1528
|
+
* device pixel ratio. Backing dimensions are at least 1px so the canvas stays valid.
|
|
1529
|
+
*/
|
|
1530
|
+
declare function computeBackingSize(width: number, height: number, dpr: number): BackingSize;
|
|
1531
|
+
|
|
1532
|
+
/** Tiny DOM helpers (browser-only). */
|
|
1533
|
+
declare function setStyle(el: HTMLElement, style: Partial<CSSStyleDeclaration>): void;
|
|
1534
|
+
declare function createDiv(className?: string, style?: Partial<CSSStyleDeclaration>): HTMLDivElement;
|
|
1535
|
+
/** Absolute-position a layer to fill its positioned parent. */
|
|
1536
|
+
declare const fillParentStyle: Partial<CSSStyleDeclaration>;
|
|
1537
|
+
|
|
1538
|
+
/**
|
|
1539
|
+
* Text measurement using a shared offscreen canvas context. Falls back to a
|
|
1540
|
+
* rough heuristic when no canvas is available (SSR/test).
|
|
1541
|
+
*/
|
|
1542
|
+
interface TextMetricsLite {
|
|
1543
|
+
width: number;
|
|
1544
|
+
}
|
|
1545
|
+
/** Measure the advance width of `text` rendered with the given CSS `font`. */
|
|
1546
|
+
declare function measureText(text: string, font: string): TextMetricsLite;
|
|
1547
|
+
/** Build a CSS font shorthand string. */
|
|
1548
|
+
declare function fontString(size: number, family: string, weight?: number | string, style?: string): string;
|
|
1549
|
+
|
|
1550
|
+
/**
|
|
1551
|
+
* Dashboard spec — the single-JSON, agent-facing layer that composes several
|
|
1552
|
+
* charts and slicers into one cross-interacting page.
|
|
1553
|
+
*
|
|
1554
|
+
* A dashboard owns a shared dataset and a shared selection store, lays its views
|
|
1555
|
+
* out on a responsive grid, and *auto-wires* cross-interaction: slicers filter
|
|
1556
|
+
* the charts, and clicking a chart cross-highlights the other charts that share
|
|
1557
|
+
* the selected field. Everything is plain JSON — no callbacks — so a dashboard
|
|
1558
|
+
* round-trips through `JSON.stringify` just like a chart.
|
|
1559
|
+
*/
|
|
1560
|
+
|
|
1561
|
+
interface DashboardResponsiveSpan {
|
|
1562
|
+
/** Dashboard/section width threshold in px. Smallest matching maxWidth wins. */
|
|
1563
|
+
maxWidth: number;
|
|
1564
|
+
/** Override column span at this width. */
|
|
1565
|
+
w?: number;
|
|
1566
|
+
/** Override row span at this width. */
|
|
1567
|
+
h?: number;
|
|
1568
|
+
/** Hide the view at this width. */
|
|
1569
|
+
hidden?: boolean;
|
|
1570
|
+
}
|
|
1571
|
+
/** One placed view in a dashboard: a chart or slicer spec plus grid placement. */
|
|
1572
|
+
interface DashboardView {
|
|
1573
|
+
/** Unique id within the dashboard (used for layout + link references). */
|
|
1574
|
+
id: string;
|
|
1575
|
+
/** The chart or slicer to render. Inherits the dashboard's `data` when omitted. */
|
|
1576
|
+
spec: ChartSpec;
|
|
1577
|
+
/** Grid column start (1-based). Omit for auto-flow placement. */
|
|
1578
|
+
x?: number;
|
|
1579
|
+
/** Grid row start (1-based). Omit for auto-flow placement. */
|
|
1580
|
+
y?: number;
|
|
1581
|
+
/** Column span (in grid columns). Defaults by view type. */
|
|
1582
|
+
w?: number;
|
|
1583
|
+
/** Row span (in grid rows). Defaults by view type. */
|
|
1584
|
+
h?: number;
|
|
1585
|
+
/** Optional card title drawn by the dashboard chrome. */
|
|
1586
|
+
title?: string;
|
|
1587
|
+
/** Optional card subtitle drawn below the dashboard card title. */
|
|
1588
|
+
subtitle?: string;
|
|
1589
|
+
/** Whether to draw the card frame. Defaults to true. */
|
|
1590
|
+
frame?: boolean;
|
|
1591
|
+
/** Card background override. */
|
|
1592
|
+
background?: string;
|
|
1593
|
+
/** Solid accent bar color for the card. */
|
|
1594
|
+
accent?: string;
|
|
1595
|
+
/** Card content padding. Defaults to 'standard'. */
|
|
1596
|
+
padding?: 'none' | 'standard';
|
|
1597
|
+
/** Per-view responsive span overrides; smallest matching maxWidth wins. */
|
|
1598
|
+
responsive?: DashboardResponsiveSpan[];
|
|
1599
|
+
}
|
|
1600
|
+
/** Explicit cross-interaction from one view to others (overrides auto-wiring). */
|
|
1601
|
+
interface InteractionLink {
|
|
1602
|
+
/** Source view id (the visual whose selection drives the interaction). */
|
|
1603
|
+
source: string;
|
|
1604
|
+
/** Target view id(s), or '*' for every other view. */
|
|
1605
|
+
target: string | string[] | '*';
|
|
1606
|
+
/** How targets react. Default: 'filter' for slicers, 'highlight' for charts. */
|
|
1607
|
+
as?: 'highlight' | 'filter' | 'none';
|
|
1608
|
+
/** Identity fields to match on. Defaults to the source's key field(s). */
|
|
1609
|
+
fields?: string[];
|
|
1610
|
+
}
|
|
1611
|
+
interface DashboardLayout {
|
|
1612
|
+
/** Number of grid columns at full width (default 12). */
|
|
1613
|
+
cols?: number;
|
|
1614
|
+
/** Height of one grid row in px (default 96). */
|
|
1615
|
+
rowHeight?: number;
|
|
1616
|
+
/** Gap between cells in px (default 14). */
|
|
1617
|
+
gap?: number;
|
|
1618
|
+
/**
|
|
1619
|
+
* Responsive column counts. When the dashboard is narrower than a breakpoint's
|
|
1620
|
+
* `maxWidth`, the grid switches to that breakpoint's `cols` and tiles reflow
|
|
1621
|
+
* (DataZen-style). The smallest matching breakpoint wins. Defaults to
|
|
1622
|
+
* `[{ maxWidth: 600, cols: 1 }, { maxWidth: 960, cols: 6 }]`.
|
|
1623
|
+
*/
|
|
1624
|
+
breakpoints?: {
|
|
1625
|
+
maxWidth: number;
|
|
1626
|
+
cols: number;
|
|
1627
|
+
}[];
|
|
1628
|
+
/**
|
|
1629
|
+
* Where unplaced slicers are laid out:
|
|
1630
|
+
* - 'top' (default): compact slicers (dropdown/search/range/dateRange) form a
|
|
1631
|
+
* navigator strip above the grid, like a BI filter bar.
|
|
1632
|
+
* - 'inline': every slicer is laid out in the grid like a chart.
|
|
1633
|
+
*/
|
|
1634
|
+
navigators?: 'top' | 'inline';
|
|
1635
|
+
/** Optional section bands; unlisted views render in an implicit trailing section. */
|
|
1636
|
+
sections?: DashboardSection[];
|
|
1637
|
+
/** Placement preset for unplaced views. Defaults to 'auto'. */
|
|
1638
|
+
preset?: 'auto' | 'kpi-first' | 'sidebar';
|
|
1639
|
+
/** Constrain and center the dashboard page. */
|
|
1640
|
+
maxWidth?: number;
|
|
1641
|
+
/** Spacing preset applied before explicit gap/rowHeight overrides. */
|
|
1642
|
+
density?: 'compact' | 'standard' | 'comfortable';
|
|
1643
|
+
/** Page padding in px. Defaults to a value derived from gap. */
|
|
1644
|
+
padding?: number;
|
|
1645
|
+
}
|
|
1646
|
+
interface DashboardSection {
|
|
1647
|
+
/** Optional stable id for the section band. */
|
|
1648
|
+
id?: string;
|
|
1649
|
+
/** Section title shown above the grid. */
|
|
1650
|
+
title?: string;
|
|
1651
|
+
/** Muted line shown under the section title. */
|
|
1652
|
+
subtitle?: string;
|
|
1653
|
+
/** View ids included in this section. */
|
|
1654
|
+
views: string[];
|
|
1655
|
+
/** Section-specific column count override. */
|
|
1656
|
+
cols?: number;
|
|
1657
|
+
/** Section-specific row height override. */
|
|
1658
|
+
rowHeight?: number;
|
|
1659
|
+
/** Header band background tint. */
|
|
1660
|
+
background?: string;
|
|
1661
|
+
/** Initial collapsed state; click the header to expand/collapse. */
|
|
1662
|
+
collapsed?: boolean;
|
|
1663
|
+
}
|
|
1664
|
+
interface DashboardSpec {
|
|
1665
|
+
type: 'dashboard';
|
|
1666
|
+
/** Shared dataset; views without their own `data` inherit this. */
|
|
1667
|
+
data?: Datum[];
|
|
1668
|
+
theme?: ThemeInput;
|
|
1669
|
+
title?: string | TitleConfig;
|
|
1670
|
+
/** Optional muted subtitle shown under the title in the dashboard header. */
|
|
1671
|
+
subtitle?: string;
|
|
1672
|
+
background?: string;
|
|
1673
|
+
dimensions?: Dimensions;
|
|
1674
|
+
/** Dashboard-level named selections (e.g. seeded initial values). */
|
|
1675
|
+
params?: SelectionParam[];
|
|
1676
|
+
layout?: DashboardLayout;
|
|
1677
|
+
/** The placed views. */
|
|
1678
|
+
views: DashboardView[];
|
|
1679
|
+
/**
|
|
1680
|
+
* Cross-interaction policy:
|
|
1681
|
+
* - 'auto' (default): slicers filter charts; chart clicks cross-highlight
|
|
1682
|
+
* charts sharing the selected field.
|
|
1683
|
+
* - 'none': views are laid out but not linked.
|
|
1684
|
+
* - an array of {@link InteractionLink}: explicit wiring (replaces auto).
|
|
1685
|
+
*/
|
|
1686
|
+
interactions?: 'auto' | 'none' | InteractionLink[];
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
/** Infer the channel type of a single value. */
|
|
1690
|
+
declare function inferValueType(value: unknown): FieldType | undefined;
|
|
1691
|
+
/**
|
|
1692
|
+
* Infer the type of a field across a dataset by sampling non-null values.
|
|
1693
|
+
* Falls back to 'nominal' when ambiguous.
|
|
1694
|
+
*/
|
|
1695
|
+
declare function inferFieldType(data: Datum[], field: string, sample?: number): FieldType;
|
|
1696
|
+
/** Infer types for every field present in the first rows of the dataset. */
|
|
1697
|
+
declare function inferFieldTypes(data: Datum[]): Record<string, FieldType>;
|
|
1698
|
+
|
|
1699
|
+
interface ValidationError {
|
|
1700
|
+
/** JSON-path-ish location, e.g. "encoding.x.field". */
|
|
1701
|
+
path: string;
|
|
1702
|
+
message: string;
|
|
1703
|
+
}
|
|
1704
|
+
interface ValidationResult {
|
|
1705
|
+
valid: boolean;
|
|
1706
|
+
errors: ValidationError[];
|
|
1707
|
+
warnings: ValidationError[];
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Validate a chart spec, returning friendly, path-pointed errors and warnings.
|
|
1711
|
+
* Designed so an agent can read the messages and fix its spec without guessing.
|
|
1712
|
+
*/
|
|
1713
|
+
declare function validateSpec(spec: unknown): ValidationResult;
|
|
1714
|
+
/**
|
|
1715
|
+
* Validate a {@link DashboardSpec}: view-id uniqueness, grid placement, each
|
|
1716
|
+
* view's nested spec (recursively, inheriting the dashboard's data so a view
|
|
1717
|
+
* doesn't error on "missing data"), the cross-interaction surface, and explicit
|
|
1718
|
+
* link references. Messages are path-pointed (e.g. `views[2].spec.encoding.x`).
|
|
1719
|
+
*/
|
|
1720
|
+
declare function validateDashboard(spec: Record<string, unknown>): ValidationResult;
|
|
1721
|
+
declare function assertValidSpec(spec: unknown): ChartSpec;
|
|
1722
|
+
|
|
1723
|
+
/**
|
|
1724
|
+
* Resolve a spec's `sketch` option into concrete, fully-defaulted knobs for the
|
|
1725
|
+
* rough engine. Returns `null` when sketching is off, which lets every chart
|
|
1726
|
+
* keep its pixel-identical clean path with a single early check.
|
|
1727
|
+
*
|
|
1728
|
+
* The seed is derived deterministically from the spec's identity when the author
|
|
1729
|
+
* doesn't pass one, so the same chart always wobbles the same way (required for
|
|
1730
|
+
* the screenshot harness).
|
|
1731
|
+
*/
|
|
1732
|
+
|
|
1733
|
+
/** Fully-resolved sketch settings handed to charts / the rough pen. */
|
|
1734
|
+
interface ResolvedSketch extends RoughStyle {
|
|
1735
|
+
/** Whether text should use the hand-drawn font. */
|
|
1736
|
+
font: boolean;
|
|
1737
|
+
}
|
|
1738
|
+
/**
|
|
1739
|
+
* Map `spec.sketch` to a `ResolvedSketch`, or `null` when disabled.
|
|
1740
|
+
* `sketch: true` uses all defaults; an object overrides individual knobs.
|
|
1741
|
+
*/
|
|
1742
|
+
declare function resolveSketch(spec: ChartSpec): ResolvedSketch | null;
|
|
1743
|
+
|
|
1744
|
+
/**
|
|
1745
|
+
* Number formatting — a pragmatic subset of the d3-format mini-language, enough
|
|
1746
|
+
* for agent-authored specs without a runtime dependency.
|
|
1747
|
+
*
|
|
1748
|
+
* Grammar: [$][,][.precision][type]
|
|
1749
|
+
* $ prefix a currency symbol
|
|
1750
|
+
* , group thousands
|
|
1751
|
+
* .N precision (digits after the decimal, or significant digits)
|
|
1752
|
+
* type:
|
|
1753
|
+
* f fixed-point (.2f → 3.14)
|
|
1754
|
+
* % percent (×100) (.0% → 42%)
|
|
1755
|
+
* s SI suffix (.1s → 1.2k, 3.4M)
|
|
1756
|
+
* d integer (rounded) (,d → 1,234)
|
|
1757
|
+
* e exponential (.2e → 1.23e+4)
|
|
1758
|
+
* g significant digits (.3g → 12300)
|
|
1759
|
+
* (none) → smart default: trims trailing zeros, optional grouping
|
|
1760
|
+
*/
|
|
1761
|
+
interface NumberFormatSpec {
|
|
1762
|
+
currency?: string;
|
|
1763
|
+
group?: boolean;
|
|
1764
|
+
precision?: number;
|
|
1765
|
+
type?: 'f' | '%' | 's' | 'd' | 'e' | 'g' | '';
|
|
1766
|
+
}
|
|
1767
|
+
declare function parseNumberFormat(spec: string): NumberFormatSpec;
|
|
1768
|
+
/** Format a number according to a parsed or string spec. */
|
|
1769
|
+
declare function formatNumber(value: number, spec?: string | NumberFormatSpec): string;
|
|
1770
|
+
|
|
1771
|
+
/**
|
|
1772
|
+
* Date formatting — strftime-style tokens, plus a "smart" formatter that picks
|
|
1773
|
+
* a sensible representation from the time span being shown (used by time axes).
|
|
1774
|
+
*/
|
|
1775
|
+
/** Format a date with a strftime-like pattern, e.g. `%b %Y` → "Jan 2024". */
|
|
1776
|
+
declare function formatDate(value: Date | number, pattern: string): string;
|
|
1777
|
+
/**
|
|
1778
|
+
* Smart, span-aware label for a tick on a time axis. Picks granularity from the
|
|
1779
|
+
* distance between adjacent ticks so labels stay compact and legible.
|
|
1780
|
+
*/
|
|
1781
|
+
declare function smartDate(value: Date | number, stepMs: number): string;
|
|
1782
|
+
/** A standalone, context-free pretty date used by tooltips. */
|
|
1783
|
+
declare function prettyDate(value: Date | number): string;
|
|
1784
|
+
|
|
1785
|
+
/**
|
|
1786
|
+
* Format any field value for display. Routes by value type and an optional
|
|
1787
|
+
* format hint:
|
|
1788
|
+
* - a hint containing `%` → strftime date pattern
|
|
1789
|
+
* - otherwise → number format mini-language (when the value is numeric)
|
|
1790
|
+
* - Dates without a hint use a friendly default; everything else stringifies.
|
|
1791
|
+
*
|
|
1792
|
+
* Because JSON has no Date type, agent-authored specs pass dates as ISO-ish
|
|
1793
|
+
* strings. When a `%` (date) hint is supplied for such a string we parse it and
|
|
1794
|
+
* format as a date, so e.g. a table column `{ format: '%b %e, %Y' }` over
|
|
1795
|
+
* `"2024-05-02"` renders `May 2, 2024` rather than the raw string.
|
|
1796
|
+
*/
|
|
1797
|
+
declare function formatValue(value: unknown, hint?: string): string;
|
|
1798
|
+
|
|
1799
|
+
/**
|
|
1800
|
+
* Layout engine.
|
|
1801
|
+
*
|
|
1802
|
+
* Given the surface size, theme typography, and what the chart needs to show
|
|
1803
|
+
* (title, legend, axis tick labels), `computeFrame` reserves space for each
|
|
1804
|
+
* region and returns the plot rectangle where marks are drawn. It is pure (text
|
|
1805
|
+
* widths come from an injected measurer) so it unit-tests in node.
|
|
1806
|
+
*/
|
|
1807
|
+
|
|
1808
|
+
interface LegendItem {
|
|
1809
|
+
label: string;
|
|
1810
|
+
color: string;
|
|
1811
|
+
/** Marker style; defaults to a filled swatch. */
|
|
1812
|
+
symbol?: 'square' | 'line' | 'circle';
|
|
1813
|
+
}
|
|
1814
|
+
interface PositionedLegendItem extends LegendItem {
|
|
1815
|
+
x: number;
|
|
1816
|
+
y: number;
|
|
1817
|
+
/** Total item width (swatch + gap + label). */
|
|
1818
|
+
width: number;
|
|
1819
|
+
}
|
|
1820
|
+
interface TitleInput {
|
|
1821
|
+
text?: string;
|
|
1822
|
+
subtitle?: string;
|
|
1823
|
+
align?: 'left' | 'center' | 'right';
|
|
1824
|
+
}
|
|
1825
|
+
interface AxisInput {
|
|
1826
|
+
show: boolean;
|
|
1827
|
+
/** Formatted tick labels used to measure the gutter. */
|
|
1828
|
+
labels: string[];
|
|
1829
|
+
title?: string;
|
|
1830
|
+
/**
|
|
1831
|
+
* True when the first/last ticks sit exactly on the plot's horizontal edges
|
|
1832
|
+
* (linear/time scales), so half of each edge label overflows the plot and the
|
|
1833
|
+
* layout must reserve room for it. Band scales inset their labels, so this is
|
|
1834
|
+
* false/omitted for them.
|
|
1835
|
+
*/
|
|
1836
|
+
edgeAnchored?: boolean;
|
|
1837
|
+
}
|
|
1838
|
+
interface FrameInput {
|
|
1839
|
+
width: number;
|
|
1840
|
+
height: number;
|
|
1841
|
+
padding: Insets;
|
|
1842
|
+
font: ThemeFont;
|
|
1843
|
+
title?: TitleInput;
|
|
1844
|
+
legend?: {
|
|
1845
|
+
items: LegendItem[];
|
|
1846
|
+
position: LegendPosition;
|
|
1847
|
+
};
|
|
1848
|
+
/** Cartesian charts pass both axes; non-cartesian omit them. */
|
|
1849
|
+
xAxis?: AxisInput;
|
|
1850
|
+
yAxis?: AxisInput;
|
|
1851
|
+
}
|
|
1852
|
+
interface Frame {
|
|
1853
|
+
width: number;
|
|
1854
|
+
height: number;
|
|
1855
|
+
plot: Rect;
|
|
1856
|
+
titleRect?: Rect;
|
|
1857
|
+
subtitleRect?: Rect;
|
|
1858
|
+
legendRect?: Rect;
|
|
1859
|
+
legendItems?: PositionedLegendItem[];
|
|
1860
|
+
legendPosition?: LegendPosition;
|
|
1861
|
+
}
|
|
1862
|
+
declare const TICK_SIZE = 6;
|
|
1863
|
+
/** Reserve space for every region and return the plot rect + positioned chrome. */
|
|
1864
|
+
declare function computeFrame(input: FrameInput): Frame;
|
|
1865
|
+
|
|
1866
|
+
/**
|
|
1867
|
+
* Interaction model contracts.
|
|
1868
|
+
*
|
|
1869
|
+
* A chart (cartesian or custom) can expose an `InteractionModel` describing how
|
|
1870
|
+
* to hit-test the cursor and what to show. The shared `InteractionController`
|
|
1871
|
+
* owns the DOM listeners, the rAF throttle, the interaction-canvas highlight,
|
|
1872
|
+
* and the HTML tooltip — so individual charts only describe *what* to surface,
|
|
1873
|
+
* never *how* to wire events.
|
|
1874
|
+
*/
|
|
1875
|
+
|
|
1876
|
+
/** One line in a tooltip card. */
|
|
1877
|
+
interface TooltipRow {
|
|
1878
|
+
/** CSS color for the leading swatch chip. Omit for no chip. */
|
|
1879
|
+
swatch?: string;
|
|
1880
|
+
/** Left-aligned label (e.g. a series name). */
|
|
1881
|
+
label: string;
|
|
1882
|
+
/** Right-aligned, pre-formatted value. */
|
|
1883
|
+
value: string;
|
|
1884
|
+
/** Render muted (secondary measure / metadata). */
|
|
1885
|
+
muted?: boolean;
|
|
1886
|
+
/** Emphasise (e.g. the focused series under the cursor). */
|
|
1887
|
+
strong?: boolean;
|
|
1888
|
+
}
|
|
1889
|
+
/** Structured tooltip content; the controller renders it as a flat card. */
|
|
1890
|
+
interface TooltipContent {
|
|
1891
|
+
/** Bold header line (typically the shared x value or the category). */
|
|
1892
|
+
title?: string;
|
|
1893
|
+
rows: TooltipRow[];
|
|
1894
|
+
}
|
|
1895
|
+
/** The resolved hover under the cursor. */
|
|
1896
|
+
interface Hover {
|
|
1897
|
+
/**
|
|
1898
|
+
* Stable identity for the focused datum. While this is unchanged between
|
|
1899
|
+
* pointer moves the controller skips re-rendering — keeping hover cheap.
|
|
1900
|
+
*/
|
|
1901
|
+
key: string;
|
|
1902
|
+
/** Tooltip anchor in CSS px (where the crosshair/marker focuses). */
|
|
1903
|
+
anchorX: number;
|
|
1904
|
+
anchorY: number;
|
|
1905
|
+
content: TooltipContent;
|
|
1906
|
+
/**
|
|
1907
|
+
* Draw the highlight (crosshair, focus markers, cell outline…) onto the
|
|
1908
|
+
* already-cleared interaction context. Coordinates are CSS px.
|
|
1909
|
+
*/
|
|
1910
|
+
draw?(ctx: CanvasRenderingContext2D): void;
|
|
1911
|
+
}
|
|
1912
|
+
/** A chart's hit-testable description for the current frame. */
|
|
1913
|
+
interface InteractionModel {
|
|
1914
|
+
/** Hit region in CSS px. Moves outside it clear the hover. */
|
|
1915
|
+
region: Rect;
|
|
1916
|
+
/** Resolve the datum under a CSS-px point, or `null` for none. */
|
|
1917
|
+
hitTest(px: number, py: number): Hover | null;
|
|
1918
|
+
/**
|
|
1919
|
+
* Resolve the *selection* under a CSS-px point (for click/tap), or `null` when
|
|
1920
|
+
* the point hits no mark (which clears the selection). Optional: charts without
|
|
1921
|
+
* a `pick` are hover-only.
|
|
1922
|
+
*/
|
|
1923
|
+
pick?(px: number, py: number): SelectionValue | null;
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* Resolved emphasis for the current frame: which rows are "in" the active
|
|
1927
|
+
* selection, and how strongly to fade the rest. Renderers dim non-matching marks
|
|
1928
|
+
* to `dim` (a 0..1 alpha multiplier) and keep matching marks at full strength.
|
|
1929
|
+
*/
|
|
1930
|
+
interface Emphasis {
|
|
1931
|
+
/** True when a row is part of the active selection (drawn at full strength). */
|
|
1932
|
+
match(row: Datum): boolean;
|
|
1933
|
+
/** Alpha multiplier applied to non-matching marks (e.g. 0.22). */
|
|
1934
|
+
dim: number;
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
/**
|
|
1938
|
+
* Cartesian model builder.
|
|
1939
|
+
*
|
|
1940
|
+
* Turns a line / area / bar / scatter spec + resolved theme + pixel size into a
|
|
1941
|
+
* fully-resolved drawing model: x/y scales, the plot rect, series split, color
|
|
1942
|
+
* mapping, and positioned axis ticks. Charts consume this model and only worry
|
|
1943
|
+
* about drawing marks. Heatmap, pie, kpi, table, and matrix are non-cartesian
|
|
1944
|
+
* and build their own models.
|
|
1945
|
+
*/
|
|
1946
|
+
|
|
1947
|
+
type CartesianChartSpec = LineSpec | AreaSpec | BarSpec | ScatterSpec | BoxSpec;
|
|
1948
|
+
type XKind = 'linear' | 'time' | 'band' | 'point';
|
|
1949
|
+
interface Tick {
|
|
1950
|
+
value: number | string;
|
|
1951
|
+
/** Pixel position along the axis. */
|
|
1952
|
+
pos: number;
|
|
1953
|
+
label: string;
|
|
1954
|
+
}
|
|
1955
|
+
interface XModel {
|
|
1956
|
+
kind: XKind;
|
|
1957
|
+
field: string;
|
|
1958
|
+
type: FieldType;
|
|
1959
|
+
/** Band/point category domain. */
|
|
1960
|
+
categories?: string[];
|
|
1961
|
+
band?: BandScale;
|
|
1962
|
+
point?: PointScale;
|
|
1963
|
+
/** Continuous (linear/time) scale; domain in numbers or epoch ms. */
|
|
1964
|
+
continuous?: ContinuousScale;
|
|
1965
|
+
/** Category width for band scales (0 otherwise). */
|
|
1966
|
+
bandwidth: number;
|
|
1967
|
+
/** Project a data value to its center pixel, or undefined if unmappable. */
|
|
1968
|
+
pixel(value: unknown): number | undefined;
|
|
1969
|
+
}
|
|
1970
|
+
interface YModel {
|
|
1971
|
+
field?: string;
|
|
1972
|
+
scale: ContinuousScale;
|
|
1973
|
+
/** Pixel of the zero baseline, clamped into the plot. */
|
|
1974
|
+
baseline: number;
|
|
1975
|
+
pixel(value: unknown): number;
|
|
1976
|
+
}
|
|
1977
|
+
interface ResolvedSeries {
|
|
1978
|
+
key: string;
|
|
1979
|
+
label: string;
|
|
1980
|
+
color: string;
|
|
1981
|
+
value: unknown;
|
|
1982
|
+
rows: Datum[];
|
|
1983
|
+
}
|
|
1984
|
+
interface CartesianModel {
|
|
1985
|
+
spec: CartesianChartSpec;
|
|
1986
|
+
tokens: ThemeTokens;
|
|
1987
|
+
frame: Frame;
|
|
1988
|
+
plot: Rect;
|
|
1989
|
+
x: XModel;
|
|
1990
|
+
y: YModel;
|
|
1991
|
+
series: ResolvedSeries[];
|
|
1992
|
+
seriesField?: string;
|
|
1993
|
+
stacked: boolean;
|
|
1994
|
+
xTicks: Tick[];
|
|
1995
|
+
yTicks: Tick[];
|
|
1996
|
+
colorOf(key: string): string;
|
|
1997
|
+
/** Hand-drawn render settings, or null for the default clean rendering. */
|
|
1998
|
+
sketch: ResolvedSketch | null;
|
|
1999
|
+
/**
|
|
2000
|
+
* Active highlight for this frame (matching rows full strength, the rest dimmed),
|
|
2001
|
+
* or null when nothing is selected. Set externally by the runtime from the spec's
|
|
2002
|
+
* `highlight` param + the shared store; `buildCartesianModel` leaves it null.
|
|
2003
|
+
*/
|
|
2004
|
+
emphasis?: Emphasis | null;
|
|
2005
|
+
}
|
|
2006
|
+
declare function resolveCurve(curve?: CurveType): Curve;
|
|
2007
|
+
interface BuildOptions {
|
|
2008
|
+
width: number;
|
|
2009
|
+
height: number;
|
|
2010
|
+
}
|
|
2011
|
+
declare function buildCartesianModel(spec: CartesianChartSpec, tokens: ThemeTokens, opts: BuildOptions): CartesianModel;
|
|
2012
|
+
|
|
2013
|
+
/**
|
|
2014
|
+
* Axis, gridline, legend, and title rendering for cartesian charts.
|
|
2015
|
+
*
|
|
2016
|
+
* Marks (gridlines, tick marks, axis baselines) are drawn on the canvas; text
|
|
2017
|
+
* (tick labels, axis titles, chart title, legend) lives in the HTML overlay for
|
|
2018
|
+
* crisp, accessible, selectable typography. Geometry comes from the already
|
|
2019
|
+
* computed `CartesianModel` / `Frame`, so this module is pure presentation.
|
|
2020
|
+
*/
|
|
2021
|
+
|
|
2022
|
+
/** Draw gridlines + axis baselines + tick marks on the marks canvas (behind marks). */
|
|
2023
|
+
declare function drawAxesUnderlay(surface: Surface, model: CartesianModel): void;
|
|
2024
|
+
/** Draw all overlay text: tick labels, axis titles, chart title, legend. */
|
|
2025
|
+
declare function drawOverlay(surface: Surface, model: CartesianModel): void;
|
|
2026
|
+
|
|
2027
|
+
/**
|
|
2028
|
+
* SelectionStore — the dependency-free bus that links interactive visuals.
|
|
2029
|
+
*
|
|
2030
|
+
* It holds the current {@link SelectionValue} for every named param and notifies
|
|
2031
|
+
* subscribers when one changes. Visuals **publish** to params they define (click,
|
|
2032
|
+
* brush, slicer change) and **subscribe** to params they consume (re-resolve
|
|
2033
|
+
* highlight/filter, redraw). A single store shared across `render()` calls links
|
|
2034
|
+
* independently-mounted charts; a `DashboardSpec` owns one internally.
|
|
2035
|
+
*
|
|
2036
|
+
* The store is deliberately "dumb": toggle/accumulate logic lives in the visual
|
|
2037
|
+
* that computes the next value. Change detection uses a structural compare so a
|
|
2038
|
+
* no-op `set` doesn't trigger redundant redraws.
|
|
2039
|
+
*/
|
|
2040
|
+
|
|
2041
|
+
/** Notified after a param's value changes. */
|
|
2042
|
+
type SelectionListener = (name: string, value: SelectionValue | null) => void;
|
|
2043
|
+
interface SelectionStore {
|
|
2044
|
+
/** Current value for `name`, or `null` if unset/empty. */
|
|
2045
|
+
get(name: string): SelectionValue | null;
|
|
2046
|
+
/** Replace `name`'s value; no-ops (and skips notifying) if unchanged. */
|
|
2047
|
+
set(name: string, value: SelectionValue | null): void;
|
|
2048
|
+
/** Clear one param, or all params when `name` is omitted. */
|
|
2049
|
+
clear(name?: string): void;
|
|
2050
|
+
/** A snapshot of every param's current value. */
|
|
2051
|
+
all(): Record<string, SelectionValue | null>;
|
|
2052
|
+
/** Subscribe to changes; returns an unsubscribe function. */
|
|
2053
|
+
subscribe(listener: SelectionListener): () => void;
|
|
2054
|
+
}
|
|
2055
|
+
declare function createSelectionStore(initial?: Record<string, SelectionValue | null>): SelectionStore;
|
|
2056
|
+
|
|
2057
|
+
/**
|
|
2058
|
+
* Chart registry.
|
|
2059
|
+
*
|
|
2060
|
+
* Cartesian charts (line/area/bar/scatter) consume a prebuilt `CartesianModel`
|
|
2061
|
+
* and only draw marks. Custom charts (heatmap/pie/kpi/table/matrix) own their
|
|
2062
|
+
* full layout and draw directly onto the surface. The runtime dispatches here.
|
|
2063
|
+
*/
|
|
2064
|
+
|
|
2065
|
+
type CartesianRenderer = (surface: Surface, model: CartesianModel) => void;
|
|
2066
|
+
type CartesianInteractionBuilder = (model: CartesianModel) => InteractionModel | void;
|
|
2067
|
+
/**
|
|
2068
|
+
* Optional per-frame interactivity context handed to custom renderers. Lets a
|
|
2069
|
+
* chart dim non-selected marks (`emphasis`), publish its own selection to the
|
|
2070
|
+
* shared bus (`store` + `param`/`def`), and request a redraw when its internal
|
|
2071
|
+
* state changes (slicers). All fields are optional — a renderer that ignores the
|
|
2072
|
+
* context behaves exactly as before.
|
|
2073
|
+
*/
|
|
2074
|
+
interface RenderContext {
|
|
2075
|
+
/** Active highlight for this frame, or null when nothing is selected. */
|
|
2076
|
+
emphasis?: Emphasis | null;
|
|
2077
|
+
/** Shared selection bus when this chart is linked to others. */
|
|
2078
|
+
store?: SelectionStore;
|
|
2079
|
+
/** The param this chart publishes selections to (usually `spec.params[0]`). */
|
|
2080
|
+
param?: string;
|
|
2081
|
+
/** The selection definition backing `param`. */
|
|
2082
|
+
def?: SelectionDef;
|
|
2083
|
+
/**
|
|
2084
|
+
* The visual's *unfiltered* source rows. Slicers derive their option lists from
|
|
2085
|
+
* this (so a slicer never hides its own choices by filtering itself), while the
|
|
2086
|
+
* `spec.data` handed to the renderer may already be cross-filtered.
|
|
2087
|
+
*/
|
|
2088
|
+
sourceData?: Datum[];
|
|
2089
|
+
/**
|
|
2090
|
+
* The host already provides card chrome (e.g. a dashboard cell), so renderers
|
|
2091
|
+
* that normally draw their own card (KPI) should render flat to avoid doubling
|
|
2092
|
+
* the border/background.
|
|
2093
|
+
*/
|
|
2094
|
+
framed?: boolean;
|
|
2095
|
+
/** Ask the runtime to redraw the marks layer (used by DOM slicers). */
|
|
2096
|
+
requestRedraw?: () => void;
|
|
2097
|
+
}
|
|
2098
|
+
type CustomRenderer = (surface: Surface, spec: ChartSpec, tokens: ThemeTokens, size: Size, context?: RenderContext) => InteractionModel | void;
|
|
2099
|
+
declare const CARTESIAN_TYPES: ReadonlySet<ChartType>;
|
|
2100
|
+
declare const cartesianRenderers: Partial<Record<ChartType, CartesianRenderer>>;
|
|
2101
|
+
/**
|
|
2102
|
+
* Cartesian charts whose hover semantics differ from the generic nearest-point
|
|
2103
|
+
* model (e.g. box plots surface quartile stats). When present, the runtime uses
|
|
2104
|
+
* this builder instead of `buildCartesianInteraction`.
|
|
2105
|
+
*/
|
|
2106
|
+
declare const cartesianInteractionBuilders: Partial<Record<ChartType, CartesianInteractionBuilder>>;
|
|
2107
|
+
declare const customRenderers: Partial<Record<ChartType, CustomRenderer>>;
|
|
2108
|
+
|
|
2109
|
+
/**
|
|
2110
|
+
* InteractionController — the single owner of pointer events for a Surface.
|
|
2111
|
+
*
|
|
2112
|
+
* Created once per chart instance; handed a fresh `InteractionModel` on every
|
|
2113
|
+
* redraw. It throttles pointer moves to one rAF, hit-tests against the current
|
|
2114
|
+
* model, paints the highlight onto the interaction canvas, and drives the HTML
|
|
2115
|
+
* tooltip. Hover never touches the marks layer.
|
|
2116
|
+
*/
|
|
2117
|
+
|
|
2118
|
+
/** How the controller publishes a click/tap selection from the current model. */
|
|
2119
|
+
interface ControllerSelect {
|
|
2120
|
+
/** Called with the picked value (or `null` when empty space is clicked). */
|
|
2121
|
+
onPick(value: SelectionValue | null): void;
|
|
2122
|
+
}
|
|
2123
|
+
declare class InteractionController {
|
|
2124
|
+
private readonly surface;
|
|
2125
|
+
private model;
|
|
2126
|
+
private tokens;
|
|
2127
|
+
private readonly tooltip;
|
|
2128
|
+
private raf;
|
|
2129
|
+
private pending;
|
|
2130
|
+
private activeKey;
|
|
2131
|
+
private selectCfg;
|
|
2132
|
+
private pickable;
|
|
2133
|
+
constructor(surface: Surface, tokens: ThemeTokens);
|
|
2134
|
+
/** Install the model for the current frame (or `null` to disable hover). */
|
|
2135
|
+
setModel(model: InteractionModel | null, tokens: ThemeTokens): void;
|
|
2136
|
+
/** Wire click/tap selection for this chart (or `null` to disable it). */
|
|
2137
|
+
setSelect(cfg: ControllerSelect | null): void;
|
|
2138
|
+
private readonly onMove;
|
|
2139
|
+
private readonly onLeave;
|
|
2140
|
+
private readonly onClick;
|
|
2141
|
+
private readonly flush;
|
|
2142
|
+
private render;
|
|
2143
|
+
private paintHighlight;
|
|
2144
|
+
private clearHover;
|
|
2145
|
+
destroy(): void;
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
/**
|
|
2149
|
+
* The hover tooltip — a flat, themed HTML card the controller positions next to
|
|
2150
|
+
* the cursor. It lives as a direct child of the Surface root (a sibling of the
|
|
2151
|
+
* overlay layer) so the per-frame `surface.clear()` never wipes it; the
|
|
2152
|
+
* controller owns its full lifecycle.
|
|
2153
|
+
*/
|
|
2154
|
+
|
|
2155
|
+
declare class Tooltip {
|
|
2156
|
+
private readonly root;
|
|
2157
|
+
private readonly el;
|
|
2158
|
+
private tokens;
|
|
2159
|
+
private visible;
|
|
2160
|
+
constructor(root: HTMLElement, tokens: ThemeTokens);
|
|
2161
|
+
setTokens(tokens: ThemeTokens): void;
|
|
2162
|
+
private applyShell;
|
|
2163
|
+
/** Replace the card contents from structured data. */
|
|
2164
|
+
setContent(content: TooltipContent): void;
|
|
2165
|
+
/**
|
|
2166
|
+
* Position the card near (anchorX, anchorY) in CSS px, flipping away from the
|
|
2167
|
+
* surface edges so it never clips. `surfaceW/H` bound the placement.
|
|
2168
|
+
*/
|
|
2169
|
+
place(anchorX: number, anchorY: number, surfaceW: number, surfaceH: number): void;
|
|
2170
|
+
hide(): void;
|
|
2171
|
+
destroy(): void;
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
/**
|
|
2175
|
+
* Cartesian hit-testing.
|
|
2176
|
+
*
|
|
2177
|
+
* Builds an `InteractionModel` from a resolved `CartesianModel`:
|
|
2178
|
+
* - line / area / bar / point-x → shared-x "index" hover (crosshair + one
|
|
2179
|
+
* tooltip row per series at the focused x).
|
|
2180
|
+
* - scatter (quantitative x) → nearest-point hover (focus ring).
|
|
2181
|
+
*
|
|
2182
|
+
* All highlight drawing targets the interaction canvas in CSS px; nothing here
|
|
2183
|
+
* mutates the marks layer, so hover never triggers a full redraw.
|
|
2184
|
+
*/
|
|
2185
|
+
|
|
2186
|
+
declare function tooltipEnabled(spec: CartesianModel['spec']): boolean;
|
|
2187
|
+
declare function buildCartesianInteraction(model: CartesianModel): InteractionModel | null;
|
|
2188
|
+
|
|
2189
|
+
/**
|
|
2190
|
+
* Predicate engine — pure, dependency-free matching of data rows against a
|
|
2191
|
+
* resolved {@link SelectionValue}.
|
|
2192
|
+
*
|
|
2193
|
+
* Used by both consumers of a selection: **filter** (keep matching rows) and
|
|
2194
|
+
* **highlight** (test each mark to decide emphasis vs. dim). Kept side-effect
|
|
2195
|
+
* free so it unit-tests in node and runs cheaply per mark.
|
|
2196
|
+
*/
|
|
2197
|
+
|
|
2198
|
+
/** A selection with no constraint (null, empty set/point/text) matches all. */
|
|
2199
|
+
declare function isEmptyValue(value: SelectionValue | null | undefined): boolean;
|
|
2200
|
+
/**
|
|
2201
|
+
* Compile a row → boolean tester for a selection value, hoisting accessors and
|
|
2202
|
+
* lookup sets out of the per-row path so it's cheap to call across many marks.
|
|
2203
|
+
* An empty/null constraint compiles to a constant `true`.
|
|
2204
|
+
*/
|
|
2205
|
+
declare function makeMatcher(value: SelectionValue | null | undefined): (row: Datum) => boolean;
|
|
2206
|
+
/** Does `row` satisfy `value`? An empty/null constraint always matches. */
|
|
2207
|
+
declare function matchesValue(row: Datum, value: SelectionValue | null | undefined): boolean;
|
|
2208
|
+
/** Keep only the rows matching *every* provided selection (logical AND). */
|
|
2209
|
+
declare function filterRows(rows: readonly Datum[], values: ReadonlyArray<SelectionValue | null | undefined>): Datum[];
|
|
2210
|
+
/** Convert a literal `filter` predicate into a {@link SelectionValue}. */
|
|
2211
|
+
declare function literalToValue(pred: LiteralPredicate): SelectionValue;
|
|
2212
|
+
/** True when a filter clause is a named-param reference (vs. a literal). */
|
|
2213
|
+
declare function isParamClause(clause: FilterClause): clause is {
|
|
2214
|
+
param: string;
|
|
2215
|
+
};
|
|
2216
|
+
|
|
2217
|
+
/**
|
|
2218
|
+
* Selection wiring — turn a single "pick" into a published selection value, and
|
|
2219
|
+
* resolve a spec's `highlight`/`filter` against the store.
|
|
2220
|
+
*
|
|
2221
|
+
* The interaction models describe *what* a click picks (a {@link SelectionValue}
|
|
2222
|
+
* for one mark); this module applies the param's toggle/accumulate semantics and
|
|
2223
|
+
* publishes the result. It also resolves the consuming side: the emphasis a chart
|
|
2224
|
+
* draws (from `highlight`) and the row predicates it filters by (from `filter`).
|
|
2225
|
+
*/
|
|
2226
|
+
|
|
2227
|
+
/** Default dim alpha applied to non-matching marks during highlight. */
|
|
2228
|
+
declare const DIM_ALPHA = 0.22;
|
|
2229
|
+
/** What a selectable visual needs to publish picks to the store. */
|
|
2230
|
+
interface SelectConfig {
|
|
2231
|
+
store: SelectionStore;
|
|
2232
|
+
param: string;
|
|
2233
|
+
def: SelectionDef;
|
|
2234
|
+
}
|
|
2235
|
+
/**
|
|
2236
|
+
* Publish a pick to the store, applying the param's semantics:
|
|
2237
|
+
* - a `null` pick (clicked empty space) clears the selection;
|
|
2238
|
+
* - `point` params with `toggle` (the default) accumulate / deselect;
|
|
2239
|
+
* - everything else replaces the value (single-select, brush, slicer).
|
|
2240
|
+
*/
|
|
2241
|
+
declare function applyPick(cfg: SelectConfig, pick: SelectionValue | null): void;
|
|
2242
|
+
/** Resolve a `highlight` reference (one param or a union of several) into the
|
|
2243
|
+
* emphasis to draw this frame. With multiple params a row is emphasized if it
|
|
2244
|
+
* matches *any* active selection; emphasis is null when none are active. */
|
|
2245
|
+
declare function resolveEmphasis(highlight: HighlightConfig | HighlightConfig[] | undefined, store: SelectionStore | undefined, dim?: number): Emphasis | null;
|
|
2246
|
+
/** Resolve a `filter` list into concrete selection values (params + literals). */
|
|
2247
|
+
declare function resolveFilterValues(filter: FilterClause[] | undefined, store: SelectionStore | undefined): SelectionValue[];
|
|
2248
|
+
/** The set of param names a spec reacts to (for change-driven redraws). */
|
|
2249
|
+
declare function dependentParams(highlight: HighlightConfig | HighlightConfig[] | undefined, filter: FilterClause[] | undefined, ownParams?: readonly string[]): Set<string>;
|
|
2250
|
+
|
|
2251
|
+
/**
|
|
2252
|
+
* Chart runtime: the public `render(container, spec)` entry point.
|
|
2253
|
+
*
|
|
2254
|
+
* Mounts a hybrid canvas + HTML `Surface`, resolves the theme, dispatches to the
|
|
2255
|
+
* right chart renderer, and manages the lifecycle (update / resize / destroy)
|
|
2256
|
+
* plus a deterministic "ready" signal for screenshot harnesses.
|
|
2257
|
+
*/
|
|
2258
|
+
|
|
2259
|
+
/** A change to a named selection: the param name and its new value (or null). */
|
|
2260
|
+
type SelectionChangeListener = (name: string, value: SelectionValue | null) => void;
|
|
2261
|
+
/** Options for {@link render}. */
|
|
2262
|
+
interface RenderOptions {
|
|
2263
|
+
/**
|
|
2264
|
+
* A shared selection bus. Pass the same store to multiple `render()` calls to
|
|
2265
|
+
* link them (cross-highlight / cross-filter). Omit to give the chart its own
|
|
2266
|
+
* private store.
|
|
2267
|
+
*/
|
|
2268
|
+
store?: SelectionStore;
|
|
2269
|
+
/**
|
|
2270
|
+
* The chart is mounted inside a host that already provides card chrome (e.g. a
|
|
2271
|
+
* dashboard cell). Renderers that normally draw their own card (KPI) render
|
|
2272
|
+
* flat so the chrome isn't doubled. Optional; defaults to false.
|
|
2273
|
+
*/
|
|
2274
|
+
frame?: boolean;
|
|
2275
|
+
}
|
|
2276
|
+
interface ChartInstance {
|
|
2277
|
+
/** Re-render with a new spec (data or config changes). */
|
|
2278
|
+
update(spec: ChartSpec): void;
|
|
2279
|
+
/** Re-measure the container (or use explicit dims) and redraw. */
|
|
2280
|
+
resize(width?: number, height?: number): void;
|
|
2281
|
+
/** Tear down DOM, observers, and listeners. */
|
|
2282
|
+
destroy(): void;
|
|
2283
|
+
/** The currently rendered spec. */
|
|
2284
|
+
readonly spec: ChartSpec;
|
|
2285
|
+
/** The mounted surface (advanced/imperative use). */
|
|
2286
|
+
readonly surface: Surface;
|
|
2287
|
+
/** The selection bus this chart is bound to (advanced/linking use). */
|
|
2288
|
+
readonly store: SelectionStore;
|
|
2289
|
+
/** Read a param's current value, or a snapshot of all params when omitted. */
|
|
2290
|
+
getSelection(name?: string): SelectionValue | null | Record<string, SelectionValue | null>;
|
|
2291
|
+
/** Set a param's value (drives highlight/filter across linked visuals). */
|
|
2292
|
+
setSelection(name: string, value: SelectionValue | null): void;
|
|
2293
|
+
/** Clear one param, or every param when `name` is omitted. */
|
|
2294
|
+
clearSelection(name?: string): void;
|
|
2295
|
+
/** Subscribe to selection changes; returns an unsubscribe function. */
|
|
2296
|
+
on(event: 'selectionchange', listener: SelectionChangeListener): () => void;
|
|
2297
|
+
/** Remove a previously-registered selection listener. */
|
|
2298
|
+
off(event: 'selectionchange', listener: SelectionChangeListener): void;
|
|
2299
|
+
}
|
|
2300
|
+
declare global {
|
|
2301
|
+
var __GRAPHEIN_READY: number | undefined;
|
|
2302
|
+
var __GRAPHEIN_DISABLE_ANIM: boolean | undefined;
|
|
2303
|
+
}
|
|
2304
|
+
declare function render(target: HTMLElement | string, spec: ChartSpec, options?: RenderOptions): ChartInstance;
|
|
2305
|
+
|
|
2306
|
+
/**
|
|
2307
|
+
* Dashboard auto-wiring — pure spec rewriting that turns a set of views into
|
|
2308
|
+
* cross-interacting ones by injecting `params` (sources), `highlight`, and
|
|
2309
|
+
* `filter` (consumers) onto each view's spec. Kept side-effect free (no DOM, no
|
|
2310
|
+
* store) so it unit-tests in node and the runtime stays a thin shell.
|
|
2311
|
+
*
|
|
2312
|
+
* Rules (interactions: 'auto'):
|
|
2313
|
+
* - A **slicer** publishes a value on its field/param; every chart that *uses*
|
|
2314
|
+
* that field gets a `filter` clause for it (cross-filter).
|
|
2315
|
+
* - A **chart** click publishes a point selection on its key field(s); every
|
|
2316
|
+
* chart that uses *all* those fields (including itself) gets that param added
|
|
2317
|
+
* to its `highlight` union (cross-highlight). Matching on shared fields means
|
|
2318
|
+
* emphasis is always well-defined — a chart never dims itself to nothing.
|
|
2319
|
+
*/
|
|
2320
|
+
|
|
2321
|
+
declare function isSlicerType(type: ChartSpec['type']): boolean;
|
|
2322
|
+
/** Every data field a chart's encoding references (used to test "uses field"). */
|
|
2323
|
+
declare function specFields(spec: ChartSpec): Set<string>;
|
|
2324
|
+
/** The field(s) a click on this chart selects (mirror of each model's `pick`). */
|
|
2325
|
+
declare function keyFields(spec: ChartSpec): string[];
|
|
2326
|
+
/** The param a slicer publishes to (explicit `param`, else its field). */
|
|
2327
|
+
declare function slicerParamName(spec: BaseSlicerSpec): string;
|
|
2328
|
+
/**
|
|
2329
|
+
* Rewrite a dashboard's views, injecting the params/highlight/filter that
|
|
2330
|
+
* implement the requested cross-interaction. Returns new view objects with new
|
|
2331
|
+
* specs; inputs are not mutated.
|
|
2332
|
+
*/
|
|
2333
|
+
declare function wireViews(views: DashboardView[], interactions: 'auto' | 'none' | InteractionLink[] | undefined): DashboardView[];
|
|
2334
|
+
|
|
2335
|
+
/**
|
|
2336
|
+
* Dashboard runtime — `renderDashboard(container, spec)`.
|
|
2337
|
+
*
|
|
2338
|
+
* Lays views out on responsive CSS grids, shares one {@link SelectionStore}
|
|
2339
|
+
* across them, and auto-wires cross-interaction (slicers filter charts; chart
|
|
2340
|
+
* clicks cross-highlight charts that share the field). Each view is mounted with
|
|
2341
|
+
* the same `render()` used for standalone charts — the dashboard is mostly a
|
|
2342
|
+
* spec rewriter (see {@link wireViews}) plus layout and a shared bus, so all of
|
|
2343
|
+
* Phase 1's filter/highlight machinery is reused unchanged.
|
|
2344
|
+
*/
|
|
2345
|
+
|
|
2346
|
+
interface RenderDashboardOptions {
|
|
2347
|
+
/** Share a bus with charts/dashboards outside this one. */
|
|
2348
|
+
store?: SelectionStore;
|
|
2349
|
+
}
|
|
2350
|
+
interface DashboardInstance {
|
|
2351
|
+
/** Re-render with a new dashboard spec (keeps the shared store/selection). */
|
|
2352
|
+
update(spec: DashboardSpec): void;
|
|
2353
|
+
/** Re-measure every view. */
|
|
2354
|
+
resize(): void;
|
|
2355
|
+
/** Tear down all views, observers, and listeners. */
|
|
2356
|
+
destroy(): void;
|
|
2357
|
+
/** The currently rendered dashboard spec. */
|
|
2358
|
+
readonly spec: DashboardSpec;
|
|
2359
|
+
/** The shared selection bus driving cross-interaction. */
|
|
2360
|
+
readonly store: SelectionStore;
|
|
2361
|
+
/** The mounted chart instances, in view order. */
|
|
2362
|
+
readonly views: ChartInstance[];
|
|
2363
|
+
getSelection(name?: string): SelectionValue | null | Record<string, SelectionValue | null>;
|
|
2364
|
+
setSelection(name: string, value: SelectionValue | null): void;
|
|
2365
|
+
clearSelection(name?: string): void;
|
|
2366
|
+
on(event: 'selectionchange', listener: SelectionChangeListener): () => void;
|
|
2367
|
+
off(event: 'selectionchange', listener: SelectionChangeListener): void;
|
|
2368
|
+
}
|
|
2369
|
+
type ResolvedLayout = Required<Pick<DashboardLayout, 'cols' | 'rowHeight' | 'gap'>> & Pick<DashboardLayout, 'breakpoints' | 'navigators' | 'sections' | 'preset' | 'maxWidth' | 'density' | 'padding'>;
|
|
2370
|
+
type WiredView = DashboardView & {
|
|
2371
|
+
spec: ChartSpec;
|
|
2372
|
+
};
|
|
2373
|
+
type SectionPlan = {
|
|
2374
|
+
section?: DashboardSection;
|
|
2375
|
+
views: WiredView[];
|
|
2376
|
+
cols: number;
|
|
2377
|
+
rowHeight: number;
|
|
2378
|
+
collapsed: boolean;
|
|
2379
|
+
explicit: boolean;
|
|
2380
|
+
};
|
|
2381
|
+
declare function resolveDashboardLayout(layout?: DashboardLayout): ResolvedLayout;
|
|
2382
|
+
declare function resolveDashboardSections(views: WiredView[], layout: Pick<ResolvedLayout, 'cols' | 'rowHeight' | 'sections'>): SectionPlan[];
|
|
2383
|
+
declare function renderDashboard(target: HTMLElement | string, spec: DashboardSpec, options?: RenderDashboardOptions): DashboardInstance;
|
|
2384
|
+
|
|
2385
|
+
/**
|
|
2386
|
+
* graphein — public entry point.
|
|
2387
|
+
*
|
|
2388
|
+
* The barrel grows as modules land. Keep exports explicit and tree-shakeable.
|
|
2389
|
+
*/
|
|
2390
|
+
declare const VERSION = "0.1.0";
|
|
2391
|
+
|
|
2392
|
+
export { type AggOp, type AnimateOptions, type AnimationConfig, type AnimationHandle, type ArcOptions, type AreaOptions, type AreaPoint, type AreaSpec, type AxesConfig, type AxisConfig, type AxisInput, type BackingSize, type BandScale, type BandScaleOptions, type BarSpec, type BaseSlicerSpec, type BaseSpec, type BoxSpec, type BuildOptions, CARTESIAN_TYPES, CHART_TYPES, CanvasLayer, type CartesianChartSpec, type CartesianInteractionBuilder, type CartesianModel, type CartesianRenderer, type ChartInstance, type ChartSpec, type ChartSummary, type ChartType, type ChoroplethSpec, type ColorScale, type Comparable, type ConditionalFormat, type ContinuousScale, type ControllerSelect, type Curve, type CurveType, type CustomRenderer, DEFAULT_ENTRANCE_DURATION, DEFAULT_ROUGH_STYLE, DEFAULT_UPDATE_DURATION, DIM_ALPHA, type DashboardInstance, type DashboardLayout, type DashboardResponsiveSpan, type DashboardSection, type DashboardSpec, type DashboardView, type DateRangeSlicerSpec, type Datum, type DecimateOptions, type Dimensions, type DropdownSlicerSpec, type EasingFunction, type Emphasis, type Encoding, type EntranceContext, type Extent, type FieldDef, type FieldType, type FieldValue, type FillStyle, type FilterClause, type Frame, type FrameInput, type FunnelSpec, type GeoFeature, type GeoFeatureCollection, type GeoGeometry, type GeoMultiPolygon, type GeoPolygon, type GeoPosition, type GroupedMap, type HachureSegment, type HeatmapSpec, type HighlightConfig, type Hover, type IconRule, type Insets, InteractionController, type InteractionLink, type InteractionModel, type Interpolator, type KpiSpec, type LegendConfig, type LegendItem, type LegendPosition, type LineOptions, type LineSpec, type LinearScaleOptions, type ListSlicerSpec, type LiteralPredicate, type LogScaleOptions, type MapProjection, type MarkOptions, type MatrixSpec, type MatrixValueDef, type NumberFormatSpec, type OKLCH, type OKLab, type PathSink, type PieLabels, type PieSpec, type PivotCell, type PivotFlatRow, type PivotHeaderNode, type PivotOptions, type PivotResult, type PivotValueDef, type Point, type PointScale, type PointScaleOptions, type PointSelection, type PositionedLegendItem, type RGBA, type RangeSelection, type RangeSlicerSpec, type Rect, type RenderContext, type RenderDashboardOptions, type RenderOptions, type ResolvedEntrance, type ResolvedSeries, type ResolvedSketch, type Rng, type RoughContext, RoughPen, type RoughStyle, SKETCH_FONT_FAMILY, SKETCH_FONT_NAME, SLICER_TYPES, SR_ONLY_STYLE, type SankeySpec, type ScaleConfig, type ScaleType, type ScatterSpec, type SearchSlicerSpec, type SelectConfig, type SelectionChangeListener, type SelectionDef, type SelectionListener, type SelectionParam, type SelectionStore, type SelectionValue, type SetSelection, type Size, type SketchConfig, type SlicerSpec, type SlicerType, Surface, TICK_SIZE, type TableColumn, type TableSpec, type TextMetricsLite, type TextSelection, type ThemeColors, type ThemeFont, type ThemeInput, type ThemeTokens, type Tick, type TimeScaleOptions, type TitleConfig, type TitleInput, Tooltip, type TooltipConfig, type TooltipContent, type TooltipRow, type UpdateContext, VERSION, type ValidationError, type ValidationResult, type ValueRef, type ValueRule, type XKind, type XModel, type YModel, aggregateValues, animate, applyA11y, applyPick, arc, area, assertValidSpec, backOut, bandScale, bounceOut, buildCartesianInteraction, buildCartesianModel, buildDataTableFallback, cartesianInteractionBuilders, cartesianRenderers, categorical, chartTitleText, chartTypeLabel, clamp01, clamp255, computeBackingSize, computeFrame, contrastRatio, createDiv, createRoughPen, createSelectionStore, cubicIn, cubicInOut, cubicOut, curveCatmullRom, curveLinear, curveMonotoneX, curveStep, curveStepAfter, curveStepBefore, customRenderers, darkTheme, decimate, dependentParams, diverging, divergingColorScale, divergingSchemes, drawAxesUnderlay, drawOverlay, easings, elasticOut, ensureSketchFont, expoInOut, expoOut, fillParentStyle, filterRows, fontString, formatDate, formatNumber, formatValue, getDevicePixelRatio, groupBy, hashString, inferFieldType, inferFieldTypes, inferValueType, interpolateArray, interpolateNumber, interpolateNumberArray, interpolateObject, interpolateOklab, interpolateRgb, isBrowser, isEmptyValue, isParamClause, isSlicerType, keyFields, lightTheme, line, linear, linearScale, linearToSrgb, literalToValue, logScale, lttbRun, makeMatcher, matchesValue, measureText, monotoneXTangents, mulberry32, niceDomain, oklabToOklch, oklabToRgb, oklchToOklab, ordinalColorScale, parseColor, parseNumberFormat, pivot, pointScale, polygonHachureLines, prefersReducedMotion, prettyDate, quadIn, quadInOut, quadOut, rampFromStops, readableTextColor, relativeLuminance, render, renderDashboard, resolveCurve, resolveDashboardLayout, resolveDashboardSections, resolveEmphasis, resolveEntrance, resolveFilterValues, resolveSketch, resolveTheme, resolveUpdate, rgbToOklab, rgbaToCss, rotatePoint, roundedRect, sampleArc, sequential, sequentialColorScale, sequentialSchemes, setStyle, sinInOut, slicerParamName, smartDate, specFields, srgbToLinear, summarizeChart, themes, tickIncrement, tickStep, ticks, timeScale, timeTickFormat, timeTicks, toHex, tooltipEnabled, validateDashboard, validateSpec, wireViews, withAlpha, withSketchFont };
|