@cascivo/charts 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -7,11 +7,31 @@ interface LinearScale {
7
7
  range: [number, number];
8
8
  map(value: number): number;
9
9
  invert(position: number): number;
10
- ticks(count?: number): number[];
10
+ ticks(count?: number, allowDecimals?: boolean): number[];
11
11
  }
12
12
  declare function linearScale(domain: [number, number], range: [number, number]): LinearScale;
13
- /** Extended nice-numbers: steps are 1, 2, 2.5 or 5 × 10^k covering [min, max]. */
14
- declare function niceTicks(min: number, max: number, count?: number): number[];
13
+ /**
14
+ * Extended nice-numbers: steps are 1, 2, 2.5 or 5 × 10^k covering [min, max].
15
+ *
16
+ * `count` is a **density hint**, not a tick count — the step is snapped to the nearest
17
+ * nice number, so the result may have more or fewer ticks than asked for. That is standard
18
+ * (d3 behaves the same) and is right for continuous data.
19
+ *
20
+ * It is wrong for a whole-number domain. `(max - min) / count` happily produces a
21
+ * *fractional* step when the requested density is finer than the data's own unit:
22
+ * `max=1, count=2` → rawStep 0.5 → `[0, 0.5, 1]`; `max=1, count=5` →
23
+ * `[0, 0.2, 0.4, 0.6, 0.8, 1]`. An incident-count chart with 0–3 incidents per severity
24
+ * hits this constantly, and the obvious workaround (`yTicks={max + 1}`) lands straight on
25
+ * it at `max=1` (2026-07-28 report C17a).
26
+ *
27
+ * So when every bound is an integer, the step is floored at 1 and snapped to an integer
28
+ * nice-number unless `allowDecimals` is explicitly `true`. Callers with genuinely
29
+ * continuous integer-bounded data (a 0–1 ratio axis) opt back in.
30
+ *
31
+ * @param allowDecimals Force fractional steps on (`true`) or off (`false`). Omit to
32
+ * auto-detect: integer bounds get integer ticks, anything else keeps today's behavior.
33
+ */
34
+ declare function niceTicks(min: number, max: number, count?: number, allowDecimals?: boolean): number[];
15
35
  interface BandScale<T extends string = string> {
16
36
  domain: readonly T[];
17
37
  range: [number, number];
@@ -226,7 +246,24 @@ interface BarChartSeries<Datum> {
226
246
  id: string;
227
247
  label: string;
228
248
  data: readonly Datum[];
229
- color?: string;
249
+ /**
250
+ * Bar color. A string colors the whole series; a function colors each bar from its own
251
+ * datum.
252
+ *
253
+ * The per-datum form exists for the common single-series categorical chart whose
254
+ * categories each carry meaning — incidents by severity, where SEV1 should read as
255
+ * danger and SEV4 as neutral regardless of which bar is tallest. Before it, the only
256
+ * route was one single-point series per category with `mode="grouped"`, which renders
257
+ * *wrong*: the bars overlap and only the first series' category label survives
258
+ * (2026-07-28 report C18).
259
+ *
260
+ * ```tsx
261
+ * series={[{ id: 'count', label: 'Incidents', data, color: (d) => SEVERITY_COLOR[d.x] }]}
262
+ * ```
263
+ *
264
+ * Each bar is also stamped with `data-x`, so CSS can target one category directly.
265
+ */
266
+ color?: string | ((datum: Datum, index: number) => string);
230
267
  /**
231
268
  * Per-series Y accessor. Overrides the chart-level `y` for this series only —
232
269
  * use it to plot two series from one shared `data` row against different fields
@@ -267,16 +304,63 @@ interface BarChartProps<Datum = {
267
304
  */
268
305
  width?: number;
269
306
  height?: number;
307
+ /**
308
+ * Approximate number of ticks on the x-axis.
309
+ *
310
+ * ⚠ **Follows SCREEN position, so its meaning swaps with `orientation`.** On a vertical
311
+ * chart the x-axis is the category axis; on a horizontal one it is the VALUE axis. Prefer
312
+ * {@link BarChartProps.valueAxisTicks} / {@link BarChartProps.categoryAxisTicks}, which
313
+ * name the axis by role and never swap.
314
+ *
315
+ * @defaultValue `5`
316
+ * @deprecated Use `valueAxisTicks` / `categoryAxisTicks`.
317
+ */
270
318
  xTicks?: number;
271
319
  /**
272
320
  * Approximate number of ticks on the y-axis.
273
321
  *
322
+ * ⚠ **Follows SCREEN position, so its meaning swaps with `orientation`** — see
323
+ * {@link BarChartProps.xTicks}.
324
+ *
274
325
  * @defaultValue `5`
275
- * @see the component manifest
326
+ * @deprecated Use `valueAxisTicks` / `categoryAxisTicks`.
276
327
  */
277
328
  yTicks?: number;
278
- /** Show every Nth category label (and always the last) to thin a crowded x-axis. */
329
+ /**
330
+ * Approximate number of ticks on the **value** axis, whichever way the chart is turned.
331
+ *
332
+ * This is the prop you want. `xTicks`/`yTicks` are named for where the axis is *drawn*,
333
+ * so on `orientation="horizontal"` the value axis moves from screen-y to screen-x and the
334
+ * controlling prop moves with it — `yTicks={1}` silently does nothing while `xTicks={1}`
335
+ * works. Meanwhile `xLabelEvery` does NOT swap: it always strides the category axis. Two
336
+ * conventions in one component, with nothing in the types to say so (2026-07-28 report
337
+ * C17b). Wins over `xTicks`/`yTicks` when both are given.
338
+ *
339
+ * @defaultValue `5`
340
+ */
341
+ valueAxisTicks?: number;
342
+ /**
343
+ * Approximate number of ticks on the CATEGORY axis, on both orientations. Role-named twin
344
+ * of valueAxisTicks.
345
+ *
346
+ * @defaultValue `5`
347
+ * @see the component manifest
348
+ */
349
+ categoryAxisTicks?: number;
350
+ /**
351
+ * Show every Nth category label (and always the last) to thin a crowded axis.
352
+ *
353
+ * Always strides the **category** axis (the `x` field of each datum), on both
354
+ * orientations — unlike `xTicks`/`yTicks`, which follow screen position.
355
+ * {@link BarChartProps.categoryLabelEvery} is the unambiguous name; this is kept for
356
+ * compatibility.
357
+ */
279
358
  xLabelEvery?: number;
359
+ /**
360
+ * Show every Nth category label (and always the last). Role-named twin of
361
+ * `xLabelEvery`; wins when both are given.
362
+ */
363
+ categoryLabelEvery?: number;
280
364
  legend?: boolean;
281
365
  tooltip?: boolean;
282
366
  /** Custom tooltip formatter. Stacked default lists "label · total" + per-layer values. */
@@ -307,6 +391,14 @@ interface BarChartProps<Datum = {
307
391
  fill?: FillKind;
308
392
  /** Pattern motif when `fill="pattern"`. */
309
393
  patternKind?: PatternKind;
394
+ /**
395
+ * Format each category/x-axis tick label. Receives the datum's raw `x` value — a number,
396
+ * a string, or a `Date`, whichever the series carries.
397
+ *
398
+ * Threads through `Axis`'s own `format`, which every chart composing an axis should
399
+ * surface (2026-07-28 report C16); enforced by `axis-parity.test.ts`.
400
+ */
401
+ format?: (value: number | string | Date) => string;
310
402
  }
311
403
  declare function BarChart<Datum = {
312
404
  x: string;
@@ -323,7 +415,10 @@ declare function BarChart<Datum = {
323
415
  height,
324
416
  xTicks,
325
417
  yTicks,
418
+ valueAxisTicks,
419
+ categoryAxisTicks,
326
420
  xLabelEvery,
421
+ categoryLabelEvery,
327
422
  legend,
328
423
  tooltip,
329
424
  tooltipFormat,
@@ -333,7 +428,8 @@ declare function BarChart<Datum = {
333
428
  labels,
334
429
  onSelect,
335
430
  fill,
336
- patternKind
431
+ patternKind,
432
+ format: xFormat
337
433
  }: BarChartProps<Datum>): import("react").JSX.Element;
338
434
  /** One layer of a stacked bar at a category. */
339
435
  interface StackedSegment {
@@ -1290,6 +1386,25 @@ interface LineChartProps<Datum = {
1290
1386
  * @see the component manifest
1291
1387
  */
1292
1388
  yTicks?: number;
1389
+ /**
1390
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a
1391
+ * string, or a `Date`, whichever the series carries.
1392
+ *
1393
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for
1394
+ * a time series) renders as `1,785,217,000,000`. Passing `Date` objects instead switches
1395
+ * the axis to a time scale, but that format is fixed, so every bucket narrower than a day
1396
+ * collapses to the same label — worse than the epoch number, which at least differed
1397
+ * between buckets. This threads through `Axis`'s existing `format`, mirroring what
1398
+ * `secondAxis.format` already offers on the right (2026-07-28 report C16).
1399
+ *
1400
+ * ```tsx
1401
+ * <LineChart
1402
+ * series={series}
1403
+ * format={(x) => new Date(Number(x)).toLocaleTimeString([], { timeStyle: 'short' })}
1404
+ * />
1405
+ * ```
1406
+ */
1407
+ format?: (value: number | string | Date) => string;
1293
1408
  legend?: boolean;
1294
1409
  tooltip?: boolean;
1295
1410
  formatTooltip?: (datum: Datum, series: LineChartSeries<Datum>) => string;
@@ -1386,6 +1501,7 @@ declare function LineChart<Datum = {
1386
1501
  height,
1387
1502
  xTicks,
1388
1503
  yTicks,
1504
+ format: xFormat,
1389
1505
  legend,
1390
1506
  tooltip,
1391
1507
  formatTooltip,
@@ -1543,6 +1659,16 @@ interface AreaChartProps<Datum = {
1543
1659
  decimate?: boolean | AreaDecimateOptions;
1544
1660
  /** Render a toolbox (PNG/SVG export, data-view toggle, restore). `true` enables all tools. */
1545
1661
  toolbox?: boolean | ToolboxOptions;
1662
+ /**
1663
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
1664
+ * or a `Date`, whichever the series carries.
1665
+ *
1666
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
1667
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
1668
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
1669
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
1670
+ */
1671
+ format?: (value: number | string | Date) => string;
1546
1672
  }
1547
1673
  declare function AreaChart<Datum = {
1548
1674
  x: number;
@@ -1575,7 +1701,8 @@ declare function AreaChart<Datum = {
1575
1701
  tooltipMode,
1576
1702
  secondAxis,
1577
1703
  decimate,
1578
- toolbox
1704
+ toolbox,
1705
+ format: xFormat
1579
1706
  }: AreaChartProps<Datum>): import("react").JSX.Element;
1580
1707
  interface PieChartDatum {
1581
1708
  id: string;
@@ -1714,6 +1841,16 @@ interface ScatterChartProps {
1714
1841
  visualMap?: VisualMapOptions;
1715
1842
  /** Render a toolbox (PNG/SVG export, data-view toggle, restore). `true` enables all tools. */
1716
1843
  toolbox?: boolean | ToolboxOptions;
1844
+ /**
1845
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
1846
+ * or a `Date`, whichever the series carries.
1847
+ *
1848
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
1849
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
1850
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
1851
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
1852
+ */
1853
+ format?: (value: number | string | Date) => string;
1717
1854
  }
1718
1855
  declare function ScatterChart({
1719
1856
  series,
@@ -1733,7 +1870,8 @@ declare function ScatterChart({
1733
1870
  glyph,
1734
1871
  renderer,
1735
1872
  visualMap,
1736
- toolbox
1873
+ toolbox,
1874
+ format
1737
1875
  }: ScatterChartProps): import("react").JSX.Element;
1738
1876
  interface SparklineBaseProps {
1739
1877
  data: readonly number[];
@@ -1879,6 +2017,16 @@ interface HistogramProps {
1879
2017
  * @see the component manifest
1880
2018
  */
1881
2019
  plain?: boolean;
2020
+ /**
2021
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
2022
+ * or a `Date`, whichever the series carries.
2023
+ *
2024
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
2025
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
2026
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
2027
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
2028
+ */
2029
+ format?: (value: number | string | Date) => string;
1882
2030
  }
1883
2031
  declare function Histogram({
1884
2032
  data,
@@ -1889,7 +2037,8 @@ declare function Histogram({
1889
2037
  width: fixedWidth,
1890
2038
  height,
1891
2039
  className,
1892
- plain
2040
+ plain,
2041
+ format
1893
2042
  }: HistogramProps): import("react").JSX.Element;
1894
2043
  interface BoxplotSeries {
1895
2044
  id: string;
@@ -1918,6 +2067,14 @@ interface BoxplotProps {
1918
2067
  * @see the component manifest
1919
2068
  */
1920
2069
  plain?: boolean;
2070
+ /**
2071
+ * Format each category/x-axis tick label. Receives the datum's raw `x` value — a number,
2072
+ * a string, or a `Date`, whichever the series carries.
2073
+ *
2074
+ * Threads through `Axis`'s own `format`, which every chart composing an axis should
2075
+ * surface (2026-07-28 report C16); enforced by `axis-parity.test.ts`.
2076
+ */
2077
+ format?: (value: number | string | Date) => string;
1921
2078
  }
1922
2079
  declare function Boxplot({
1923
2080
  series,
@@ -1926,7 +2083,8 @@ declare function Boxplot({
1926
2083
  width: fixedWidth,
1927
2084
  height,
1928
2085
  className,
1929
- plain
2086
+ plain,
2087
+ format
1930
2088
  }: BoxplotProps): import("react").JSX.Element;
1931
2089
  interface BubbleDatum {
1932
2090
  x: number;
@@ -1962,6 +2120,16 @@ interface BubbleChartProps {
1962
2120
  plain?: boolean;
1963
2121
  /** Point glyph shape — a fixed shape, or a function to encode a category by shape. Defaults to a circle. */
1964
2122
  glyph?: GlyphShape | ((d: BubbleDatum, seriesName: string) => GlyphShape);
2123
+ /**
2124
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
2125
+ * or a `Date`, whichever the series carries.
2126
+ *
2127
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
2128
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
2129
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
2130
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
2131
+ */
2132
+ format?: (value: number | string | Date) => string;
1965
2133
  }
1966
2134
  declare function BubbleChart({
1967
2135
  series,
@@ -1972,7 +2140,8 @@ declare function BubbleChart({
1972
2140
  tooltip,
1973
2141
  className,
1974
2142
  plain,
1975
- glyph
2143
+ glyph,
2144
+ format
1976
2145
  }: BubbleChartProps): import("react").JSX.Element;
1977
2146
  interface ComboChartBar {
1978
2147
  label: string;
@@ -2032,6 +2201,16 @@ interface ComboChartProps {
2032
2201
  plain?: boolean;
2033
2202
  /** Reference lines, bands, and markers. `y` maps to the bar value axis. */
2034
2203
  annotations?: readonly Annotation[];
2204
+ /**
2205
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
2206
+ * or a `Date`, whichever the series carries.
2207
+ *
2208
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
2209
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
2210
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
2211
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
2212
+ */
2213
+ format?: (value: number | string | Date) => string;
2035
2214
  }
2036
2215
  declare function ComboChart({
2037
2216
  bars,
@@ -2048,7 +2227,8 @@ declare function ComboChart({
2048
2227
  tooltip,
2049
2228
  className,
2050
2229
  plain,
2051
- annotations
2230
+ annotations,
2231
+ format
2052
2232
  }: ComboChartProps): import("react").JSX.Element;
2053
2233
  interface HeatmapDatum {
2054
2234
  x: string;
@@ -2081,6 +2261,14 @@ interface HeatmapProps {
2081
2261
  visualMap?: VisualMapOptions;
2082
2262
  /** Render a toolbox (PNG/SVG export, data-view toggle, restore). `true` enables all tools. */
2083
2263
  toolbox?: boolean | ToolboxOptions;
2264
+ /**
2265
+ * Format each category/x-axis tick label. Receives the datum's raw `x` value — a number,
2266
+ * a string, or a `Date`, whichever the series carries.
2267
+ *
2268
+ * Threads through `Axis`'s own `format`, which every chart composing an axis should
2269
+ * surface (2026-07-28 report C16); enforced by `axis-parity.test.ts`.
2270
+ */
2271
+ format?: (value: number | string | Date) => string;
2084
2272
  }
2085
2273
  declare function Heatmap({
2086
2274
  data,
@@ -2091,7 +2279,8 @@ declare function Heatmap({
2091
2279
  className,
2092
2280
  plain,
2093
2281
  visualMap,
2094
- toolbox
2282
+ toolbox,
2283
+ format
2095
2284
  }: HeatmapProps): import("react").JSX.Element;
2096
2285
  interface TreemapDatum {
2097
2286
  id: string;
@@ -2380,6 +2569,14 @@ interface StreamProps {
2380
2569
  * @see the component manifest
2381
2570
  */
2382
2571
  plain?: boolean;
2572
+ /**
2573
+ * Format each category/x-axis tick label. Receives the datum's raw `x` value — a number,
2574
+ * a string, or a `Date`, whichever the series carries.
2575
+ *
2576
+ * Threads through `Axis`'s own `format`, which every chart composing an axis should
2577
+ * surface (2026-07-28 report C16); enforced by `axis-parity.test.ts`.
2578
+ */
2579
+ format?: (value: number | string | Date) => string;
2383
2580
  }
2384
2581
  /** A streamgraph — stacked areas on a flowing (centered) baseline. */
2385
2582
  declare function Stream({
@@ -2394,7 +2591,8 @@ declare function Stream({
2394
2591
  legend,
2395
2592
  tooltip,
2396
2593
  className,
2397
- plain
2594
+ plain,
2595
+ format
2398
2596
  }: StreamProps): import("react").JSX.Element;
2399
2597
  interface SunburstProps {
2400
2598
  /** Root of the hierarchy. Leaves carry `value`; parents sum their children. */
@@ -2586,6 +2784,16 @@ interface CandlestickProps {
2586
2784
  * @see the component manifest
2587
2785
  */
2588
2786
  tooltipMode?: 'item' | 'axis';
2787
+ /**
2788
+ * Format each X-axis tick label. Receives the datum's raw `x` value — a number, a string,
2789
+ * or a `Date`, whichever the series carries.
2790
+ *
2791
+ * Without it a numeric x renders raw: a `Date.now()`-scale value (the natural shape for a
2792
+ * time series) renders as `1,785,217,000,000`. Passing `Date` objects switches the axis to
2793
+ * a time scale, but that format is fixed, so every bucket narrower than a day collapses to
2794
+ * the same label. Threads through `Axis`'s own `format` (2026-07-28 report C16).
2795
+ */
2796
+ format?: (value: number | string | Date) => string;
2589
2797
  }
2590
2798
  declare function Candlestick({
2591
2799
  data: rawData,
@@ -2605,7 +2813,8 @@ declare function Candlestick({
2605
2813
  dataZoom,
2606
2814
  zoom,
2607
2815
  syncId,
2608
- tooltipMode
2816
+ tooltipMode,
2817
+ format: xFormat
2609
2818
  }: CandlestickProps): import("react").JSX.Element;
2610
2819
  interface PolarDatum {
2611
2820
  label: string;