@zakkster/lite-charts 1.0.0 → 1.1.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/CHANGELOG.md +96 -0
- package/Charts.d.ts +249 -21
- package/Charts.js +1653 -482
- package/README.md +156 -102
- package/llms.txt +276 -126
- package/package.json +6 -4
package/llms.txt
CHANGED
|
@@ -1,30 +1,49 @@
|
|
|
1
1
|
# @zakkster/lite-charts
|
|
2
2
|
|
|
3
3
|
> Reactive, zero-GC chart library built on @zakkster/lite-scene. Signals
|
|
4
|
-
> for data, dimensions, theme, series visibility. v1.
|
|
5
|
-
> chart types on
|
|
6
|
-
> axis kernel, pie/donut on the polar slice kernel, radar on its
|
|
7
|
-
> kernel
|
|
8
|
-
>
|
|
9
|
-
>
|
|
10
|
-
>
|
|
11
|
-
>
|
|
12
|
-
>
|
|
13
|
-
>
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
>
|
|
17
|
-
>
|
|
18
|
-
>
|
|
19
|
-
>
|
|
20
|
-
>
|
|
21
|
-
>
|
|
22
|
-
>
|
|
23
|
-
>
|
|
24
|
-
>
|
|
25
|
-
>
|
|
26
|
-
>
|
|
27
|
-
>
|
|
4
|
+
> for data, dimensions, theme, series visibility. v1.1.0 ships NINE
|
|
5
|
+
> chart types on FOUR independent kernels: line/area/bar/bubble/scatter
|
|
6
|
+
> on the axis kernel, pie/donut on the polar slice kernel, radar on its
|
|
7
|
+
> own kernel, and heatmap on its own grid kernel. Kernel boundaries are
|
|
8
|
+
> strict and verified with esbuild tree-shaking: line bundle = 23 KB
|
|
9
|
+
> minified, bar = 25 KB (stacked + rounded + hover), bubble = 25 KB
|
|
10
|
+
> (spatial-index + multi-series + per-point color), scatter = 22 KB,
|
|
11
|
+
> pie = 13 KB, radar = 13 KB, **heatmap = 10.5 KB (the smallest)**.
|
|
12
|
+
> all-nine total: ~70 KB. Kernel-side auto-resize: omit width / height
|
|
13
|
+
> from config and the chart observes its mount container via
|
|
14
|
+
> ResizeObserver.
|
|
15
|
+
> Heatmap: createHeatmap rides createBaseGridChart -- a kernel that
|
|
16
|
+
> knows nothing about axis / polar / radar code. Two band scales (x +
|
|
17
|
+
> y) reuse makeBandScale math; the y band scale uses the same +y-down
|
|
18
|
+
> convention so yBand.leftEdge(0) is the topmost cell. Cell storage:
|
|
19
|
+
> flat Float32Array indexed yIdx*nx + xIdx; Uint8Array presentMask
|
|
20
|
+
> for sparse data (missing cells render as empty space, hit-test
|
|
21
|
+
> returns null for them). Per-cell colors precomputed at extract into
|
|
22
|
+
> state.cellColors: string[] so the draw loop is just
|
|
23
|
+
> fillStyle = cellColors[i]; fillRect(...) -- zero alloc per cell.
|
|
24
|
+
> Default ramp is linear RGB interpolation between two endpoint hex
|
|
25
|
+
> colors via _parseHexColor + _lerpRGBString. The colorFn(v, vMin,
|
|
26
|
+
> vMax) -> css config overrides entirely for OKLCH ramps, quantile
|
|
27
|
+
> binning, diverging schemes. Hit-test is O(1) -- one xBand.invert +
|
|
28
|
+
> one yBand.invert + a presence-mask check. chart.hover is a
|
|
29
|
+
> signal-style reactive accessor with .peek() escape hatch. Mouse
|
|
30
|
+
> events on canvas drive moveHover / hideHover automatically.
|
|
31
|
+
> Bar polish (this release): postExtract hook for stacked layout,
|
|
32
|
+
> native ctx.roundRect with arcTo fallback for rounded corners,
|
|
33
|
+
> per-bar hover tint via reusable cached tinted-color strings.
|
|
34
|
+
> Multi-series bubble: per-point color via colorKey, global size
|
|
35
|
+
> domain across visible series so equal sizes render at equal radii
|
|
36
|
+
> regardless of which series owns the point.
|
|
37
|
+
> Pluggable spatial index: SpatialIndex / SpatialIndexFactory
|
|
38
|
+
> contract auto-engages at ~1000 points for bubble + scatter
|
|
39
|
+
> hit-test; @zakkster/lite-delaunay is the intended default but
|
|
40
|
+
> optional. 231/231 tests pass.
|
|
41
|
+
> ESM-only. Single-file ~6.2k lines. Four peer deps (lite-signal,
|
|
42
|
+
> lite-scene, lite-axis required; lite-delaunay optional), zero
|
|
43
|
+
> runtime deps. MIT.
|
|
44
|
+
> See ROADMAP.md for forward plan: v1.2 heatmap polish (per-row /
|
|
45
|
+
> per-column highlight, quantile binning), v1.3 SVG export, v1.4 log
|
|
46
|
+
> scale + pan/zoom, v1.5 time-series + annotations.
|
|
28
47
|
|
|
29
48
|
## Why use this library
|
|
30
49
|
|
|
@@ -37,48 +56,39 @@
|
|
|
37
56
|
|
|
38
57
|
## When NOT to use
|
|
39
58
|
|
|
40
|
-
- You need SVG output -- coming v1.
|
|
41
|
-
`toDataURL`).
|
|
59
|
+
- You need SVG output -- coming v1.3. Today only `exportPNG`.
|
|
42
60
|
- You need server-side rendering -- the renderer is Canvas2D-bound.
|
|
43
|
-
- You need
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
`@zakkster/lite-virtual`.
|
|
49
|
-
- You need `<2000` points and don't care about GC -- Chart.js is simpler.
|
|
61
|
+
- You need `<200` points across one chart and don't care about GC --
|
|
62
|
+
Chart.js is simpler and the bundle-size argument is weaker at that
|
|
63
|
+
scale.
|
|
64
|
+
- You need built-in heatmap, scatter, or polar-area charts today -- on
|
|
65
|
+
the roadmap (v1.2.0 +) but not yet shipped.
|
|
50
66
|
|
|
51
67
|
## Module shape
|
|
52
68
|
|
|
53
69
|
ESM-only. Named exports from the single `Charts.js` entry:
|
|
54
70
|
|
|
55
71
|
```ts
|
|
56
|
-
|
|
57
|
-
function
|
|
58
|
-
function
|
|
59
|
-
function createBarChart(config: BarChartConfig): Chart;
|
|
72
|
+
function createLineChart(config: LineChartConfig): Chart;
|
|
73
|
+
function createAreaChart(config: AreaChartConfig): Chart;
|
|
74
|
+
function createBarChart(config: BarChartConfig): Chart;
|
|
60
75
|
function createBubbleChart(config: BubbleChartConfig): Chart;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
function
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
function createHeatmap(): never;
|
|
74
|
-
|
|
75
|
-
// Test-only export -- NOT part of the stable public API. Pure helpers
|
|
76
|
-
// for white-box unit testing; the leading underscore signals private.
|
|
77
|
-
// Production code that imports only chart factories never references
|
|
78
|
-
// _testHelpers, so the bundler drops it and everything it pins.
|
|
79
|
-
const _testHelpers: { /* decimateMinMax, makeBandScale, ... */ };
|
|
76
|
+
function createScatterChart(config: ScatterChartConfig): Chart;
|
|
77
|
+
function createPieChart(config: PieChartConfig): Chart;
|
|
78
|
+
function createDonutChart(config: DonutChartConfig): Chart;
|
|
79
|
+
function createRadarChart(config: RadarChartConfig): Chart;
|
|
80
|
+
function createHeatmap(config: HeatmapConfig): Chart;
|
|
81
|
+
|
|
82
|
+
// Public interface for the spatial-index integration (v1.2.0-alpha.0+).
|
|
83
|
+
interface SpatialIndex {
|
|
84
|
+
findNearest(qx, qy, k, maxDistSq, outIndices, outDistSq): number;
|
|
85
|
+
dispose(): void;
|
|
86
|
+
}
|
|
87
|
+
type SpatialIndexFactory = (pxs, pys, n) => SpatialIndex;
|
|
80
88
|
```
|
|
81
89
|
|
|
90
|
+
Nine chart types across four strictly-independent kernels.
|
|
91
|
+
|
|
82
92
|
`AreaChartConfig extends LineChartConfig` with three additional fields:
|
|
83
93
|
|
|
84
94
|
```ts
|
|
@@ -395,77 +405,217 @@ createLineChart({
|
|
|
395
405
|
many `node.set()` calls per signal write, use
|
|
396
406
|
`schedule: (fn) => queueMicrotask(fn)`. The bench documents this clearly.
|
|
397
407
|
|
|
398
|
-
##
|
|
399
|
-
|
|
400
|
-
- v1.0.0-beta.0 - beta.3: line + area polish (interp, markers, theme,
|
|
401
|
-
zero-alloc crosshair, DPR fix)
|
|
402
|
-
- v1.0.0:
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
(
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
visibility -- hidden slices give up their wedge, others grow to fill.
|
|
425
|
-
Pie and donut share SLICE_RENDERER; only innerRadius default differs
|
|
426
|
-
(0 vs 0.5; overridable). Pie bundle 13 KB minified (no axis kernel),
|
|
427
|
-
line bundle 23 KB (no polar kernel). 29 new tests, 148 total.
|
|
428
|
-
- v1.2.0-alpha.2: slice colour resolution bug fix (raw CSS-var leaked
|
|
408
|
+
## Release history (shipped)
|
|
409
|
+
|
|
410
|
+
- v1.0.0-beta.0 - beta.3: line + area polish (interp, markers, theme,
|
|
411
|
+
gridlines, zero-alloc crosshair, DPR fix)
|
|
412
|
+
- v1.0.0 alpha.0: architectural refactor.
|
|
413
|
+
`_createChartImpl(config, renderKind)` extracted into
|
|
414
|
+
`createBaseAxisChart(config, renderer)` + per-chart renderer objects
|
|
415
|
+
(LINE_RENDERER, AREA_RENDERER, BAR_RENDERER). 22 renderKind branches
|
|
416
|
+
eliminated. Polymorphic dispatch via renderer interface:
|
|
417
|
+
buildXAccessor, createXScale, extractData, updateXScale, buildXAxis,
|
|
418
|
+
makeDrawFn, hitTest, lookupRow, formatTooltipHeader. `rendererCtx`
|
|
419
|
+
singleton (xScale, yScale, opts, categoriesRef) mutated in place --
|
|
420
|
+
preserves zero-alloc on hot path.
|
|
421
|
+
- v1.0.0 alpha.1: pie + donut chart family. New `createBasePolarChart`
|
|
422
|
+
kernel -- completely independent from `createBaseAxisChart`. Polar
|
|
423
|
+
state struct: parallel arrays for values (Float32), labels (string[]),
|
|
424
|
+
colors, visibility (Uint8), startAngles (Float64 -- Float32 widens
|
|
425
|
+
PI/2 enough that exact-boundary hits land in wrong slice), arcAngles
|
|
426
|
+
(Float64). `extractSliceData` normalizes array-of-objects, parallel-
|
|
427
|
+
arrays, or plain number arrays. `computeSliceGeometry` centers in
|
|
428
|
+
plot rect with configurable inner radius. `sliceHitTest` is O(n)
|
|
429
|
+
atan2 + linear scan inside ring (n typically 3-12). `makeSliceDrawFn`
|
|
430
|
+
renders wedge (pie) or arc-ring (donut) per slice; hovered slice
|
|
431
|
+
expands 4 px. Pie and donut share SLICE_RENDERER; only innerRadius
|
|
432
|
+
default differs (0 vs 0.5).
|
|
433
|
+
- v1.0.0 alpha.2: slice colour resolution bug fix (raw CSS-var leaked
|
|
429
434
|
into canvas fillStyle); demo gets ResizeObserver-backed responsive
|
|
430
|
-
sizing
|
|
431
|
-
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
r = sqrt(rMin^2 + t*(rMax^2 - rMin^2)). New seriesState fields rs
|
|
436
|
-
(raw sizes) and prs (pixel radii) -- both null on non-bubble
|
|
437
|
-
zero extra memory.
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
435
|
+
sizing.
|
|
436
|
+
- v1.0.0 alpha.3: createBubbleChart on the axis kernel. New
|
|
437
|
+
BUBBLE_RENDERER. Each point becomes a circle with AREA proportional
|
|
438
|
+
to a third dimension via sqrt scale (default; linear available).
|
|
439
|
+
Pixel radii computed at extract time:
|
|
440
|
+
r = sqrt(rMin^2 + t*(rMax^2 - rMin^2)). New seriesState fields `rs`
|
|
441
|
+
(raw sizes) and `prs` (pixel radii) -- both null on non-bubble
|
|
442
|
+
series, zero extra memory. Hit-test signature on the axis kernel
|
|
443
|
+
extended from (canvasX, primary, xScale, ctx) to (canvasX, canvasY,
|
|
444
|
+
primary, xScale, ctx) -- line/area/bar tests ignore canvasY; bubble
|
|
445
|
+
uses both for circle-containment with smallest-on-top tie-breaking
|
|
446
|
+
on overlap.
|
|
447
|
+
- v1.0.0 alpha.4: createRadarChart on a third independent kernel.
|
|
448
|
+
Multi-axis polygon layout: N axes (min 3, typical 5-8) spoked from a
|
|
449
|
+
shared center; each series is a polygon connecting one value-per-axis
|
|
450
|
+
vertex. `computeRadarGeometry` precomputes cos/sin per axis into
|
|
451
|
+
Float64 tables -- polygons, grid rings, and spokes all share them,
|
|
452
|
+
zero per-frame trig. `radarHitTest` is nearest-vertex within 12 px
|
|
453
|
+
across visible series. Spoke labels auto-align based on angular
|
|
454
|
+
position. Three drawing layers (grid -> polygons -> spokes+labels) as
|
|
455
|
+
separate scene nodes for natural z-ordering.
|
|
456
|
+
- v1.0.0: kernel-side auto-resize. `_wireAutoSize` shared helper; omit
|
|
457
|
+
width / height from config and the chart observes its container via
|
|
458
|
+
ResizeObserver. Three modes per dimension: explicit static, explicit
|
|
459
|
+
reactive (signal/fn), or implicit (auto-observe). Synchronous
|
|
460
|
+
initial read avoids size pop; rAF-throttled updates coalesce burst
|
|
461
|
+
events. Falls back gracefully (keeps default size) when
|
|
462
|
+
ResizeObserver is absent. 182 tests, full tree-shake verification.
|
|
463
|
+
- **v1.1.0: bar layout polish.** Stacked bars via new
|
|
464
|
+
`postExtract(states, ctx)` renderer hook -- the kernel calls it
|
|
465
|
+
after the per-series extract loop, bar's hook fills per-series
|
|
466
|
+
`state.stackBottoms` / `state.stackTops` (Float32Arrays, lazy-
|
|
467
|
+
allocated via ensureFloat32) with a visibility-aware cumulative
|
|
468
|
+
accumulator in declaration order; the kernel then re-aggregates
|
|
469
|
+
y-domain to pick up the total stack height. Hidden series excluded
|
|
470
|
+
from the stack; surviving segments snap down on legend toggle.
|
|
471
|
+
Negative values clamp to 0 (diverging stacks deferred to a future
|
|
472
|
+
cut). Rounded corners on the end opposite the baseline (top for
|
|
473
|
+
positive bars, bottom for negative) via native `ctx.roundRect`
|
|
474
|
+
where available + hand-traced `arcTo` fallback for older Safari;
|
|
475
|
+
radii capped at min(w,h)/2. Per-bar hover tint as a CSS-color
|
|
476
|
+
overlay keyed on the chart's `crosshairDataRef.snapIdx`; default
|
|
477
|
+
low-alpha white works against any series color without color
|
|
478
|
+
parsing, opt out with `hoverTint: false`. All three opt-in;
|
|
479
|
+
defaults match v1.0.0 exactly. Bar bundle 25 KB (+1.6 KB);
|
|
480
|
+
line/area/bubble bundles +~300 bytes for the kernel's null-check
|
|
481
|
+
on postExtract; pie/donut/radar unchanged. Three kernels still
|
|
482
|
+
strictly independent -- verified. 196 tests.
|
|
483
|
+
- **v1.2.0-alpha.0: spatial-index foundation.** Pluggable
|
|
484
|
+
nearest-neighbor index for O(log n) hit-test on dense point clouds
|
|
485
|
+
(bubble first; scatter + heatmap will ride the same interface in
|
|
486
|
+
alpha.1+). Public contract on `Charts.d.ts`:
|
|
487
|
+
`SpatialIndexFactory = (pxs, pys, n) -> SpatialIndex` where the
|
|
488
|
+
index exposes `findNearest(qx, qy, k, maxDistSq, outIndices,
|
|
489
|
+
outDistSq) -> count` (caller-owned output buffers; zero alloc per
|
|
490
|
+
query) and `dispose()`. `@zakkster/lite-delaunay` is the intended
|
|
491
|
+
default (sweepline Delaunay -> half-edge mesh -> Voronoi-dual walk
|
|
492
|
+
for the cursor's cell) but optional -- k-d tree, uniform-grid, or
|
|
493
|
+
any custom impl works equally well. lite-charts adds it as an
|
|
494
|
+
optional peer dep via `peerDependenciesMeta.optional`. New
|
|
495
|
+
`BUBBLE_RENDERER` config: `spatialIndex: factory` and
|
|
496
|
+
`spatialIndexThreshold` (default 1000). Below threshold the v1.0.0
|
|
497
|
+
linear scan stays the path. Index is lazy-built on the first hit-
|
|
498
|
+
test after extract, cached across queries, disposed on data /
|
|
499
|
+
scale change (extract calls `_disposeSpatialIndex`) AND on chart
|
|
500
|
+
unmount (via new `renderer.cleanup(states)` hook on the axis
|
|
501
|
+
kernel, symmetric with v1.1.0's `postExtract`). `k > 1` matters
|
|
502
|
+
for bubble: overlapping discs mean the nearest CENTER may not be
|
|
503
|
+
the one whose disc contains the cursor; the renderer asks for
|
|
504
|
+
`K = 8` candidates and post-filters by disc containment +
|
|
505
|
+
smallest-r tie-break, preserving v1.0.0 "visually-topmost wins on
|
|
506
|
+
overlap" semantics. Cached `state.prMaxSq` (max pixel-radius
|
|
507
|
+
squared) gives the index a tight maxDistSq upper bound.
|
|
508
|
+
`state._hitIndices` (Int32Array(8)) and `state._hitDistSq`
|
|
509
|
+
(Float32Array(8)) are stable refs that the index writes into.
|
|
510
|
+
Bubble bundle ~22 KB -> ~23 KB. Line / area / bar / pie / donut /
|
|
511
|
+
radar unchanged. 8 new tests. 204 total.
|
|
512
|
+
- **v1.2.0-alpha.1: createScatterChart.** New `SCATTER_RENDERER` on
|
|
513
|
+
the axis kernel. Eighth chart type ships. Bubble's simpler sibling:
|
|
514
|
+
no size dimension, every marker the same pixel radius
|
|
515
|
+
(`markerSize`, default 4), hit-test snaps to nearest point within
|
|
516
|
+
`hitTolerance` disc (default `markerSize + 4`). Reuses the
|
|
517
|
+
spatial-index foundation from alpha.0 with `k = 1` (no overlap
|
|
518
|
+
concerns). `_extractScatterData` disposes any existing spatial
|
|
519
|
+
index per-extract so the index rebuilds lazily on data / scale
|
|
520
|
+
change. `_scatterCleanup` symmetric with bubble's: disposes
|
|
521
|
+
indices on unmount. Scatter bundle ~24 KB. Adds nothing to other
|
|
522
|
+
bundles (verified). 6 new tests. 210 total.
|
|
523
|
+
- **v1.2.0-alpha.2: multi-series bubble + per-point color +
|
|
524
|
+
global size domain.** `BUBBLE_RENDERER` gains a `postExtract` hook
|
|
525
|
+
(`_bubblePostExtract`) that computes a GLOBAL size domain across
|
|
526
|
+
visible series and re-runs `computeBubbleRadii` on every state.
|
|
527
|
+
Single-series charts short-circuit -- the v1.0.0 fast path stays
|
|
528
|
+
in effect when only one series is visible. Per-series state gets
|
|
529
|
+
`state._stateIdx = i` at construction so the bubble's `lookupRow`
|
|
530
|
+
can tell which series got hit and scope the tooltip to that
|
|
531
|
+
series. Crosshair state gains `snapSeriesIdx` (default -1 --
|
|
532
|
+
non-bubble charts never set it). `_bubbleHitTest` iterates
|
|
533
|
+
`ctx.seriesStates` rather than checking only `primary`. Each
|
|
534
|
+
visible series' bubbles get tested (linear scan or spatial index,
|
|
535
|
+
per its threshold). Returns `snapSeriesIdx` along with `snapIdx`.
|
|
536
|
+
`_initBubbleOpts` gains `colorKey` (string / number / fn). The
|
|
537
|
+
accessor uses `buildRawAccessor` (not `buildAccessor`) so color
|
|
538
|
+
strings like `'#ff0000'` are not `+v`-coerced to NaN.
|
|
539
|
+
`extractBubbleData` extracts per-point colors into `state.cs`
|
|
540
|
+
(plain string Array, lazy-allocated). Colors resolve once via
|
|
541
|
+
`resolveColor` at extract time -- CSS-var values like
|
|
542
|
+
`'--c-emerald'` become concrete strings the draw fn writes to
|
|
543
|
+
`ctx.fillStyle` directly. Bubble draw fn picks `state.cs[i]` per
|
|
544
|
+
row when set, falling back to the series fill otherwise. Zero
|
|
545
|
+
hot-path cost when `colorKey` is unset. Bubble bundle ~23 KB ->
|
|
546
|
+
~24 KB. Test infra bumped lite-signal default registry to
|
|
547
|
+
`maxNodes: 32768` for 200+ chart-creations across the suite. 9
|
|
548
|
+
new tests. 219 total.
|
|
549
|
+
- **v1.2.0-alpha.3 (current): createHeatmap + fourth kernel.**
|
|
550
|
+
New `createBaseGridChart(config, renderer)` kernel. Strictly
|
|
551
|
+
independent from axis / polar / radar kernels -- no cross-kernel
|
|
552
|
+
imports either direction. Heatmap bundle is 10.5 KB minified --
|
|
553
|
+
the smallest of the nine -- because the grid kernel has none of
|
|
554
|
+
the axes / interpolation / decimation / markers / scale-math /
|
|
555
|
+
multi-series weight of the axis kernel. Two band scales (x + y)
|
|
556
|
+
reuse `makeBandScale` math; the y band scale uses the same
|
|
557
|
+
+y-down convention so `yBand.leftEdge(0)` is the topmost cell.
|
|
558
|
+
Cell storage: flat `Float32Array` indexed `yIdx * nx + xIdx`;
|
|
559
|
+
`Uint8Array` `presentMask` for sparse data (missing cells render
|
|
560
|
+
empty, hit-test returns null for them). Per-cell colors
|
|
561
|
+
precomputed at extract into `state.cellColors: string[]` so the
|
|
562
|
+
draw loop is `fillStyle = cellColors[i]; fillRect(...)` --
|
|
563
|
+
zero alloc per cell. Default ramp is linear RGB interpolation
|
|
564
|
+
between two endpoint hex colors via `_parseHexColor` +
|
|
565
|
+
`_lerpRGBString`. The `colorFn(v, vMin, vMax) -> css` config
|
|
566
|
+
overrides entirely for OKLCH ramps, quantile binning, diverging
|
|
567
|
+
schemes. Hit-test is O(1) -- one `xBand.invert` + one
|
|
568
|
+
`yBand.invert` + a presence-mask check. `chart.hover` is a
|
|
569
|
+
signal-style reactive accessor with `.peek()` escape hatch.
|
|
570
|
+
Mouse events on canvas drive `moveHover` / `hideHover`
|
|
571
|
+
automatically; tests drive them manually too. Axis labels
|
|
572
|
+
render inline (no lite-axis dep for the grid kernel since
|
|
573
|
+
categories are arbitrary strings). 12 new tests covering
|
|
574
|
+
category extraction, vMin/vMax, cell render counts (full +
|
|
575
|
+
sparse), default ramp endpoint correctness, custom colorFn,
|
|
576
|
+
hit-test (correct cell + missing + outside plot), data signal
|
|
577
|
+
reactivity, value labels, lifecycle. 231 total.
|
|
578
|
+
|
|
579
|
+
## Capacity (lite-signal arena)
|
|
580
|
+
|
|
581
|
+
lite-signal's default node-arena capacity is **1024 nodes**. Each
|
|
582
|
+
axis-kernel chart uses ~45-60 active nodes (the dominant cost is
|
|
583
|
+
the per-axis tick pool: each tick = `lineNode` + `textNode` = 2
|
|
584
|
+
scene-node effects, at max 12 ticks per axis = ~24 effects per axis
|
|
585
|
+
x 2 axes = ~48). Heatmap uses ~5 nodes (grid kernel renders cells
|
|
586
|
+
through a single pathNode per layer). Pie / donut use ~25.
|
|
587
|
+
|
|
588
|
+
**Default 1024 fits ~15-20 axis-kernel charts.** Dashboards or demos
|
|
589
|
+
with more must bump the registry BEFORE constructing any chart:
|
|
590
|
+
|
|
591
|
+
```js
|
|
592
|
+
import { createRegistry, setDefaultRegistry } from '@zakkster/lite-signal';
|
|
593
|
+
setDefaultRegistry(createRegistry({ maxNodes: 32768 }));
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
The lite-charts demo (`demo/index.html`) and the test suite both
|
|
597
|
+
bump to 32768 for this reason. Bumping after charts are already
|
|
598
|
+
constructed doesn't help those charts -- they read the current
|
|
599
|
+
default registry at construction time.
|
|
600
|
+
|
|
601
|
+
Each mount/unmount cycle leaves ~4 nodes of residue from
|
|
602
|
+
construction-time signals (`widthAutoSig`, `plotBoundsSignal`,
|
|
603
|
+
`scaleVersion`, etc.) that survive unmount so the chart can be
|
|
604
|
+
remounted. For long-running apps that create and destroy many charts
|
|
605
|
+
dynamically, this accumulates until the chart reference is dropped.
|
|
606
|
+
A terminal-teardown `chart.destroy()` is on the roadmap for v1.3.
|
|
607
|
+
|
|
608
|
+
## Forward plan
|
|
609
|
+
|
|
610
|
+
- v1.2.0: heatmap polish -- per-row / per-column highlight on hover,
|
|
611
|
+
quantile binning, value-label auto-color (white-on-dark, dark-on-
|
|
612
|
+
light). Final v1.2.0 cut after the polish lands.
|
|
613
|
+
- v1.3.0: SVG export across all nine charts. Mirrors every draw fn
|
|
614
|
+
through SVG path commands; reuses every projection function so
|
|
615
|
+
output is pixel-identical to canvas.
|
|
616
|
+
- v1.4.0: log scale; pan + zoom; brushing primitives.
|
|
617
|
+
- v1.5.0: time-series specialized variants; legend virtualization via
|
|
618
|
+
`@zakkster/lite-virtual`; annotation layer.
|
|
469
619
|
|
|
470
620
|
## License
|
|
471
621
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zakkster/lite-charts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Reactive, zero-GC chart library. Signal-native data, scales, and dimensions; 60fps at 100k points; zero allocations in steady-state render. Built on @zakkster/lite-scene.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./Charts.js",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"Charts.js",
|
|
19
19
|
"Charts.d.ts",
|
|
20
20
|
"README.md",
|
|
21
|
+
"CHANGELOG.md",
|
|
21
22
|
"llms.txt",
|
|
22
23
|
"LICENSE"
|
|
23
24
|
],
|
|
@@ -28,9 +29,10 @@
|
|
|
28
29
|
"@zakkster/lite-signal": "^1.1.5"
|
|
29
30
|
},
|
|
30
31
|
"scripts": {
|
|
31
|
-
"test": "node --test --test-reporter=spec",
|
|
32
|
-
"test:gc": "node --expose-gc --test --test-reporter=spec",
|
|
33
|
-
"bench": "node --expose-gc bench/line-100k.mjs"
|
|
32
|
+
"test": "node --test --test-reporter=spec test/*.test.js",
|
|
33
|
+
"test:gc": "node --expose-gc --test --test-reporter=spec test/*.test.js",
|
|
34
|
+
"bench": "node --expose-gc bench/line-100k.mjs",
|
|
35
|
+
"demo": "node demo/serve.js"
|
|
34
36
|
},
|
|
35
37
|
"keywords": [
|
|
36
38
|
"chart",
|