@zakkster/lite-charts 1.0.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/Charts.d.ts ADDED
@@ -0,0 +1,638 @@
1
+ /**
2
+ * @zakkster/lite-charts -- TypeScript declarations.
3
+ */
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Common types
7
+ // ---------------------------------------------------------------------------
8
+
9
+ export type Accessor<T> = T | (() => T);
10
+
11
+ export type DataAccessor =
12
+ | object[]
13
+ | { xs: Float32Array; ys: Float32Array }
14
+ | (() => object[] | { xs: Float32Array; ys: Float32Array });
15
+
16
+ export type FieldAccessor =
17
+ | string
18
+ | number
19
+ | ((row: any, index: number) => number);
20
+
21
+ export interface Scale {
22
+ type: 'linear' | 'time';
23
+ dMin: number;
24
+ dMax: number;
25
+ rMin: number;
26
+ rMax: number;
27
+ map(value: number): number;
28
+ invert(pixel: number): number;
29
+ }
30
+
31
+ export interface PNGExportOptions {
32
+ mimeType?: string;
33
+ quality?: number;
34
+ }
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Line chart
38
+ // ---------------------------------------------------------------------------
39
+
40
+ export interface SeriesConfig {
41
+ name?: string;
42
+ data: DataAccessor;
43
+ color?: string;
44
+ lineWidth?: number;
45
+ /** Per-series override for interpolation mode. Default inherits from chart. */
46
+ interpolation?: InterpolationMode;
47
+ /** Per-series override for markers. `false` disables. Default inherits from chart. */
48
+ markers?: boolean | MarkerConfig;
49
+ }
50
+
51
+ export interface XScaleConfig {
52
+ type?: 'linear' | 'time';
53
+ domain?: [number, number];
54
+ }
55
+
56
+ export interface YScaleConfig {
57
+ domain?: [number, number];
58
+ zero?: boolean;
59
+ nice?: boolean;
60
+ }
61
+
62
+ export interface MarginConfig {
63
+ top?: number;
64
+ right?: number;
65
+ bottom?: number;
66
+ left?: number;
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Crosshair / tooltip
71
+ // ---------------------------------------------------------------------------
72
+
73
+ export interface CrosshairState {
74
+ visible: boolean;
75
+ /** Index into the primary series xs[] of the snapped sample, or -1 if hidden. */
76
+ snapIdx: number;
77
+ /** Domain x-value of the snapped sample (ms for time scales). */
78
+ snapDomainX: number;
79
+ /** Pixel x of the snapped sample (where the vertical crosshair line draws). */
80
+ snapPixelX: number;
81
+ /** Pixel y of the mouse cursor (not snapped to any sample). */
82
+ mousePixelY: number;
83
+ }
84
+
85
+ export interface CrosshairConfig {
86
+ /** CSS color of the vertical crosshair line. Default: '#666'. */
87
+ color?: string;
88
+ /** Dash pattern for the crosshair line. Default: [3, 3]. */
89
+ dash?: number[];
90
+ }
91
+
92
+ export type InterpolationMode =
93
+ | 'linear'
94
+ | 'step'
95
+ | 'step-after'
96
+ | 'step-before'
97
+ | 'step-mid'
98
+ | 'monotone'
99
+ | 'catmull-rom';
100
+
101
+ export type MarkerShape = 'circle' | 'square' | 'triangle' | 'diamond';
102
+
103
+ export interface MarkerConfig {
104
+ shape?: MarkerShape;
105
+ /** Marker size in CSS pixels (diameter for circle, side length otherwise). Default 5. */
106
+ size?: number;
107
+ /** Fill color. Defaults to the series color. */
108
+ fill?: string;
109
+ /** Stroke color. Default '#ffffff'. Pass falsy to disable. */
110
+ stroke?: string;
111
+ /** Stroke width. Default 1. Pass 0 to disable stroke. */
112
+ strokeWidth?: number;
113
+ /** Draw 1 of every N samples. Default 1. Use 5-10 for dense series to avoid visual noise. */
114
+ everyN?: number;
115
+ }
116
+
117
+ export interface GridConfig {
118
+ /** Show vertical gridlines at X tick positions. Default true (when grid object passed). */
119
+ x?: boolean;
120
+ /** Show horizontal gridlines at Y tick positions. Default true (when grid object passed). */
121
+ y?: boolean;
122
+ /** Gridline color. Subtle by default. Accepts hex / oklch / '--css-var' tokens. */
123
+ color?: string;
124
+ }
125
+
126
+ export interface TooltipRow {
127
+ color: string;
128
+ label: string;
129
+ value: string;
130
+ }
131
+
132
+ export interface TooltipFormatContext {
133
+ snapIdx: number;
134
+ snapDomainX: number;
135
+ xScaleType: 'linear' | 'time';
136
+ /**
137
+ * For bar charts (categorical x), the category name at the snapped
138
+ * index; `null` for line / area / bubble charts. Present on the same
139
+ * context object across all axis-kernel charts so a formatter doesn't
140
+ * have to branch on chart type.
141
+ */
142
+ category: string | null;
143
+ /** Default rows, one per series with data at the snap. Mutable by formatter. */
144
+ rows: TooltipRow[];
145
+ }
146
+
147
+ export interface TooltipConfig {
148
+ background?: string;
149
+ border?: string;
150
+ /**
151
+ * Override the default rows + header. Return a string for header-only,
152
+ * or `{ header?, rows? }` to customize both.
153
+ */
154
+ format?: (ctx: TooltipFormatContext) => string | { header?: string; rows?: TooltipRow[] };
155
+ }
156
+
157
+ // ---------------------------------------------------------------------------
158
+ // Legend
159
+ // ---------------------------------------------------------------------------
160
+
161
+ export type LegendPosition = 'top' | 'bottom' | 'left' | 'right';
162
+
163
+ export interface LegendConfig {
164
+ position?: LegendPosition;
165
+ /**
166
+ * Append the legend into an existing element instead of auto-wrapping
167
+ * the canvas. Useful for custom layouts where canvas and legend live
168
+ * in different DOM trees.
169
+ */
170
+ container?: HTMLElement;
171
+ }
172
+
173
+ /** Minimal signal shape (lite-signal). Read = `sig()`, write = `sig.set(v)`. */
174
+ export interface Signal<T> {
175
+ (): T;
176
+ peek(): T;
177
+ set(v: T): void;
178
+ update(fn: (v: T) => T): void;
179
+ }
180
+
181
+ export interface LineChartConfig {
182
+ /** Single-series shorthand. Either `data` or `series` is required. */
183
+ data?: DataAccessor;
184
+ /** Multi-series form. */
185
+ series?: SeriesConfig[];
186
+
187
+ /** x accessor: string key, integer index, or function. Default 'x'. */
188
+ x?: FieldAccessor;
189
+ /** y accessor: string key, integer index, or function. Default 'y'. */
190
+ y?: FieldAccessor;
191
+
192
+ /** Static or reactive width in CSS pixels. Default 800. */
193
+ width?: Accessor<number>;
194
+ /** Static or reactive height in CSS pixels. Default 400. */
195
+ height?: Accessor<number>;
196
+
197
+ margin?: MarginConfig;
198
+
199
+ color?: string;
200
+ lineWidth?: number;
201
+ background?: string | null;
202
+ axisColor?: string;
203
+ labelColor?: string;
204
+ font?: string;
205
+
206
+ /** Path interpolation. Default 'linear'. Per-series override via SeriesConfig.interpolation. */
207
+ interpolation?: InterpolationMode;
208
+ /** Marker dots at each sample point. `true` for defaults, `false` to disable, or an object. */
209
+ markers?: boolean | MarkerConfig;
210
+ /** Gridlines through the plot rect at each tick. Default false. `true` enables both axes. */
211
+ grid?: boolean | GridConfig;
212
+
213
+ dpr?: number;
214
+
215
+ xScale?: XScaleConfig;
216
+ yScale?: YScaleConfig;
217
+
218
+ /** Crosshair: vertical line + per-series marker dots at the snapped x. Default true. Pass false to disable. */
219
+ crosshair?: boolean | CrosshairConfig;
220
+ /** Tooltip: canvas-drawn box with sample values at the snapped x. Default true. Pass false to disable. */
221
+ tooltip?: boolean | TooltipConfig;
222
+ /** Legend: DOM-rendered with click-to-toggle. Default 'bottom'. Pass false to disable, a position string for shorthand, or LegendConfig for full control. */
223
+ legend?: boolean | LegendPosition | LegendConfig;
224
+
225
+ /**
226
+ * Frame scheduler. Default rAF in browser, synchronous in Node.
227
+ * Pass `queueMicrotask` for headless coalesced batching.
228
+ */
229
+ schedule?: (fn: () => void) => void;
230
+ }
231
+
232
+ export interface SeriesConfigPublic extends SeriesConfig {}
233
+
234
+ export interface Chart {
235
+ /** Mount into a DOM element (creates canvas inside) or directly into a canvas. */
236
+ mount(target: HTMLElement | HTMLCanvasElement): Chart;
237
+ /** Dispose effects and remove the owned canvas. Idempotent. */
238
+ unmount(): void;
239
+ /** Returns a data URL via canvas.toDataURL. Requires a real HTMLCanvasElement. */
240
+ exportPNG(opts?: PNGExportOptions): string;
241
+ /** Force a redraw without changing data. */
242
+ redraw(): void;
243
+
244
+ /**
245
+ * Programmatically move the crosshair to a canvas-local pixel position.
246
+ * Snaps to the nearest sample on the primary series. Used by tests and
247
+ * for synchronizing crosshairs across small multiples.
248
+ */
249
+ moveCrosshair(canvasX: number, canvasY: number): void;
250
+ /** Hide the crosshair / tooltip. Idempotent. */
251
+ hideCrosshair(): void;
252
+ /** Toggle a series' visibility. Out-of-range indices are safe no-ops. */
253
+ setSeriesVisible(idx: number, visible: boolean): void;
254
+ /**
255
+ * Re-resolve every CSS-var-driven color against the current container's
256
+ * computed style and trigger a redraw. Call after a theme switch (dark
257
+ * mode, etc.). No-op if the chart isn't mounted.
258
+ */
259
+ refreshTheme(): void;
260
+
261
+ readonly scene: unknown; // lite-scene Scene instance, or null pre-mount
262
+ readonly canvas: HTMLCanvasElement | null;
263
+ readonly xScale: Scale;
264
+ readonly yScale: Scale;
265
+ readonly xScaleType: 'linear' | 'time';
266
+ /** Version-counter signal; read to subscribe to plot-bounds changes. */
267
+ readonly plotBounds: () => number;
268
+ /**
269
+ * Crosshair state. Behaves like a signal: callable to subscribe-and-read,
270
+ * `.peek()` to read without subscribing, `.set()` to write, `.subscribe()`
271
+ * to register a callback. The returned `CrosshairState`
272
+ * is the SAME mutable reference on every read -- this eliminates the
273
+ * per-mousemove allocation that hardware polling rates would otherwise
274
+ * generate. Read fields eagerly when notified; do not retain the reference
275
+ * and re-read later expecting stable values.
276
+ */
277
+ readonly crosshair: (() => CrosshairState) & {
278
+ peek(): CrosshairState;
279
+ /** Partial accepted; missing fields fall back to safe defaults (snapIdx=-1, x/y=0). */
280
+ set(v: Partial<CrosshairState>): void;
281
+ subscribe(fn: (s: CrosshairState) => void): () => void;
282
+ };
283
+ /** One signal per series. Read in a reactive context to bind external UI; write to toggle. */
284
+ readonly seriesVisibility: Signal<boolean>[];
285
+ /** The legend DOM element, or null if legend was disabled / mounted into a bare canvas. */
286
+ readonly legend: HTMLElement | null;
287
+ }
288
+
289
+ export function createLineChart(config: LineChartConfig): Chart;
290
+
291
+ // ---------------------------------------------------------------------------
292
+ // Area chart
293
+ // ---------------------------------------------------------------------------
294
+ //
295
+ // Same data + scale + reactivity contract as the line chart, plus a baseline
296
+ // to fill to. Renders fill + optional upper-boundary stroke.
297
+
298
+ export interface AreaChartConfig extends LineChartConfig {
299
+ /**
300
+ * Where to close the area to. Number = domain y value (default 0); 'bottom'
301
+ * pins to the bottom edge of the plot rect regardless of domain.
302
+ */
303
+ baseline?: number | 'bottom';
304
+ /** Whether to stroke the upper boundary. Default true. */
305
+ stroke?: boolean;
306
+ /** Fill opacity multiplied into globalAlpha before fill. Default 0.3. */
307
+ fillOpacity?: number;
308
+ }
309
+
310
+ export function createAreaChart(config: AreaChartConfig): Chart;
311
+
312
+ // ---------------------------------------------------------------------------
313
+ // Bar chart (v1.1.0)
314
+ // ---------------------------------------------------------------------------
315
+ //
316
+ // Categorical x-axis (band scale) with linear y. Multi-series renders grouped
317
+ // side-by-side by default; each series occupies a slice of the band centered
318
+ // on its series index. Stacked layout ships in v1.1.1.
319
+
320
+ export interface BarChartConfig extends Omit<LineChartConfig, 'interpolation' | 'markers' | 'xScale'> {
321
+ /**
322
+ * Y value at which bars start. Default 0. Negative bars extend downward
323
+ * from this baseline; positive bars extend upward.
324
+ */
325
+ baseline?: number;
326
+ /**
327
+ * Fraction of step taken up by the gap BETWEEN bands. d3-scaleBand
328
+ * convention; in [0, 1). Default 0.15.
329
+ */
330
+ paddingInner?: number;
331
+ /**
332
+ * Fraction of step at each END of the range. d3-scaleBand convention;
333
+ * in [0, 1). Default 0.1.
334
+ */
335
+ paddingOuter?: number;
336
+ /**
337
+ * For grouped layout: fraction of group-slot width left as gap between
338
+ * adjacent grouped bars within the same category. Default 0.08.
339
+ */
340
+ groupInnerPad?: number;
341
+ /**
342
+ * X-scale overrides. Bar charts always use a band scale; only `domain`
343
+ * (an explicit categories array) is honoured here -- the inferred type
344
+ * is forced to 'band'.
345
+ */
346
+ xScale?: { domain?: string[] };
347
+ }
348
+
349
+ export function createBarChart(config: BarChartConfig): Chart;
350
+
351
+ // ---------------------------------------------------------------------------
352
+ // Bubble chart (axis kernel with size dimension)
353
+ // ---------------------------------------------------------------------------
354
+
355
+ export interface BubbleSeriesInput {
356
+ /** Single-series data: array of points or accessor function. */
357
+ data: Array<{ [k: string]: unknown }> | (() => Array<{ [k: string]: unknown }>);
358
+ name?: string;
359
+ color?: string;
360
+ stroke?: string;
361
+ strokeWidth?: number;
362
+ fillOpacity?: number;
363
+ }
364
+
365
+ export interface BubbleChartConfig {
366
+ /** Single-series data shape (preferred). */
367
+ data?: Array<{ [k: string]: unknown }> | (() => Array<{ [k: string]: unknown }>);
368
+ /** Multi-series shape (v1.3+; one series for now). */
369
+ series?: BubbleSeriesInput[];
370
+
371
+ /** Key (or index) for x values. Default 'x'. */
372
+ x?: string | number | ((row: unknown, i: number) => number);
373
+ /** Key (or index) for y values. Default 'y'. */
374
+ y?: string | number | ((row: unknown, i: number) => number);
375
+ /** Key (or index) for the size dimension. Default 'value'. */
376
+ size?: string | number | ((row: unknown, i: number) => number);
377
+
378
+ /** Minimum pixel radius. Default 4. */
379
+ minRadius?: number;
380
+ /** Maximum pixel radius. Default 40. */
381
+ maxRadius?: number;
382
+ /**
383
+ * 'sqrt' (default) is area-proportional and eye-correct (Tukey
384
+ * convention). 'linear' is radius-proportional; use when the size
385
+ * dimension is already a length/radius rather than a magnitude.
386
+ */
387
+ sizeScale?: 'sqrt' | 'linear';
388
+
389
+ color?: string;
390
+ stroke?: string;
391
+ strokeWidth?: number;
392
+ /** Bubble fill alpha. Default 0.6. */
393
+ fillOpacity?: number;
394
+
395
+ width?: number | (() => number);
396
+ height?: number | (() => number);
397
+ margin?: { top?: number; right?: number; bottom?: number; left?: number };
398
+
399
+ xScale?: { type?: 'linear' | 'time'; domain?: [number, number] };
400
+ yScale?: { type?: 'linear'; domain?: [number, number]; nice?: boolean; zero?: boolean };
401
+
402
+ grid?: boolean | { x?: boolean; y?: boolean; color?: string };
403
+
404
+ crosshair?: false | { color?: string; dash?: [number, number] };
405
+ tooltip?: false | {
406
+ background?: string;
407
+ border?: string;
408
+ format?: (info: {
409
+ snapIdx: number;
410
+ snapDomainX: number;
411
+ xScaleType: string;
412
+ category: string | null;
413
+ rows: Array<{ color: string; label: string; value: string }>;
414
+ }) => string | { header?: string; rows?: Array<{ color: string; label: string; value: string }> };
415
+ };
416
+
417
+ legend?: boolean | 'top' | 'bottom' | 'left' | 'right' | {
418
+ position?: 'top' | 'bottom' | 'left' | 'right';
419
+ container?: HTMLElement;
420
+ };
421
+
422
+ font?: string;
423
+ labelColor?: string;
424
+ axisColor?: string;
425
+ background?: string;
426
+ dpr?: number;
427
+ schedule?: (cb: () => void) => unknown;
428
+ }
429
+
430
+ export function createBubbleChart(config: BubbleChartConfig): Chart;
431
+
432
+ // ---------------------------------------------------------------------------
433
+ // Radar chart (separate kernel)
434
+ // ---------------------------------------------------------------------------
435
+
436
+ export interface RadarSeriesInput {
437
+ name?: string;
438
+ color?: string;
439
+ values: number[]; // one value per axis, parallel to config.axes
440
+ }
441
+
442
+ export interface RadarChartConfig {
443
+ /** Axis labels. Length determines axis count; minimum 3 (triangle). */
444
+ axes: string[];
445
+ /** Series, each with values parallel to `axes`. */
446
+ series: RadarSeriesInput[] | (() => RadarSeriesInput[]);
447
+
448
+ /** Shared [min, max] across all axes. If omitted, auto-computed from data. */
449
+ domain?: [number, number];
450
+ /** Number of concentric grid rings. Default 4. */
451
+ gridTicks?: number;
452
+ /** Polygon fill alpha [0..1]. Default 0.2. */
453
+ fillOpacity?: number;
454
+ /** Polygon stroke width. Default 2. */
455
+ strokeWidth?: number;
456
+
457
+ width?: number | (() => number);
458
+ height?: number | (() => number);
459
+ margin?: { top?: number; right?: number; bottom?: number; left?: number };
460
+
461
+ axisColor?: string;
462
+ gridColor?: string;
463
+ labelColor?: string;
464
+ font?: string;
465
+ background?: string;
466
+
467
+ tooltip?: false | {
468
+ background?: string;
469
+ border?: string;
470
+ format?: (info: {
471
+ axisIdx: number;
472
+ axisLabel: string;
473
+ seriesIdx: number;
474
+ value: number;
475
+ rows: Array<{ color: string; label: string; value: string }>;
476
+ }) => string | { header?: string; rows?: Array<{ color: string; label: string; value: string }> };
477
+ };
478
+
479
+ legend?: boolean | 'top' | 'bottom' | 'left' | 'right' | {
480
+ position?: 'top' | 'bottom' | 'left' | 'right';
481
+ container?: HTMLElement;
482
+ };
483
+
484
+ dpr?: number;
485
+ schedule?: (cb: () => void) => unknown;
486
+ }
487
+
488
+ export interface RadarChart {
489
+ mount(target: HTMLElement | HTMLCanvasElement): RadarChart;
490
+ unmount(): void;
491
+ exportPNG(opts?: { mimeType?: string; quality?: number }): string;
492
+ redraw(): void;
493
+ moveCrosshair(canvasX: number, canvasY: number): void;
494
+ hideCrosshair(): void;
495
+ setSeriesVisible(idx: number, visible: boolean): void;
496
+ refreshTheme(): void;
497
+ readonly scene: unknown;
498
+ readonly canvas: HTMLCanvasElement | null;
499
+ readonly geometry: { cx: number; cy: number; rOuter: number; axisCount: number };
500
+ readonly domain: [number, number];
501
+ readonly legend: HTMLElement | null;
502
+ plotBounds: unknown;
503
+ crosshair: unknown;
504
+ seriesVisibility: Array<unknown>;
505
+ }
506
+
507
+ export function createRadarChart(config: RadarChartConfig): RadarChart;
508
+
509
+ // ---------------------------------------------------------------------------
510
+ // Polar slice charts -- pie / donut
511
+ // ---------------------------------------------------------------------------
512
+
513
+ export interface SliceInput {
514
+ label?: string;
515
+ value: number;
516
+ color?: string;
517
+ }
518
+
519
+ export interface PieChartConfig {
520
+ /** Slices, as objects with {label, value, color} OR a function returning them. */
521
+ data?: SliceInput[] | (() => SliceInput[]) | { values: number[]; labels?: string[]; colors?: string[] };
522
+ /** Parallel-arrays form: provided alongside data, or instead of it. */
523
+ values?: number[];
524
+ labels?: string[];
525
+ colors?: string[];
526
+
527
+ width?: number | (() => number);
528
+ height?: number | (() => number);
529
+ margin?: { top?: number; right?: number; bottom?: number; left?: number };
530
+
531
+ /**
532
+ * Inner radius: 0 = pie, 0..1 = fraction of outer, >1 = absolute pixels.
533
+ * createPieChart defaults to 0; createDonutChart defaults to 0.5.
534
+ */
535
+ innerRadius?: number;
536
+
537
+ sliceStroke?: string;
538
+ sliceStrokeWidth?: number;
539
+ labelColor?: string;
540
+ font?: string;
541
+ background?: string;
542
+
543
+ tooltip?: boolean | {
544
+ background?: string;
545
+ border?: string;
546
+ format?: (info: {
547
+ sliceIdx: number;
548
+ label: string;
549
+ value: number;
550
+ total: number;
551
+ percent: number;
552
+ }) => string | { header?: string; value?: string };
553
+ };
554
+
555
+ legend?: boolean | 'top' | 'bottom' | 'left' | 'right' | {
556
+ position?: 'top' | 'bottom' | 'left' | 'right';
557
+ container?: HTMLElement;
558
+ };
559
+
560
+ dpr?: number;
561
+ schedule?: (cb: () => void) => unknown;
562
+ }
563
+
564
+ /** createDonutChart accepts the same config as createPieChart, with a 0.5
565
+ * innerRadius default instead of 0. The user can still override
566
+ * innerRadius (e.g. 0.7 for a thinner donut). */
567
+ export type DonutChartConfig = PieChartConfig;
568
+
569
+ export interface PolarChart {
570
+ mount(target: HTMLElement | HTMLCanvasElement): PolarChart;
571
+ unmount(): void;
572
+ exportPNG(opts?: { mimeType?: string; quality?: number }): string;
573
+ redraw(): void;
574
+ moveCrosshair(canvasX: number, canvasY: number): void;
575
+ hideCrosshair(): void;
576
+ setSliceVisible(idx: number, visible: boolean): void;
577
+ refreshTheme(): void;
578
+ readonly scene: unknown;
579
+ readonly canvas: HTMLCanvasElement | null;
580
+ readonly geometry: { cx: number; cy: number; rOuter: number; rInner: number };
581
+ readonly legend: HTMLElement | null;
582
+ plotBounds: unknown;
583
+ crosshair: unknown;
584
+ sliceVisibility: Array<unknown>;
585
+ }
586
+
587
+ export function createPieChart(config: PieChartConfig): PolarChart;
588
+ export function createDonutChart(config: DonutChartConfig): PolarChart;
589
+
590
+ // ---------------------------------------------------------------------------
591
+ // Stubs for subsequent versions -- throw at runtime.
592
+ // ---------------------------------------------------------------------------
593
+ //
594
+ // Each will be a thin composition over a base kernel:
595
+ // createHeatmap = createBaseGridChart(config, HEATMAP_RENDERER)
596
+
597
+ export function createScatterChart(...args: unknown[]): never;
598
+ export function createHeatmap(...args: unknown[]): never;
599
+
600
+ // ---------------------------------------------------------------------------
601
+ // Test-only export (NOT part of the stable public API)
602
+ // ---------------------------------------------------------------------------
603
+ //
604
+ // Pure helpers for white-box unit testing. The leading underscore signals
605
+ // private; this lives on a separate top-level export so production code that
606
+ // imports only chart factories never pins these symbols into its bundle.
607
+
608
+ export const _testHelpers: {
609
+ // Axis-chart kernel
610
+ decimateMinMax: Function;
611
+ updateLinearScale: Function;
612
+ extractSeriesData: Function;
613
+ extractBarSeriesData: Function;
614
+ scaleSeriesToPixels: Function;
615
+ makeLinearScale: Function;
616
+ makeBandScale: Function;
617
+ updateBandScale: Function;
618
+ buildAccessor: Function;
619
+ buildRawAccessor: Function;
620
+ niceYDomain: Function;
621
+ inferXScaleType: Function;
622
+ resolveColor: Function;
623
+ bisectNearest: Function;
624
+ // Bubble-specific
625
+ extractBubbleData: Function;
626
+ computeBubbleRadii: Function;
627
+ // Polar (pie/donut) kernel
628
+ extractSliceData: Function;
629
+ computeSliceGeometry: Function;
630
+ sliceHitTest: Function;
631
+ recomputePolarAngles: Function;
632
+ makePolarState: Function;
633
+ // Radar kernel
634
+ extractRadarSeriesData: Function;
635
+ computeRadarGeometry: Function;
636
+ radarHitTest: Function;
637
+ makeRadarSeriesState: Function;
638
+ };