basalt-ui 1.15.0 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/agent/rules/basalt-charts.md +74 -13
  3. package/agent/templates/CLAUDE-block.md.tpl +9 -6
  4. package/dist/charts/cursor/resolve.d.ts +29 -5
  5. package/dist/charts/cursor/resolve.d.ts.map +1 -1
  6. package/dist/charts/cursor/resolve.js +25 -4
  7. package/dist/charts/hooks/useChartCursor.d.ts +8 -1
  8. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
  9. package/dist/charts/hooks/useChartCursor.js +6 -2
  10. package/dist/charts/index.d.ts +2 -1
  11. package/dist/charts/index.d.ts.map +1 -1
  12. package/dist/charts/kinds/Bars.d.ts +30 -8
  13. package/dist/charts/kinds/Bars.d.ts.map +1 -1
  14. package/dist/charts/kinds/Bars.js +10 -3
  15. package/dist/charts/kinds/Bars.test.d.ts +2 -0
  16. package/dist/charts/kinds/Bars.test.d.ts.map +1 -0
  17. package/dist/charts/kinds/DualPanel.d.ts +30 -0
  18. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  19. package/dist/charts/kinds/DualPanel.js +82 -12
  20. package/dist/charts/kinds/DualPanel.test.d.ts +2 -0
  21. package/dist/charts/kinds/DualPanel.test.d.ts.map +1 -0
  22. package/dist/charts/kinds/MultiLine.d.ts +9 -0
  23. package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
  24. package/dist/charts/kinds/MultiLine.js +45 -33
  25. package/dist/charts/kinds/MultiLine.test.d.ts +2 -0
  26. package/dist/charts/kinds/MultiLine.test.d.ts.map +1 -0
  27. package/dist/charts/kinds/StackedArea.d.ts +9 -0
  28. package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
  29. package/dist/charts/kinds/StackedArea.js +16 -1
  30. package/dist/charts/kinds/StackedArea.test.d.ts +2 -0
  31. package/dist/charts/kinds/StackedArea.test.d.ts.map +1 -0
  32. package/dist/charts/kinds/ZonedLine.d.ts +9 -0
  33. package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
  34. package/dist/charts/kinds/ZonedLine.js +5 -0
  35. package/dist/charts/kinds/ZonedLine.test.d.ts +2 -0
  36. package/dist/charts/kinds/ZonedLine.test.d.ts.map +1 -0
  37. package/dist/charts/primitives/CartesianChart.d.ts +45 -6
  38. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
  39. package/dist/charts/primitives/CartesianChart.js +29 -12
  40. package/dist/charts/primitives/CartesianChart.test.d.ts.map +1 -1
  41. package/dist/charts/primitives/ChartFrame.d.ts +8 -1
  42. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  43. package/dist/charts/primitives/ChartFrame.js +1 -1
  44. package/dist/charts/primitives/ChartLegend.d.ts +3 -0
  45. package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
  46. package/dist/charts/primitives/ChartLegend.js +4 -0
  47. package/dist/charts/primitives/ChartTooltip.d.ts +6 -1
  48. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  49. package/dist/charts/primitives/ChartTooltip.js +3 -2
  50. package/dist/charts/primitives/ChartTooltip.test.d.ts +2 -0
  51. package/dist/charts/primitives/ChartTooltip.test.d.ts.map +1 -0
  52. package/dist/charts/primitives/XZoneRects.d.ts +22 -8
  53. package/dist/charts/primitives/XZoneRects.d.ts.map +1 -1
  54. package/dist/charts/primitives/XZoneRects.js +39 -2
  55. package/dist/charts/series.d.ts +16 -3
  56. package/dist/charts/series.d.ts.map +1 -1
  57. package/dist/charts/series.js +2 -1
  58. package/dist/charts/utils/domain.d.ts +15 -0
  59. package/dist/charts/utils/domain.d.ts.map +1 -1
  60. package/dist/charts/utils/domain.js +6 -1
  61. package/llms.txt +2 -2
  62. package/package.json +1 -1
  63. package/src/charts/cursor/resolve.test.ts +62 -0
  64. package/src/charts/cursor/resolve.ts +71 -7
  65. package/src/charts/hooks/useChartCursor.ts +12 -1
  66. package/src/charts/index.ts +2 -0
  67. package/src/charts/kinds/Bars.test.tsx +183 -0
  68. package/src/charts/kinds/Bars.tsx +41 -12
  69. package/src/charts/kinds/DualPanel.test.tsx +441 -0
  70. package/src/charts/kinds/DualPanel.tsx +127 -11
  71. package/src/charts/kinds/MultiLine.test.tsx +218 -0
  72. package/src/charts/kinds/MultiLine.tsx +64 -36
  73. package/src/charts/kinds/StackedArea.test.tsx +114 -0
  74. package/src/charts/kinds/StackedArea.tsx +25 -1
  75. package/src/charts/kinds/ZonedLine.test.tsx +147 -0
  76. package/src/charts/kinds/ZonedLine.tsx +14 -0
  77. package/src/charts/primitives/CartesianChart.test.tsx +269 -0
  78. package/src/charts/primitives/CartesianChart.tsx +79 -19
  79. package/src/charts/primitives/ChartFrame.test.tsx +42 -0
  80. package/src/charts/primitives/ChartFrame.tsx +9 -2
  81. package/src/charts/primitives/ChartLegend.test.tsx +30 -0
  82. package/src/charts/primitives/ChartLegend.tsx +7 -0
  83. package/src/charts/primitives/ChartTooltip.test.tsx +23 -0
  84. package/src/charts/primitives/ChartTooltip.tsx +7 -1
  85. package/src/charts/primitives/XZoneRects.test.tsx +60 -0
  86. package/src/charts/primitives/XZoneRects.tsx +75 -10
  87. package/src/charts/series.test.ts +51 -0
  88. package/src/charts/series.ts +18 -6
  89. package/src/charts/utils/domain.test.ts +31 -1
  90. package/src/charts/utils/domain.ts +19 -0
  91. package/src/charts/primitives/CartesianChart.test.ts +0 -99
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.17.0](https://github.com/jkrumm/basalt-ui/compare/v1.16.0...v1.17.0) (2026-08-18)
2
+
3
+
4
+ ### Features
5
+
6
+ * fix seven chart defects, six of them found by consumers ([c3df0b8](https://github.com/jkrumm/basalt-ui/commit/c3df0b8439c51effaf425c54644cd9a15328ef12))
7
+
8
+ # [1.16.0](https://github.com/jkrumm/basalt-ui/compare/v1.15.0...v1.16.0) (2026-08-18)
9
+
10
+
11
+ ### Features
12
+
13
+ * close the chart-API gaps the first full consumer migration found ([518a086](https://github.com/jkrumm/basalt-ui/commit/518a086e632c7c010584071f5780d7aa3bbe3bc2))
14
+
1
15
  # [1.15.0](https://github.com/jkrumm/basalt-ui/compare/v1.14.0...v1.15.0) (2026-08-18)
2
16
 
3
17
 
@@ -137,7 +137,14 @@ ships no map kind.
137
137
  `tickFormat` (`TickFormatter<string>`, defaults to `fmtAxisDate` — `DD.MM`) — the only supported
138
138
  exit for a sub-day window, where the default collapses every tick to the same label and a raw
139
139
  `<AxisBottom>` isn't an option. `CartesianChart`'s own `formatX` prop is the higher-level way to
140
- set this without touching the axis component at all.
140
+ set this without touching the axis component at all — and every cartesian kind (`Bars`/
141
+ `MultiLine`/`StackedArea`/`ZonedLine`/`DualPanel`) forwards its own `formatX?: (key) => string`
142
+ now too, not just `CartesianChart` itself. Without it, the only route to a custom x label was
143
+ pre-formatting it into the domain key, which makes one string serve as display value, scale
144
+ identity, AND cursor key simultaneously — and a truncating formatter then collapses two points
145
+ onto one domain value, silently dropping one from the plot (see #6 below for why that's the
146
+ cursor's problem too). `Heatmap` is deliberately excluded: its existing `colLabel`/`rowLabel`
147
+ already are that seam, and a second prop over one concern would fork them.
141
148
  6. **The cursor is shared by default.** No provider needed — `useChartCursor` reads a module-level
142
149
  external store (`useSyncExternalStore`), so every `CartesianChart`/`ChartFrame`-composed chart on
143
150
  the page shares one cursor out of the box. `ChartCursorScope` **isolates** a subtree onto a
@@ -160,10 +167,26 @@ ships no map kind.
160
167
  into weekly buckets still tracks a hover from a sibling plotting daily points, with no extra
161
168
  wiring — `resolveKey` no longer exists because there is nothing left for it to patch.
162
169
 
170
+ `CursorResolution = 'nearest' | 'leading'` (exported from `basalt-ui/charts`) picks WHICH own
171
+ point wins that resolution, reachable as `cursorResolution` on `CartesianChart`/every cartesian
172
+ kind/`DualPanel` and as `useChartCursor`'s `resolution` option — default `'nearest'`, unchanged.
173
+ Reach for `'leading'` when `getX` returns a bucket's LEADING EDGE (a weekly series keyed by its
174
+ Monday, a monthly series keyed by its 1st): under `'nearest'`, a hover landing in the back half
175
+ of a bucket resolves to the FOLLOWING bucket instead of the one it's actually inside, so the
176
+ shared crosshair sits one column right of the data being pointed at — reproducibly, for every
177
+ back-half hover.
178
+
163
179
  7. **Keyboard-operable by construction.** The hover overlay `CartesianChart` renders is focusable;
164
180
  `←`/`→` scrub the shared cursor one point at a time, `Escape` clears it. The tooltip is
165
181
  `aria-live="polite"`. This comes for free from composing `CartesianChart`/`ChartFrame` — nothing
166
- to wire per chart.
182
+ to wire per chart. That's exactly why `ChartFrame`'s outer container uses `role="group"` on its
183
+ `ariaLabel`, **never** `role="img"`: per the ARIA spec every descendant of a `role="img"`
184
+ element is presentational, which would erase the hover overlay's `role="slider"` from the
185
+ accessibility tree entirely — the label still announces, but the keyboard-scrubbable control
186
+ underneath it becomes unreachable, silently, with no error anywhere. jsdom does not implement
187
+ that pruning, so no `getByRole` test can ever catch this regression; it is a structural rule, not
188
+ a test-covered one. Do not "simplify" `ChartFrame`'s container role back to `img` — it looks like
189
+ a no-op refactor and it is not.
167
190
  8. **Theme-aware colors** via `VX.*` tokens + `alpha()`. **Never** a raw hex literal in a chart file.
168
191
  **Never** `localStorage.getItem('theme')` — the scheme resolves via CSS vars (see Dark/light
169
192
  below).
@@ -188,17 +211,28 @@ and its crosshair dots entirely):
188
211
 
189
212
  - `label?: (d: T) => { text; color } | null` — a right-aligned badge in the header (a status label,
190
213
  a zone name).
191
- - `prependRows?: (d: T) => ReactNode` — rows rendered BEFORE the derived per-series rows (a total, a
192
- context line).
193
- - `extraRows?: (d: T) => ReactNode` — rows appended after the derived rows.
214
+ - `prependRows?: (d: T, ctx: { visible; hidden }) => ReactNode` — rows rendered BEFORE the derived
215
+ per-series rows (a total, a context line). `ctx` is the same `visible`/`hidden` the plot itself
216
+ draws from, so a hand-authored row tracks legend toggling instead of desyncing from it.
217
+ - `extraRows?: (d: T, ctx: { visible; hidden }) => ReactNode` — rows appended after the derived
218
+ rows. Same `ctx` as `prependRows`.
194
219
  - `follow?: boolean` — default `true`, the tooltip tracks the pointer. `follow: false` anchors it to
195
220
  the crosshair at the plot's top edge instead, which reads better across a column of charts sharing
196
221
  one cursor (every tooltip lines up on the same x). Anchoring costs one `getBoundingClientRect` per
197
222
  hovered frame, which is why following is the default.
223
+ - `formatHeader?: (key, d) => string` — overrides the tooltip header's date text (also
224
+ `TooltipHeader`'s own `format` prop directly, and the identical seam on `DualPanel`'s
225
+ `formatHeader`, so the two never diverge). Default: today's `fmtTooltipDate` behavior, unchanged.
226
+ The seam exists because `fmtTooltipDate` regexes `YYYY-MM-DD` out of the domain key and builds a
227
+ LOCAL `Date` from it — a UTC ISO key then names a different day than `formatX`, the tooltip badge,
228
+ and every sibling chart, all of which resolve locally; the only prior workaround was carrying a
229
+ local-offset ISO key. Receives the raw `getX` key alongside the hovered datum.
198
230
 
199
231
  Per-series, `SeriesStyle.tooltip: false` means "draw the mark and legend it, but never give it a
200
- tooltip row" — it lives on the series, not on the
201
- kind.
232
+ tooltip row" — it lives on the series, not on the kind. `ChartSeries.formatValue` is
233
+ `(v: number, d: T) => string` — a row can cite the hovered datum, not just the plotted number (e.g.
234
+ `97.5 kg (92.5 × 3)`). `Bars` has its own per-key version of the opt-out: `BarsBar.tooltip: false` /
235
+ `BarsLine.tooltip: false` draws and legends a bar series without ever listing it as a tooltip row.
202
236
 
203
237
  A **stacked** chart needs `cursorValue` too: `CartesianChart`'s crosshair dot defaults to
204
238
  `series.getValue(point)`, which is correct for an unstacked line but puts a stacked band's dot at
@@ -257,10 +291,15 @@ per-series `getValue` accessors on `ChartSeries<T>`):
257
291
  - **`MultiLine`** — N series on a shared y-axis (or two, via `y2`): legend-hover dimming, per-series
258
292
  synced dots, dashed companion (MA) lines, per-point markers (PR stars / status dots), zones +
259
293
  refLines, fixed or auto domain (covers z-score/σ via a fixed symmetric domain + zero refLine).
260
- Composes `CartesianChart`.
294
+ Composes `CartesianChart`. `ChartSeries.getMarker` returns `{ color?, r?, fillOpacity?, ring? }`
295
+ — `ring` defaults `true` (today's punched-out stroke, unchanged), `ring: false` omits the stroke
296
+ entirely, `fillOpacity` defaults `1`. Existed because a consumer's plain `fillOpacity: 0.7` dots
297
+ were unreproducible after moving their chart onto the kind — the old shape had no seam for either.
261
298
  - **`DualPanel`** — top line-pane + bottom signed-histogram pane sharing one x-scale and one cursor;
262
- optional fill-between two top lines, zones, refLines. Composes `ChartFrame` directly (two panes,
263
- not one plot rect).
299
+ optional fill-between two top lines, zones, refLines, per-point markers on the top pane
300
+ (`ChartSeries.getMarker`). Composes `ChartFrame` directly (two panes, not one plot rect). The
301
+ bottom pane's tooltip row is `formatBar` (separate from `formatBottom`'s tick labels); its domain
302
+ is configurable via `bottomYDomain`/`bottomMaxAbsFloor`.
264
303
  - **`Heatmap`** — category×category intensity grid (`color-mix` alpha), per-cell tooltip, optional
265
304
  gradient legend strip. Composes `ChartFrame` directly; self-measures, no separate responsive
266
305
  wrapper.
@@ -269,9 +308,13 @@ per-series `getValue` accessors on `ChartSeries<T>`):
269
308
 
270
309
  `ZonedLine` and `MultiLine` also accept `xZones?: XZoneSpec[]` — the vertical counterpart to a
271
310
  kind's horizontal zone bands, for marking a time window rather than a value range. Each
272
- `{ from?, to?, fill }` bound is a `getX` **domain key** (the label string the kind's
311
+ `{ from?, to?, fill, align? }` bound is a `getX` **domain key** (the label string the kind's
273
312
  `scalePoint<string>` runs over), not a date or timestamp. An omitted bound is the plot edge; a key
274
- absent from the scale's domain skips that band entirely rather than clamping to an edge.
313
+ absent from the scale's domain skips that band entirely rather than clamping to an edge. `align`
314
+ defaults `'center'` (a present bound resolves to the point's own center, today's behaviour);
315
+ `align: 'edge'` widens by half a step at each present bound instead — a two-key band covers both
316
+ terminal slots in full, and `from === to` renders one step wide instead of being skipped as
317
+ degenerate. An edge-aligned bound is clamped into the plot range at the first/last sample.
275
318
 
276
319
  How to add a chart:
277
320
 
@@ -301,15 +344,28 @@ One object per axis on `CartesianChart` and every kind that composes it — this
301
344
  ```tsx
302
345
  y={{
303
346
  domain: 'auto', // 'auto' (default) | [min, max] | (data, visible) => [min, max]
304
- autoMaxFloor: 100, // when 'auto': upper bound is at least this
347
+ autoMaxFloor: 100, // when 'auto': raw upper bound is at least this, clamped BEFORE padding
305
348
  autoMinCeil: 0, // when 'auto': lower bound is at most this (Infinity = pad from raw min)
306
349
  autoPad: 1.1, // when 'auto': padding multiplier away from the data
307
350
  ticks: 5,
308
351
  format: (v) => `${v}%`,
309
352
  grid: true, // horizontal grid rules; default on for `y`, off for `y2`
353
+ nice: false, // default false — opt in to d3's scale.nice() domain rounding
310
354
  }}
311
355
  ```
312
356
 
357
+ **Behavior change (2026-08-19) — the one item in this doc that can move an existing chart's
358
+ rendering:** `autoMaxFloor` now clamps the raw upper bound BEFORE padding, mirroring `autoMinCeil`,
359
+ which has always clamped first and padded second — two different laws used to live in one function.
360
+ The old order padded the raw max, then applied the floor last, so when the floor won it landed
361
+ exactly on the axis top with zero headroom: a target line sitting at precisely the floor value was
362
+ glued to the plot edge. Measured case: dataMax 3.2, pad 1.1, floor 6 → axis top was 6.0, is now
363
+ 6.6. A consumer relying on the old ordering will see their axis top move — lower the floor or pin
364
+ `domain` explicitly to opt back out.
365
+
366
+ `nice` defaults `false` deliberately: flipping the default would move the domain of every
367
+ already-migrated chart, so it's opt-in per axis.
368
+
313
369
  **Passing `y2` is what makes a chart dual-axis** — it draws the right axis and widens the right
314
370
  margin by measurement; nothing else flips it on. A series opts into that axis with `axis: 'right'`
315
371
  on its `ChartSeries` entry (`SeriesStyle.axis`, default `'left'`).
@@ -342,6 +398,11 @@ A series that's invisible in the plot (flat at the domain floor, filtered to zer
342
398
  an explanation: `SeriesStyle.note` (and so `LegendEntry.note`, via `deriveLegend`) renders a short
343
399
  muted qualifier after the legend label — e.g. _"Low cloud — 0% all night"_. Keep it to a clause.
344
400
 
401
+ `SeriesStyle.strokeOpacity` dims the plotted stroke (e.g. a faint moving-average companion) — the
402
+ legend swatch honors it too, parity with `fillOpacity`. The tooltip-row swatch and the crosshair
403
+ dot deliberately do NOT honor it: a sub-1 opacity on a 12px value-readout chip reads as a rendering
404
+ bug, not as data.
405
+
345
406
  ## Dark/light mode
346
407
 
347
408
  Theme reactivity is **pure CSS**: the `--vx-*` variables are redeclared under the light/dark color
@@ -8,12 +8,15 @@ or the `basalt-*` rules instead; manual changes here are overwritten on the next
8
8
 
9
9
  **Stack:** React 19 + Mantine v9, themed by `basalt-ui` (`BasaltProvider` + `createBasaltTheme`).
10
10
  Colors come from the three-tier `--vx-*` token system — read `VX.*` / a `defineSeries` token,
11
- never a raw hex/`rgb()`/`hsl()`. Charts are visx via `basalt-ui/charts` (compose the primitives:
12
- `ChartCard`, `ChartLegend`, the `ChartTooltip` family, `AxisLeftNumeric`/`AxisBottomDate`); add a
13
- kind on the third repeat, don't loosen the primitives. `basalt-ui/charts` and `basalt-ui/tokens`
14
- are Mantine-free internally (a framework invariant, not something your own app code must follow)
15
- — never import `@visx/*` outside a `charts/` directory (oxlint-enforced). Toolchain is oxlint +
16
- oxfmt (no ESLint/Biome/Prettier) and `basalt-ui check-theme` guards the palette. Runtime is Bun.
11
+ never a raw hex/`rgb()`/`hsl()`. Charts are visx via `basalt-ui/charts`: every single-plot chart
12
+ composes `CartesianChart` (owns margins, scales, axes, grid, cursor, tooltip — draw only marks);
13
+ legends/tooltip rows are DERIVED from `series`, never hand-authored (`basalt/hand-rolled-plot` +
14
+ `basalt/chart-legend-literal` enforce both); `DualPanel`/`Donut`/`Heatmap` are the declared
15
+ exceptions. Add a kind on the third repeat, don't loosen the primitives. `basalt-ui/charts` and
16
+ `basalt-ui/tokens` are Mantine-free internally (a framework invariant, not something your own app
17
+ code must follow) — never import `@visx/*` outside a `charts/` directory (oxlint-enforced).
18
+ Toolchain is oxlint + oxfmt (no ESLint/Biome/Prettier) and `basalt-ui check-theme` guards the
19
+ palette. Runtime is Bun.
17
20
 
18
21
  **Before guessing an import, check the installed package's machine docs**:
19
22
  `node_modules/basalt-ui/llms.txt` (per-subpath import map), `node_modules/basalt-ui/AGENTS.md`, or
@@ -13,6 +13,26 @@
13
13
  * since "nearest category" is meaningless.
14
14
  */
15
15
  export declare function parseKey(key: string): number | null;
16
+ /**
17
+ * How a foreign key maps onto a chart's own points.
18
+ *
19
+ * `'nearest'` (default) treats every key as a POINT — the foreign key resolves to whichever own
20
+ * point sits closest to it. Correct when a chart's x-domain is a set of instants.
21
+ *
22
+ * `'leading'` treats every key as the LEADING EDGE of a bucket covering `[key, nextKey)` — the
23
+ * foreign key resolves to the LAST own key `<=` it. Use this when a chart's keys are bucket
24
+ * starts (a weekly series keyed by the Monday of each week, a monthly series keyed by the 1st):
25
+ * under `'nearest'`, a target landing in the back half of a bucket resolves to the FOLLOWING
26
+ * bucket instead of the one it's actually inside, and the shared crosshair lands one column off
27
+ * for every back-half hover.
28
+ *
29
+ * The two modes differ at the DOMAIN EDGES too, and deliberately: `'nearest'` tolerates one step
30
+ * past each end (the target is an instant, so the closest point is still a defensible answer),
31
+ * while `'leading'` bounds strictly to `[first, last + step)` — outside that range no bucket
32
+ * CONTAINS the key, and answering with the nearest one would paint a crosshair on a bucket that
33
+ * provably excludes it.
34
+ */
35
+ export type CursorResolution = 'nearest' | 'leading';
16
36
  export type DomainIndex<T> = {
17
37
  byKey: Map<string, T>;
18
38
  /** Parsed positions, ascending. Empty when the domain isn't parseable. */
@@ -21,13 +41,17 @@ export type DomainIndex<T> = {
21
41
  points: T[];
22
42
  /** Median spacing — the tolerance within which a foreign key still resolves. */
23
43
  step: number;
44
+ /** The resolution strategy this index was built for. */
45
+ resolution: CursorResolution;
24
46
  };
25
47
  /** Build the lookup structure once per `data` identity. */
26
- export declare function buildDomainIndex<T>(data: readonly T[], getKey: (d: T) => string): DomainIndex<T>;
48
+ export declare function buildDomainIndex<T>(data: readonly T[], getKey: (d: T) => string, resolution?: CursorResolution): DomainIndex<T>;
27
49
  /**
28
- * The own point a broadcast `key` maps to: exact match first, else the nearest parsed position
29
- * within one domain step. Out of range → null, so charts over unrelated domains never paint a
30
- * cursor for each other — which is what makes "shared by default" safe.
50
+ * The own point a broadcast `key` maps to: exact match first, else — per `index.resolution` — the
51
+ * nearest parsed position or the last position at/before the target, both within one domain step.
52
+ * Out of range → null, so charts over unrelated domains never paint a cursor for each other —
53
+ * which is what makes "shared by default" safe. A categorical (unparseable) domain falls back to
54
+ * exact-match-only in BOTH resolutions, since "nearest"/"leading" over a category is meaningless.
31
55
  */
32
- export declare function resolveCursorPoint<T>(index: DomainIndex<T>, key: string): T | null;
56
+ export declare function resolveCursorPoint<T>(index: DomainIndex<T>, key: string, resolution?: CursorResolution): T | null;
33
57
  //# sourceMappingURL=resolve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/charts/cursor/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOnD;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IACrB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,mCAAmC;IACnC,MAAM,EAAE,CAAC,EAAE,CAAA;IACX,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CA4BhG;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CA+BlF"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/charts/cursor/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOnD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,CAAA;AAEpD,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IACrB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,mCAAmC;IACnC,MAAM,EAAE,CAAC,EAAE,CAAA;IACX,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,wDAAwD;IACxD,UAAU,EAAE,gBAAgB,CAAA;CAC7B,CAAA;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,IAAI,EAAE,SAAS,CAAC,EAAE,EAClB,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,EACxB,UAAU,GAAE,gBAA4B,GACvC,WAAW,CAAC,CAAC,CAAC,CA8BhB;AAoBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EACrB,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,gBAAmC,GAC9C,CAAC,GAAG,IAAI,CA0CV"}
@@ -6,7 +6,7 @@ function parseKey(key) {
6
6
  const t = Date.parse(key);
7
7
  return Number.isNaN(t) ? null : t;
8
8
  }
9
- function buildDomainIndex(data, getKey) {
9
+ function buildDomainIndex(data, getKey, resolution = "nearest") {
10
10
  const byKey = /* @__PURE__ */ new Map();
11
11
  const parsed = [];
12
12
  let parseable = true;
@@ -17,7 +17,9 @@ function buildDomainIndex(data, getKey) {
17
17
  if (value === null) parseable = false;
18
18
  else parsed.push({ value, point: d });
19
19
  }
20
- if (!parseable || parsed.length < 2) return { byKey, values: [], points: [], step: 0 };
20
+ if (!parseable || parsed.length < 2) {
21
+ return { byKey, values: [], points: [], step: 0, resolution };
22
+ }
21
23
  parsed.sort((a, b) => a.value - b.value);
22
24
  const values = parsed.map((p) => p.value);
23
25
  const points = parsed.map((p) => p.point);
@@ -26,15 +28,34 @@ function buildDomainIndex(data, getKey) {
26
28
  diffs.push(values[i] - values[i - 1]);
27
29
  }
28
30
  diffs.sort((a, b) => a - b);
29
- return { byKey, values, points, step: diffs[Math.floor(diffs.length / 2)] ?? 0 };
31
+ return { byKey, values, points, step: diffs[Math.floor(diffs.length / 2)] ?? 0, resolution };
32
+ }
33
+ function resolveLeading(index, target) {
34
+ let lo = 0;
35
+ let hi = index.values.length - 1;
36
+ let result = 0;
37
+ while (lo <= hi) {
38
+ const mid = lo + hi >> 1;
39
+ if (index.values[mid] <= target) {
40
+ result = mid;
41
+ lo = mid + 1;
42
+ } else {
43
+ hi = mid - 1;
44
+ }
45
+ }
46
+ return index.points[result];
30
47
  }
31
- function resolveCursorPoint(index, key) {
48
+ function resolveCursorPoint(index, key, resolution = index.resolution) {
32
49
  const exact = index.byKey.get(key);
33
50
  if (exact !== void 0) return exact;
34
51
  const target = parseKey(key);
35
52
  if (target === null || index.values.length === 0) return null;
36
53
  const first = index.values[0];
37
54
  const last = index.values[index.values.length - 1];
55
+ if (resolution === "leading") {
56
+ if (target < first || target >= last + index.step) return null;
57
+ return resolveLeading(index, target);
58
+ }
38
59
  if (target < first - index.step || target > last + index.step) return null;
39
60
  let lo = 0;
40
61
  let hi = index.values.length - 1;
@@ -1,4 +1,5 @@
1
1
  import type { KeyboardEvent, PointerEvent } from 'react';
2
+ import type { CursorResolution } from '../cursor/resolve.js';
2
3
  /** Viewport-space pointer anchor the tooltip positions against. */
3
4
  export type CursorAnchor = {
4
5
  x: number;
@@ -28,12 +29,18 @@ export type ChartCursor<T> = {
28
29
  * costs nothing while a real bucket change reaches siblings on the same tick rather than a frame
29
30
  * late.
30
31
  */
31
- export declare function useChartCursor<T>({ data, chartId, getKey, xScale, marginLeft, }: {
32
+ export declare function useChartCursor<T>({ data, chartId, getKey, xScale, marginLeft, resolution, }: {
32
33
  data: readonly T[];
33
34
  chartId: string;
34
35
  getKey: (d: T) => string;
35
36
  /** Own x scale — maps a domain key to a plot-local x offset. */
36
37
  xScale: (key: string) => number | undefined;
37
38
  marginLeft: number;
39
+ /**
40
+ * How a sibling's broadcast key resolves against this chart's own points. Default `'nearest'`
41
+ * (point domains). Pass `'leading'` when `getKey` returns a bucket's leading edge (a weekly
42
+ * series keyed by its Monday, a monthly series keyed by its 1st) — see {@link CursorResolution}.
43
+ */
44
+ resolution?: CursorResolution;
38
45
  }): ChartCursor<T>;
39
46
  //# sourceMappingURL=useChartCursor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useChartCursor.d.ts","sourceRoot":"","sources":["../../../src/charts/hooks/useChartCursor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAIxD,mEAAmE;AACnE,MAAM,MAAM,YAAY,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnD,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,kGAAkG;IAClG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAA;IACf,kGAAkG;IAClG,QAAQ,EAAE,OAAO,CAAA;IACjB,4EAA4E;IAC5E,MAAM,EAAE,YAAY,GAAG,IAAI,CAAA;IAC3B,aAAa,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,cAAc,CAAC,KAAK,IAAI,CAAA;IAC5D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,KAAK,IAAI,CAAA;IACzD,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,EAChC,IAAI,EACJ,OAAO,EACP,MAAM,EACN,MAAM,EACN,UAAU,GACX,EAAE;IACD,IAAI,EAAE,SAAS,CAAC,EAAE,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACxB,gEAAgE;IAChE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IAC3C,UAAU,EAAE,MAAM,CAAA;CACnB,GAAG,WAAW,CAAC,CAAC,CAAC,CAmHjB"}
1
+ {"version":3,"file":"useChartCursor.d.ts","sourceRoot":"","sources":["../../../src/charts/hooks/useChartCursor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAIzD,mEAAmE;AACnE,MAAM,MAAM,YAAY,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnD,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,kGAAkG;IAClG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAA;IACf,kGAAkG;IAClG,QAAQ,EAAE,OAAO,CAAA;IACjB,4EAA4E;IAC5E,MAAM,EAAE,YAAY,GAAG,IAAI,CAAA;IAC3B,aAAa,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,cAAc,CAAC,KAAK,IAAI,CAAA;IAC5D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,KAAK,IAAI,CAAA;IACzD,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,EAChC,IAAI,EACJ,OAAO,EACP,MAAM,EACN,MAAM,EACN,UAAU,EACV,UAAsB,GACvB,EAAE;IACD,IAAI,EAAE,SAAS,CAAC,EAAE,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACxB,gEAAgE;IAChE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IAC3C,UAAU,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAA;CAC9B,GAAG,WAAW,CAAC,CAAC,CAAC,CAsHjB"}
@@ -7,7 +7,8 @@ function useChartCursor({
7
7
  chartId,
8
8
  getKey,
9
9
  xScale,
10
- marginLeft
10
+ marginLeft,
11
+ resolution = "nearest"
11
12
  }) {
12
13
  const store = useCursorStore();
13
14
  const cursor = useCursorState();
@@ -16,7 +17,10 @@ function useChartCursor({
16
17
  getKeyRef.current = getKey;
17
18
  const xScaleRef = useRef(xScale);
18
19
  xScaleRef.current = xScale;
19
- const index = useMemo(() => buildDomainIndex(data, getKeyRef.current), [data]);
20
+ const index = useMemo(
21
+ () => buildDomainIndex(data, getKeyRef.current, resolution),
22
+ [data, resolution]
23
+ );
20
24
  const frameRef = useRef(null);
21
25
  const pendingRef = useRef(null);
22
26
  useEffect(
@@ -16,7 +16,7 @@ export { VxThemeProvider, useVxTheme, type VxTheme } from './theme.js';
16
16
  export { ChartCursorScope, type ChartCursorScopeProps, useCursorState } from './cursor/scope.js';
17
17
  export { createCursorStore, globalCursorStore, type CursorState, type CursorStore, } from './cursor/store.js';
18
18
  export { type SeriesMark, type SeriesDash, type SeriesRole, type LegendPlacement, type ChartLegendConfig, type SeriesStyle, type ChartSeries, deriveLegend, deriveTooltipRows, } from './series.js';
19
- export { CartesianChart, resolveAxisDomain, type CartesianChartProps, type CartesianTooltipConfig, type AxisConfig, type PlotContext, } from './primitives/CartesianChart.js';
19
+ export { CartesianChart, resolveAxisDomain, type CartesianChartProps, type CartesianTooltipConfig, type CartesianTooltipRowContext, type AxisConfig, type PlotContext, } from './primitives/CartesianChart.js';
20
20
  export { ChartFrame, type ChartFrameProps, type ChartFrameLegend, type PlotRect, resolveLegend, } from './primitives/ChartFrame.js';
21
21
  export { ChartCenter, type ChartCenterProps, ChartPending, type ChartPendingProps, } from './primitives/ChartPending.js';
22
22
  export { Crosshair, SeriesDot } from './primitives/Crosshair.js';
@@ -29,6 +29,7 @@ export { ZoneRects, type ZoneSpec } from './primitives/ZoneRects.js';
29
29
  export { XZoneRects, type XZoneSpec } from './primitives/XZoneRects.js';
30
30
  export { AreaGradient, areaFillUrl } from './primitives/AreaGradient.js';
31
31
  export { useChartCursor, type ChartCursor, type CursorAnchor } from './hooks/useChartCursor.js';
32
+ export type { CursorResolution } from './cursor/resolve.js';
32
33
  export { useChartSize, type UseChartSizeResult, type ChartSize } from './hooks/useChartSize.js';
33
34
  export { fmtAxisDate, fmtTooltipDate } from './utils/format.js';
34
35
  export { smartTicks, smartTicksEvery } from './utils/ticks.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/charts/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/charts/index.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,14 @@
1
+ import type { CursorResolution } from '../cursor/resolve.js';
1
2
  import type { ChartMargin } from '../../tokens/index.js';
2
3
  import type { AxisConfig, CartesianTooltipConfig } from '../primitives/CartesianChart.js';
3
4
  import type { ZoneSpec } from '../primitives/ZoneRects.js';
4
5
  import type { ChartLegendConfig } from '../series.js';
5
- export type BarsBar = {
6
+ /**
7
+ * `T = unknown` is load-bearing, not a shortcut: it keeps a consumer's existing
8
+ * `const bars: BarsBar[] = [...]` compiling (and still assignable to `BarsBar<Row>[]`,
9
+ * contravariantly) even though the type is now generic over the datum.
10
+ */
11
+ export type BarsBar<T = unknown> = {
6
12
  /** Field key — `getValue(d, key)` extracts the number (null = skip this slot, not domain hole). */
7
13
  key: string;
8
14
  /** Tooltip + legend label. */
@@ -10,21 +16,29 @@ export type BarsBar = {
10
16
  /** Fill color (a resolved CSS color / token ref). Per `docs/DESIGN-SPEC.md` §5, the primary bar
11
17
  * series should typically resolve to `VX.accent`, a secondary companion to `VX.faint`. */
12
18
  color: string;
13
- /** Per-series tooltip value formatter — overrides the left/right axis's own format. */
14
- formatValue?: (v: number) => string;
19
+ /** Per-series tooltip value formatter — overrides the left/right axis's own format. Receives the
20
+ * hovered datum alongside the value. */
21
+ formatValue?: (v: number, d: T) => string;
15
22
  /** Y axis to plot against. Honored only in grouped layout. Default 'left'. */
16
23
  axisSide?: 'left' | 'right';
17
24
  /** Relative width within the group (only honored in grouped layout). Default 1 — all bars equal. */
18
25
  weight?: number;
26
+ /** Default true; false = drawn and legended, but never listed as a tooltip row — threads
27
+ * `SeriesStyle.tooltip` through `Bars`, replacing the removed per-kind `hideBarTooltipRows`. */
28
+ tooltip?: boolean;
19
29
  };
20
- export type BarsLine = {
30
+ export type BarsLine<T = unknown> = {
21
31
  key: string;
22
32
  label: string;
23
33
  color: string;
24
34
  axisSide?: 'left' | 'right';
25
35
  strokeWidth?: number;
26
36
  dashed?: boolean;
27
- formatValue?: (v: number) => string;
37
+ formatValue?: (v: number, d: T) => string;
38
+ /** Default true; false = drawn and legended, but never listed as a tooltip row. */
39
+ tooltip?: boolean;
40
+ /** Dims the plotted stroke AND the legend swatch, never the tooltip row (`SeriesStyle.strokeOpacity`). */
41
+ strokeOpacity?: number;
28
42
  };
29
43
  /** @deprecated Use ZoneSpec from primitives/ZoneRects. Kept as an alias. */
30
44
  export type BarsZone = ZoneSpec;
@@ -44,11 +58,11 @@ export type BarsProps<T> = {
44
58
  /** Generic value accessor — given a data point and a bar/line key, returns the value or null. */
45
59
  getValue: (d: T, key: string) => number | null;
46
60
  /** 1+ bar series, stacked when ≥2, plotted above baseline (y >= 0). */
47
- positiveBars: BarsBar[];
61
+ positiveBars: BarsBar<T>[];
48
62
  /** Optional bar series stacked below baseline (rendered as flipped negatives). */
49
- negativeBars?: BarsBar[];
63
+ negativeBars?: BarsBar<T>[];
50
64
  /** 0–2 line overlays, each on left or right axis. */
51
- lines?: BarsLine[];
65
+ lines?: BarsLine<T>[];
52
66
  /** Horizontal value-range overlays (target zones, optimal bands). */
53
67
  zones?: BarsZone[];
54
68
  /** Dashed/solid horizontal reference lines. */
@@ -75,6 +89,14 @@ export type BarsProps<T> = {
75
89
  barOpacity?: (d: T, key: string) => number;
76
90
  /** X tick count override. Default: as many as fit. */
77
91
  xTicks?: number;
92
+ /** X tick label formatter. Default `fmtAxisDate` (DD.MM). */
93
+ formatX?: (key: string) => string;
94
+ /**
95
+ * How a sibling chart's broadcast cursor key resolves against this chart's points. Default
96
+ * `'nearest'`. Pass `'leading'` when `getX` returns a bucket's leading edge (a weekly series
97
+ * keyed by its Monday) — see `CursorResolution`.
98
+ */
99
+ cursorResolution?: CursorResolution;
78
100
  /** `false` disables the tooltip entirely (and with it the crosshair dots). */
79
101
  tooltip?: CartesianTooltipConfig<T> | false;
80
102
  /** Per-side overrides of the measured margins — the escape hatch, applied last. */
@@ -1 +1 @@
1
- {"version":3,"file":"Bars.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/Bars.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE/C,OAAO,KAAK,EAAE,UAAU,EAAE,sBAAsB,EAAe,MAAM,8BAA8B,CAAA;AACnG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAEvD,OAAO,KAAK,EAAE,iBAAiB,EAAe,MAAM,WAAW,CAAA;AAG/D,MAAM,MAAM,OAAO,GAAG;IACpB,mGAAmG;IACnG,GAAG,EAAE,MAAM,CAAA;IACX,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb;8FAC0F;IAC1F,KAAK,EAAE,MAAM,CAAA;IACb,uFAAuF;IACvF,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B,oGAAoG;IACpG,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CACpC,CAAA;AAED,4EAA4E;AAC5E,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAA;AAE/B,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,oDAAoD;IACpD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB,iGAAiG;IACjG,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;IAE9C,uEAAuE;IACvE,YAAY,EAAE,OAAO,EAAE,CAAA;IACvB,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,EAAE,CAAA;IAExB,qDAAqD;IACrD,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAA;IAElB,qEAAqE;IACrE,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAA;IAClB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;IAExB;;;oGAGgG;IAChG,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;IACjB;;8BAE0B;IAC1B,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;IAElB,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAEjC,iFAAiF;IACjF,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAE1C,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf,8EAA8E;IAC9E,OAAO,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;IAC3C,mFAAmF;IACnF,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7B;4CACwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gGAAgG;IAChG,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED;;;;;;;;;;;GAWG;AACH,iBAAS,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,+BA4JxC;AA6ND;;;GAGG;AACH,eAAO,MAAM,IAAI,EAAsB,OAAO,SAAS,CAAA"}
1
+ {"version":3,"file":"Bars.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/Bars.tsx"],"names":[],"mappings":"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"}
@@ -24,6 +24,8 @@ function BarsInner(props) {
24
24
  barLayout = "stacked",
25
25
  barOpacity,
26
26
  xTicks,
27
+ formatX,
28
+ cursorResolution,
27
29
  tooltip,
28
30
  margin,
29
31
  legend,
@@ -39,7 +41,8 @@ function BarsInner(props) {
39
41
  fillOpacity: 0.85,
40
42
  ...b.axisSide !== void 0 && { axis: b.axisSide },
41
43
  getValue: (d) => getValue(d, b.key),
42
- ...b.formatValue !== void 0 && { formatValue: b.formatValue }
44
+ ...b.formatValue !== void 0 && { formatValue: b.formatValue },
45
+ ...b.tooltip !== void 0 && { tooltip: b.tooltip }
43
46
  })),
44
47
  [positiveBars, negativeBars, getValue]
45
48
  );
@@ -53,7 +56,9 @@ function BarsInner(props) {
53
56
  strokeWidth: ln.strokeWidth ?? LINE_OVERLAY_STROKE_WIDTH,
54
57
  ...ln.axisSide !== void 0 && { axis: ln.axisSide },
55
58
  getValue: (d) => getValue(d, ln.key),
56
- ...ln.formatValue !== void 0 && { formatValue: ln.formatValue }
59
+ ...ln.formatValue !== void 0 && { formatValue: ln.formatValue },
60
+ ...ln.tooltip !== void 0 && { tooltip: ln.tooltip },
61
+ ...ln.strokeOpacity !== void 0 && { strokeOpacity: ln.strokeOpacity }
57
62
  })),
58
63
  [lines, getValue]
59
64
  );
@@ -123,6 +128,8 @@ function BarsInner(props) {
123
128
  y: yConfig,
124
129
  ...y2 !== void 0 && { y2 },
125
130
  ...xTicks !== void 0 && { xTicks },
131
+ ...formatX !== void 0 && { formatX },
132
+ ...cursorResolution !== void 0 && { cursorResolution },
126
133
  ...zones !== void 0 && { zones },
127
134
  ...mappedRefLines !== void 0 && { refLines: mappedRefLines },
128
135
  ...height !== void 0 && { height },
@@ -294,7 +301,7 @@ function BarsMarks({
294
301
  stroke: ln.color,
295
302
  strokeWidth: ln.strokeWidth ?? LINE_OVERLAY_STROKE_WIDTH,
296
303
  strokeDasharray: ln.dashed ? VX.dashArray : void 0,
297
- strokeOpacity: dim(ln.key),
304
+ strokeOpacity: dim(ln.key) * (ln.strokeOpacity ?? 1),
298
305
  curve: curveMonotoneX
299
306
  },
300
307
  `line-${ln.key}`
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Bars.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Bars.test.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/Bars.test.tsx"],"names":[],"mappings":""}
@@ -1,4 +1,5 @@
1
1
  import type { ZoneSpec } from '../primitives/ZoneRects.js';
2
+ import type { CursorResolution } from '../cursor/resolve.js';
2
3
  import type { ChartLegendConfig, ChartSeries } from '../series.js';
3
4
  export type DualPanelProps<T> = {
4
5
  data: T[];
@@ -6,6 +7,15 @@ export type DualPanelProps<T> = {
6
7
  height?: number;
7
8
  chartId: string;
8
9
  getX: (d: T) => string;
10
+ /** X tick label formatter. Default `fmtAxisDate` (DD.MM) — same default `CartesianChart` uses,
11
+ * so omitting it is byte-identical to today's rendering. */
12
+ formatX?: (key: string) => string;
13
+ /**
14
+ * How a sibling chart's broadcast cursor key resolves against this chart's points. Default
15
+ * `'nearest'`. Pass `'leading'` when `getX` returns a bucket's leading edge (a weekly series
16
+ * keyed by its Monday) — see `CursorResolution`.
17
+ */
18
+ cursorResolution?: CursorResolution;
9
19
  /** 1+ line series in the top pane — the single source of truth for color, dash, legend, and
10
20
  * tooltip rows. E.g. acute (solid) / chronic (dashed). */
11
21
  series: ChartSeries<T>[];
@@ -33,12 +43,32 @@ export type DualPanelProps<T> = {
33
43
  /** Top pane share of the inner plot height. Default 0.62. */
34
44
  topFraction?: number;
35
45
  formatTop: (v: number) => string;
46
+ /** Drives the bottom-pane TICKS. Also the tooltip-row fallback when `formatBar` is omitted. */
36
47
  formatBottom: (v: number) => string;
48
+ /** Per-row tooltip formatter for the bottom-pane bar — overrides `formatBottom` for the tooltip
49
+ * row only (ticks keep reading `formatBottom`). Receives the hovered datum alongside the value,
50
+ * so a row can add a unit/decimal `formatBottom` can't (e.g. a velocity reading). */
51
+ formatBar?: (v: number, d: T) => string;
52
+ /**
53
+ * Bottom-pane y-domain. Default `'auto'`: a symmetric `[-maxAbs, maxAbs]` computed from the
54
+ * data, `nice`d. A fixed tuple pins the domain instead (and is used verbatim, un-`nice`d).
55
+ */
56
+ bottomYDomain?: [number, number] | 'auto';
57
+ /** When `bottomYDomain` is `'auto'`: floors the symmetric max-abs so a plateau doesn't amplify
58
+ * to fill the whole pane (mirrors `AxisConfig.autoMaxFloor`). Ignored with a fixed tuple. */
59
+ bottomMaxAbsFloor?: number;
37
60
  /** Tooltip badge — appears at the right of the tooltip header. */
38
61
  tooltipLabel?: (d: T) => {
39
62
  text: string;
40
63
  color: string;
41
64
  } | null;
65
+ /**
66
+ * Overrides the tooltip header's date text — the same seam `CartesianChart`'s
67
+ * `CartesianTooltipConfig.formatHeader` exposes, so the two don't diverge. Default:
68
+ * `TooltipHeader`'s own `fmtTooltipDate` behavior, unchanged. Receives the raw `getX` key
69
+ * alongside the hovered datum.
70
+ */
71
+ formatHeader?: (key: string, d: T) => string;
42
72
  /** Legend config forwarded to `ChartFrame`; `false` disables the legend (sparkline escape).
43
73
  * Default `{ placement: 'bottom' }`. */
44
74
  legend?: ChartLegendConfig | false;
@@ -1 +1 @@
1
- {"version":3,"file":"DualPanel.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/DualPanel.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAIvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAe,MAAM,WAAW,CAAA;AAK5E,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;8DAC0D;IAC1D,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACxB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACtC,2EAA2E;IAC3E,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACxD,wGAAwG;IACxG,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAClE,oDAAoD;IACpD,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;IACrB,8DAA8D;IAC9D,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAChC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,kEAAkE;IAClE,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC/D;4CACwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAID;;;;;;;;;;;GAWG;AACH,iBAAS,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,+BAmClD;AA4ZD;;;GAGG;AACH,eAAO,MAAM,SAAS,EAA2B,OAAO,cAAc,CAAA"}
1
+ {"version":3,"file":"DualPanel.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/DualPanel.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAIzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAe,MAAM,WAAW,CAAA;AAK5E,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;gEAC4D;IAC5D,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACjC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;8DAC0D;IAC1D,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACxB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACtC,2EAA2E;IAC3E,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACxD,wGAAwG;IACxG,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAClE,oDAAoD;IACpD,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;IACrB,8DAA8D;IAC9D,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAChC,+FAA+F;IAC/F,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC;;yFAEqF;IACrF,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACvC;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACzC;iGAC6F;IAC7F,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,kEAAkE;IAClE,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC/D;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IAC5C;4CACwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAID;;;;;;;;;;;GAWG;AACH,iBAAS,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,+BAmClD;AAifD;;;GAGG;AACH,eAAO,MAAM,SAAS,EAA2B,OAAO,cAAc,CAAA"}