@umriss-ui/charts 0.42.0 → 0.43.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/docs/llms-full.md CHANGED
@@ -1,4 +1,4 @@
1
- # @umriss-ui/charts 0.42.0
1
+ # @umriss-ui/charts 0.43.0
2
2
 
3
3
  > React canvas chart library for data-dense dashboards - line, area, bar, box plot, scatter, control and Pareto charts. Few chart kinds, drawn well, fast.
4
4
 
@@ -181,7 +181,7 @@ export default function WatchLatency() {
181
181
  }
182
182
  />
183
183
  <CardBody>
184
- <Chart height={260} syncId="checkout" ariaLabel="Checkout latency today against its objective">
184
+ <Chart height={260} syncId="checkout" aria-label="Checkout latency today against its objective">
185
185
  <XAxis value="t" time domain={TODAY} />
186
186
  {/* Tick labels as wide as the error rate's keep the two crosshairs in one column. */}
187
187
  <YAxis label="ms" />
@@ -196,7 +196,7 @@ export default function WatchLatency() {
196
196
  <Card data-callout="4">
197
197
  <CardHeader title={`${CHECKOUT.name} failed requests`} />
198
198
  <CardBody>
199
- <ErrorChart height={140} syncId="checkout" ariaLabel="Checkout error rate today">
199
+ <ErrorChart height={140} syncId="checkout" aria-label="Checkout error rate today">
200
200
  <XAxis value="t" time domain={TODAY} />
201
201
  <YAxis label="%" tickCount={3} tickFormat={(v) => v.toFixed(1)} />
202
202
  <LimitLine value={ERROR_ALERT} severity="alarm" label={`Alert at ${ERROR_ALERT} %`} inExtent />
@@ -408,7 +408,7 @@ const DRIVING_NOW = FLEET.filter((one) => one.driving).length;
408
408
  function FleetLanes() {
409
409
  const { Chart, XAxis, YAxis, StateBand } = useChart(FLEET[0]!.points);
410
410
  return (
411
- <Chart height={320} ariaLabel="What each vehicle has done today, lane by lane">
411
+ <Chart height={320} aria-label="What each vehicle has done today, lane by lane">
412
412
  <XAxis value="t" time domain={DAY} />
413
413
  <YAxis grid={false} />
414
414
  {FLEET.map((one) => (
@@ -430,7 +430,7 @@ function FleetLanes() {
430
430
  function IdleMinutes() {
431
431
  const { Chart, XAxis, YAxis, Bar } = useChart(FLEET);
432
432
  return (
433
- <Chart height={200} ariaLabel="Idle minutes per vehicle so far today">
433
+ <Chart height={200} aria-label="Idle minutes per vehicle so far today">
434
434
  <XAxis value={(d) => d.vehicle.plate} />
435
435
  <YAxis label="min" tickCount={4} />
436
436
  <Bar value="idle" name="Idle" color={VEHICLE_STATES[IDLE]!.color} barWidth={0.6} />
@@ -534,7 +534,7 @@ const monthName = (month: string) => new Date(`${month}-01T00:00`).toLocaleStrin
534
534
  function GapPerCentre() {
535
535
  const { Chart, XAxis, YAxis, Bar } = useChart(YEAR);
536
536
  return (
537
- <Chart height={240} ariaLabel="Outlook minus budget for the year, per cost centre">
537
+ <Chart height={240} aria-label="Outlook minus budget for the year, per cost centre">
538
538
  <XAxis value="name" />
539
539
  <YAxis label="k€" tickFormat={euros} />
540
540
  {/* One series per sign, stacked on one another so that each bar stands centred on its cost centre. */}
@@ -555,7 +555,7 @@ function GapPerCentre() {
555
555
  function MarketingByMonth() {
556
556
  const { Chart, XAxis, YAxis, Bar, Line } = useChart(MONTHLY);
557
557
  return (
558
- <Chart height={260} ariaLabel="Marketing's budget, actual and forecast per month">
558
+ <Chart height={260} aria-label="Marketing's budget, actual and forecast per month">
559
559
  <XAxis value="month" categoryLabel={monthName} />
560
560
  <YAxis label="k€" tickFormat={euros} />
561
561
  <Bar value={(d) => thousands(d.budget)} name="Budget" color="var(--uc-color-text)" barWidth={0.7} format={euros} />
@@ -733,7 +733,7 @@ const HOLDERS: readonly Holder[] = PEOPLE.map((person) => ({
733
733
  function SprintBurnDown() {
734
734
  const { Chart, XAxis, YAxis, Line } = useChart(BURNDOWN);
735
735
  return (
736
- <Chart height={280} ariaLabel={`${SPRINT.name}: hours left per working day against an even pace`}>
736
+ <Chart height={280} aria-label={`${SPRINT.name}: hours left per working day against an even pace`}>
737
737
  <XAxis value={dayOf} label="Working day" />
738
738
  <YAxis label="Hours left" domain={[0, BURNDOWN[0]!.ideal]} />
739
739
  <Line value="ideal" name="Even pace" color="var(--uc-color-text)" dash={[4, 4]} strokeWidth={1} />
@@ -747,7 +747,7 @@ function SprintBurnDown() {
747
747
  function OpenPerPerson() {
748
748
  const { Chart, XAxis, YAxis, Bar } = useChart(HOLDERS);
749
749
  return (
750
- <Chart height={200} ariaLabel="Estimated hours of unfinished items per person">
750
+ <Chart height={200} aria-label="Estimated hours of unfinished items per person">
751
751
  <XAxis value="name" />
752
752
  <YAxis label="h" tickCount={4} />
753
753
  <Bar value="open" name="Estimate" barWidth={0.6} />
@@ -974,7 +974,7 @@ const clock = (minute: number) => {
974
974
  function LengthInControl() {
975
975
  const { Chart, XAxis, YAxis } = useChart(SHIFT.samples);
976
976
  return (
977
- <Chart height={280} syncId="tiles" ariaLabel="Control chart of the tile length over the early shift">
977
+ <Chart height={280} syncId="tiles" aria-label="Control chart of the tile length over the early shift">
978
978
  <XAxis value="minute" domain={DOMAIN} ticks={HOURS} tickFormat={clock} />
979
979
  <YAxis label="mm" />
980
980
  <LimitLine value={SPEC.nominal + SPEC.tolerance} severity="alarm" label="USL" inExtent />
@@ -996,7 +996,7 @@ function LengthInControl() {
996
996
  function KilnZone() {
997
997
  const { Chart, XAxis, YAxis, Line } = useChart(SHIFT.readings);
998
998
  return (
999
- <Chart height={200} syncId="tiles" ariaLabel="Kiln zone 3 temperature over the early shift">
999
+ <Chart height={200} syncId="tiles" aria-label="Kiln zone 3 temperature over the early shift">
1000
1000
  <XAxis value="minute" domain={DOMAIN} ticks={HOURS} tickFormat={clock} />
1001
1001
  <YAxis label="°C" tickCount={4} />
1002
1002
  <LimitBand from={KILN.tolerance[0]} to={KILN.tolerance[1]} severity="warning" label="Tolerance" />
@@ -1090,7 +1090,7 @@ const SIGN_INS = [
1090
1090
  export default function FirstChart() {
1091
1091
  const { Chart, XAxis, YAxis, Line } = useChart(SIGN_INS);
1092
1092
  return (
1093
- <Chart height={240} ariaLabel="Sign-ins per hour today">
1093
+ <Chart height={240} aria-label="Sign-ins per hour today">
1094
1094
  <XAxis value="t" time />
1095
1095
  <YAxis label="Sign-ins per hour" />
1096
1096
  <Line value="signIns" name="Sign-ins" />
@@ -1196,7 +1196,7 @@ const SIGN_IN = metrics("sign-in");
1196
1196
  function Requests({ width, height }: { width?: number; height: number }) {
1197
1197
  const { Chart, XAxis, YAxis, Line } = useChart(SIGN_IN);
1198
1198
  return (
1199
- <Chart width={width} height={height} ariaLabel="Sign-in requests per minute today">
1199
+ <Chart width={width} height={height} aria-label="Sign-in requests per minute today">
1200
1200
  <XAxis value="t" time />
1201
1201
  <YAxis />
1202
1202
  <Line value="requests" name="Requests per minute" />
@@ -1242,12 +1242,6 @@ From `ChartProps` — the full table stands on [Chart](#chart-1).
1242
1242
  | [`height`](#chart-1) | `number` | fills the frame, at least 300 px | Height in CSS pixels, fixed. Left out, the chart fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 300 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). |
1243
1243
  | [`children`](#chart-1) | `ReactNode` | — | The axes, series and companions of this chart. They draw nothing themselves: each registers its configuration and the scene draws it, and the order in the JSX is the drawing order. |
1244
1244
 
1245
- ###### Accessibility
1246
-
1247
- | Prop | Type | Default | Description |
1248
- |---|---|---|---|
1249
- | [`ariaLabel`](#chart-1) | `string` | — | Required in DEV through a warning. |
1250
-
1251
1245
  From `XAxisProps` — the full table stands on [Axis](#axis).
1252
1246
 
1253
1247
  | Prop | Type | Default | Description |
@@ -1345,7 +1339,7 @@ const DEPOT_DAYS: readonly DepotDay[] = (() => {
1345
1339
  export default function Mixed() {
1346
1340
  const { Chart, XAxis, YAxis, Bar, Area, Line, Scatter } = useChart(DEPOT_DAYS);
1347
1341
  return (
1348
- <Chart height={320} ariaLabel="North depot: pallets in, out and on hand over fourteen days">
1342
+ <Chart height={320} aria-label="North depot: pallets in, out and on hand over fourteen days">
1349
1343
  <XAxis value="day" label="Day of March" tickCount={7} />
1350
1344
  <YAxis label="Pallets" />
1351
1345
  <Bar value="arrived" name="Arrived" />
@@ -1377,7 +1371,7 @@ const NOTHING: Reading[] = [];
1377
1371
  function Latency({ empty }: { empty?: string }) {
1378
1372
  const { Chart, XAxis, YAxis, Line } = useChart(NOTHING);
1379
1373
  return (
1380
- <Chart height={220} ariaLabel="Latency of a service without readings" empty={empty}>
1374
+ <Chart height={220} aria-label="Latency of a service without readings" empty={empty}>
1381
1375
  <XAxis value="t" time />
1382
1376
  <YAxis label="ms" />
1383
1377
  <Line value="p95" name="Refunds" />
@@ -1515,14 +1509,14 @@ export default function KeyboardAndScreenReader() {
1515
1509
  const { Chart: Diagramm } = german;
1516
1510
  return (
1517
1511
  <div>
1518
- <Chart height={200} ariaLabel="Search latency over last week">
1512
+ <Chart height={200} aria-label="Search latency over last week">
1519
1513
  <XAxis value="t" time domain="data" zoomable />
1520
1514
  <YAxis label="ms" />
1521
1515
  <Line value="p95" name="p95" />
1522
1516
  <Line value="p50" name="p50" />
1523
1517
  <Tooltip mode="x" />
1524
1518
  </Chart>
1525
- <Diagramm height={200} ariaLabel="Antwortzeiten der Suche in der letzten Woche" wording={GERMAN_CHARTS_WORDING}>
1519
+ <Diagramm height={200} aria-label="Antwortzeiten der Suche in der letzten Woche" wording={GERMAN_CHARTS_WORDING}>
1526
1520
  <XAxis value="t" time domain="data" zoomable />
1527
1521
  <YAxis label="ms" />
1528
1522
  <Line value="p95" name="p95" />
@@ -1619,7 +1613,7 @@ const monthName = (month: string) => MONTH_NAMES[Number(month.slice(5)) - 1] ??
1619
1613
  function Forecasts({ encoding }: { encoding?: "marks" }) {
1620
1614
  const { Chart, XAxis, YAxis, Line } = useChart(LEDGER);
1621
1615
  return (
1622
- <Chart height={240} ariaLabel="Forecast of four cost centres per month" encoding={encoding}>
1616
+ <Chart height={240} aria-label="Forecast of four cost centres per month" encoding={encoding}>
1623
1617
  <XAxis value="month" categoryLabel={monthName} />
1624
1618
  <YAxis label="€" />
1625
1619
  {CENTRES.map((c) => (
@@ -1710,7 +1704,7 @@ const DEPOT_DAYS: readonly DepotDay[] = (() => {
1710
1704
  export default function MarksOnEveryKind() {
1711
1705
  const { Chart, XAxis, YAxis, Bar, Area, Line, Scatter } = useChart(DEPOT_DAYS);
1712
1706
  return (
1713
- <Chart height={320} ariaLabel="North depot's pallets, told apart by marks" encoding="marks">
1707
+ <Chart height={320} aria-label="North depot's pallets, told apart by marks" encoding="marks">
1714
1708
  <XAxis value="day" label="Day of March" tickCount={7} />
1715
1709
  <YAxis label="Pallets" />
1716
1710
  <Bar value="arrived" name="Arrived" />
@@ -1964,7 +1958,7 @@ const STATES = vehicleDay("v2");
1964
1958
  function BatteryAndStates() {
1965
1959
  const { Chart, XAxis, YAxis, Line, StateBand } = useChart(BATTERY);
1966
1960
  return (
1967
- <Chart height={280} ariaLabel="An e-van's battery against its reserve, above its states" encoding="marks">
1961
+ <Chart height={280} aria-label="An e-van's battery against its reserve, above its states" encoding="marks">
1968
1962
  <XAxis value="t" time label="Time" tickCount={4} />
1969
1963
  <YAxis domain={[-40, 100]} ticks={[0, 20, 40, 60, 80, 100]} label="%" />
1970
1964
  {/* One lane, and four places of room above it for the course. */}
@@ -1982,7 +1976,7 @@ function BatteryAndStates() {
1982
1976
  function SuccessCells() {
1983
1977
  const { Chart, XAxis, YAxis, Matrix } = useChart(SUCCESS_BY_HOUR);
1984
1978
  return (
1985
- <Chart height={280} ariaLabel="Successful requests per service and hour, by limits and by marks" encoding="marks">
1979
+ <Chart height={280} aria-label="Successful requests per service and hour, by limits and by marks" encoding="marks">
1986
1980
  <XAxis value="hour" ticks={[0, 6, 12, 18]} tickFormat={(v) => `${v}:00`} label="Hour" />
1987
1981
  <YAxis />
1988
1982
  <Matrix
@@ -2044,7 +2038,7 @@ const NOTHING: Reading[] = [];
2044
2038
  function Latency({ rows, loading, label }: { rows: Reading[]; loading: boolean; label: string }) {
2045
2039
  const { Chart, XAxis, YAxis, Line } = useChart(rows);
2046
2040
  return (
2047
- <Chart height={220} ariaLabel={label} loading={loading}>
2041
+ <Chart height={220} aria-label={label} loading={loading}>
2048
2042
  <XAxis value="t" time />
2049
2043
  <YAxis label="ms" />
2050
2044
  <Line value="p95" name="Refunds" />
@@ -2127,7 +2121,7 @@ const BY_SERVICE = [...new Set(ERRORS.map((row) => row.service))].map((service)
2127
2121
  export default function SeriesFromLongRows() {
2128
2122
  const { Chart, XAxis, YAxis, Line } = useChart(ERRORS);
2129
2123
  return (
2130
- <Chart height={260} ariaLabel="Failed requests per service and hour this morning">
2124
+ <Chart height={260} aria-label="Failed requests per service and hour this morning">
2131
2125
  <XAxis value="t" time />
2132
2126
  <YAxis label="Errors %" />
2133
2127
  {BY_SERVICE.map(({ service, rows }) => (
@@ -2161,7 +2155,7 @@ export default function SeriesFromLongRows() {
2161
2155
 
2162
2156
  #### Accessibility
2163
2157
 
2164
- The plot is an image named by `ariaLabel` and described by a summary: the series, the visible stretch and each series' lowest and highest value in it. With a `Tooltip` it becomes one tab stop, the role `application` read as "chart", and the summary adds the keys. Pass `ariaLabel`: without it the plot has no name, and the development build warns.
2158
+ The plot is an image named by `aria-label` and described by a summary: the series, the visible stretch and each series' lowest and highest value in it. With a `Tooltip` it becomes one tab stop, the role `application` read as "chart", and the summary adds the keys. Pass `aria-label` (or `aria-labelledby`): without it the plot has no name, and the development build warns.
2165
2159
 
2166
2160
  When a key comes to rest, a polite live region beside the plot reads the active position and every series' value there, as the tooltip shows it; a pointer announces nothing. A reader who wants every value at once opens the `DataTable` (on [Tooltip](#tooltip)), a plain table of what the chart shows.
2167
2161
 
@@ -2173,15 +2167,16 @@ Under forced colours the chart draws in the system colours and tells its series
2173
2167
 
2174
2168
  | Prop | Type | Default | Description |
2175
2169
  |---|---|---|---|
2170
+ | `ref` | `Ref<HTMLDivElement>` | — | The root element, the `<div>` that `className`, `style`, `id` and the `data-*` attributes reach. |
2176
2171
  | `width` | `number \| "100%"` | `"100%"` | Width in CSS pixels, or `"100%"` for the host's: the chart measures its host and follows it through a ResizeObserver.<br>Shown in: [Size a chart by its container (Installation)](#installation) |
2177
- | `height` | `number` | fills the frame, at least 300 px | Height in CSS pixels, fixed. Left out, the chart fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 300 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)).<br>Shown in: [Mix series kinds](#chart-1), [Say there is nothing to show](#chart-1), [Read it by keyboard and screen reader](#chart-1) and 74 more |
2172
+ | `height` | `number` | fills the frame, at least 300 px | Height in CSS pixels, fixed. Left out, the chart fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 300 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)).<br>Shown in: [Mix series kinds](#chart-1), [Say there is nothing to show](#chart-1), [Read it by keyboard and screen reader](#chart-1) and 75 more |
2178
2173
  | `padding` | `number \| Partial<``Padding``>` | — | Outer spacing of the plot area in CSS pixels.<br>Shown in: [Fix the domain and name the ticks (Axis)](#axis) |
2179
2174
  | `empty` | `ReactNode` | the wording's `empty` | Shown in the middle of the plot area when no visible series has a point to show - no data, only gaps, or every series hidden. Axes and frame stay. Not while `loading`.<br>Shown in: [Say there is nothing to show](#chart-1) |
2180
2175
  | `loading` | `boolean` | `false` | The rows are on their way, as the table's `loading` says ([ADR-0042](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0042-a-rows-height-never-follows-what-it-shows.md)). A course already drawn stays: it dims after a moment and takes no pointer until the answer is in. Where there is nothing to show, the empty message waits. The plot is `aria-busy` meanwhile.<br>Shown in: [Show that rows are on their way](#chart-1) |
2181
2176
  | `wording` | `Partial<``ChartsWording``>` | `DEFAULT_CHARTS_WORDING` | The chart's words ([ADR-0031](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0031-the-charts-carry-their-own-wording.md)); entries left out fall back to English. Its `locale` also formats every number and every date or time label the chart makes itself (`en-GB`, `de-DE`; the browser's language is not read) - a `tickFormat` or `format` of your own stays in charge of its axis or series. German: `GERMAN_CHARTS_WORDING` from `@umriss-ui/charts/wording/de`.<br>Shown in: [Read it by keyboard and screen reader](#chart-1) |
2182
2177
  | `encoding` | `"color" \| "marks"` | `"color"` | How series are told apart. `"color"`: by their colour. `"marks"`: by a dash and a marker shape as well - a line, an area and a scatter -, and by a hatch - a bar, a state, a matrix cell, a limit band -, chosen by the same palette place as the colour, so that a reader who does not see the colours still tells them apart; the legend's chips show the same. A caller's own `dash` wins over the place's.<br>Shown in: [Tell series apart without colour](#chart-1), [Mark every kind](#chart-1), [Mark bands, limits and cells](#chart-1) |
2183
2178
  | `syncId` | `string` | — | Charts with the same id share the pointer's x position, in domain units: each draws its crosshair there, the tooltip stays with the chart under the pointer. Zoom is not shared this way: charts zoom together by sending each other their view (`useChart`'s `onViewChange` and `setView`).<br>Shown in: [Keep charts in step (Zoom and pan)](#zoom-and-pan), [Watch latency against its objective (Scenarios)](#watch-latency-against-its-objective), [Hold the tile length in control (Scenarios)](#hold-the-tile-length-in-control) |
2184
- | `children` | `ReactNode` | — | The axes, series and companions of this chart. They draw nothing themselves: each registers its configuration and the scene draws it, and the order in the JSX is the drawing order.<br>Shown in: [Mix series kinds](#chart-1), [Say there is nothing to show](#chart-1), [Read it by keyboard and screen reader](#chart-1) and 74 more |
2179
+ | `children` | `ReactNode` | — | The axes, series and companions of this chart. They draw nothing themselves: each registers its configuration and the scene draws it, and the order in the JSX is the drawing order.<br>Shown in: [Mix series kinds](#chart-1), [Say there is nothing to show](#chart-1), [Read it by keyboard and screen reader](#chart-1) and 75 more |
2185
2180
 
2186
2181
  ###### Events
2187
2182
 
@@ -2193,7 +2188,7 @@ Under forced colours the chart draws in the system colours and tells its series
2193
2188
 
2194
2189
  | Prop | Type | Default | Description |
2195
2190
  |---|---|---|---|
2196
- | `ariaLabel` | `string` | — | Required in DEV through a warning.<br>Shown in: [Mix series kinds](#chart-1), [Say there is nothing to show](#chart-1), [Read it by keyboard and screen reader](#chart-1) and 74 more |
2191
+ | `ariaLabel` | `string` | — | *Deprecated* since 0.43 - use `aria-label`. Accessible name of the plot. Without a name, `aria-label` or `aria-labelledby`, the chart warns in development. |
2197
2192
 
2198
2193
  ###### Styling
2199
2194
 
@@ -2202,6 +2197,8 @@ Under forced colours the chart draws in the system colours and tells its series
2202
2197
  | `className` | `string` | — | Goes to the root element, as it does everywhere in this workspace. |
2203
2198
  | `style` | `CSSProperties` | — | Goes to the root element, after the chart's own styles: a `height` or `minHeight` set here wins. The plot area is observed in both directions and the scene follows it. |
2204
2199
 
2200
+ Also takes every attribute of `<div>` – without `children`.
2201
+
2205
2202
  ##### Types on this page
2206
2203
 
2207
2204
  ###### `Padding`
@@ -2267,9 +2264,9 @@ Measurements of the instrumentation for the benchmark page.
2267
2264
  #### Known limits
2268
2265
 
2269
2266
  - No error state of its own: where the rows could not be loaded, show an `Alert` from @umriss-ui/core. Waiting for them is `loading`.
2270
- - No pie, donut, radar or candlestick, no smoothing, animation or export, no WebGL ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.42.0/docs/adr/0032-what-umriss-is-not.md)).
2267
+ - No pie, donut, radar or candlestick, no smoothing, animation or export, no WebGL ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.43.0/docs/adr/0032-what-umriss-is-not.md)).
2271
2268
 
2272
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
2269
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
2273
2270
 
2274
2271
  ### Axis
2275
2272
 
@@ -2334,7 +2331,7 @@ function WeekTooltip({ hit }: { hit: TooltipHit<Week> }) {
2334
2331
  export default function Configuration() {
2335
2332
  const { Chart, XAxis, YAxis, Line } = useChart(BOOKED);
2336
2333
  return (
2337
- <Chart height={280} padding={{ top: 12, right: 16, bottom: 8, left: 8 }} ariaLabel="Share of capacity booked per team and week">
2334
+ <Chart height={280} padding={{ top: 12, right: 16, bottom: 8, left: 8 }} aria-label="Share of capacity booked per team and week">
2338
2335
  <XAxis value="week" label="Week" tickCount={6} />
2339
2336
  <YAxis domain={[0, 100]} tickFormat={percent} label="Booked" />
2340
2337
  <Line value="web" name="Web" markers="always" strokeWidth={2} />
@@ -2393,7 +2390,7 @@ const LOAD_TEST: readonly LoadTestPoint[] = (() => {
2393
2390
  export default function Axes() {
2394
2391
  const { Chart, XAxis, YAxis, Line } = useChart(LOAD_TEST);
2395
2392
  return (
2396
- <Chart height={340} ariaLabel="A load test: CPU, throughput and latency on three y axes">
2393
+ <Chart height={340} aria-label="A load test: CPU, throughput and latency on three y axes">
2397
2394
  <XAxis value="minute" label="Minute of the test" />
2398
2395
  <XAxis id="seconds" position="top" value={(d) => d.minute * 60} label="Seconds into the test" />
2399
2396
  <YAxis id="cpu" position="left" label="CPU %" />
@@ -2461,7 +2458,7 @@ const weekdayAndTime = (v: number) =>
2461
2458
  export default function WorkingTime() {
2462
2459
  const { Chart, XAxis, YAxis, Line } = useChart(SORTED);
2463
2460
  return (
2464
- <Chart height={280} ariaLabel="Parcels sorted per hour across last week's sorting hours">
2461
+ <Chart height={280} aria-label="Parcels sorted per hour across last week's sorting hours">
2465
2462
  <XAxis
2466
2463
  value="t"
2467
2464
  calendar={SORTING_HOURS}
@@ -2572,7 +2569,7 @@ const CHECKOUT = metrics("checkout");
2572
2569
  export default function TimeAxis() {
2573
2570
  const { Chart, XAxis, YAxis, Line } = useChart(CHECKOUT);
2574
2571
  return (
2575
- <Chart height={260} ariaLabel="Checkout's error rate today">
2572
+ <Chart height={260} aria-label="Checkout's error rate today">
2576
2573
  <XAxis value="t" />
2577
2574
  <YAxis label="Errors %" />
2578
2575
  <Line value="errorRate" name="Error rate" />
@@ -2690,7 +2687,7 @@ const SEARCH_WEEK = week("search", 5 * 60_000);
2690
2687
  export default function AlignedTicks() {
2691
2688
  const { Chart, XAxis, YAxis, Line } = useChart(SEARCH_WEEK);
2692
2689
  return (
2693
- <Chart height={260} ariaLabel="Search requests and latency over last week on one grid">
2690
+ <Chart height={260} aria-label="Search requests and latency over last week on one grid">
2694
2691
  <XAxis value="t" time />
2695
2692
  <YAxis label="Requests/min" />
2696
2693
  <YAxis id="latency" position="right" label="ms" alignTicks />
@@ -2873,7 +2870,7 @@ const HOURS = hourly(SHIFT);
2873
2870
  export default function EveryKind() {
2874
2871
  const { Chart, XAxis, YAxis, Line, Area, Bar, Scatter } = useChart(SHIFT.readings);
2875
2872
  return (
2876
- <Chart height={340} ariaLabel="The kiln's temperature by the minute above the tiles it fired by the hour">
2873
+ <Chart height={340} aria-label="The kiln's temperature by the minute above the tiles it fired by the hour">
2877
2874
  <XAxis value="minute" domain={[0, SHIFT_MINUTES]} label="Minute of the shift" />
2878
2875
  {/* This axis reads the hourly rows its series bring, so it names their type. */}
2879
2876
  <XAxis<HourCount>
@@ -2921,7 +2918,7 @@ const NAMES: Record<string, string> = { mon: "Monday", tue: "Tuesday", wed: "Wed
2921
2918
  export default function Categories() {
2922
2919
  const { Chart, XAxis, YAxis, Bar } = useChart(LATE);
2923
2920
  return (
2924
- <Chart height={260} ariaLabel="Minutes late per working day">
2921
+ <Chart height={260} aria-label="Minutes late per working day">
2925
2922
  <XAxis value="day" categories={WEEK} categoryLabel={(day) => NAMES[day] ?? day} />
2926
2923
  <YAxis label="Minutes late" />
2927
2924
  <Bar value="minutes" name="Late" />
@@ -2941,7 +2938,7 @@ The keys of [Chart](#chart-1) apply here.
2941
2938
 
2942
2939
  | Prop | Type | Default | Description |
2943
2940
  |---|---|---|---|
2944
- | `value` *required* | `AxisField``<T> \| ((d: T, index: number) => ``AxisValue``)` | — | Where a row lies on this axis: a field of the row, compared by its name, or a function of it, compared by its source text as a series' is (`ValueFunction`) - with the same closure limit. A number is a position, a `Date` an instant, a string a category: every distinct one gets a place of its own, in the order the rows bring them, a tick and its name as the label ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)). The type decides, never the content - `"2024"` is a category, `2024` a number - and a row without a value has no place. The axis reads the rows of every series bound to it; where those bring their own `data`, name their row: `<XAxis<HourCount> value="hour" />`.<br>Shown in: [Fix the domain and name the ticks](#axis), [Add axes for other magnitudes](#axis), [Leave out the hours nobody works](#axis) and 74 more |
2941
+ | `value` *required* | `AxisField``<T> \| ((d: T, index: number) => ``AxisValue``)` | — | Where a row lies on this axis: a field of the row, compared by its name, or a function of it, compared by its source text as a series' is (`ValueFunction`) - with the same closure limit. A number is a position, a `Date` an instant, a string a category: every distinct one gets a place of its own, in the order the rows bring them, a tick and its name as the label ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)). The type decides, never the content - `"2024"` is a category, `2024` a number - and a row without a value has no place. The axis reads the rows of every series bound to it; where those bring their own `data`, name their row: `<XAxis<HourCount> value="hour" />`.<br>Shown in: [Fix the domain and name the ticks](#axis), [Add axes for other magnitudes](#axis), [Leave out the hours nobody works](#axis) and 75 more |
2945
2942
  | `domain` | `"nice" \| "data" \| readonly [number, number] \| readonly [Date, Date]` | `"nice"` | `"nice"` widens the data's extent to ticks - to named `ticks` where there are any, on a time axis to whole units of a step of hours or longer -, `"data"` keeps it, a pair is fixed: numbers, on a time axis milliseconds or two `Date`s, on a category axis places. On a `zoomable` axis it is the start: what the axis shows while the chart's view names no span for it.<br>Shown in: [Bind every kind of series to a second axis](#axis), [Read it by keyboard and screen reader (Chart)](#chart-1), [Gestures and keys (Zoom and pan)](#zoom-and-pan) and 11 more |
2946
2943
  | `position` | `"bottom" \| "top"` | `"bottom"` | Which edge the axis stands at. A second x axis on the opposite edge is how a series counts in a unit of its own.<br>Shown in: [Add axes for other magnitudes](#axis), [Bind every kind of series to a second axis](#axis) |
2947
2944
  | `time` | `boolean` | — | The values are instants: milliseconds since the epoch, or with `time` said ISO 8601 strings as an API sends them, read by the platform's rule - a date with a time and no offset is local time, a date alone is UTC; a string that is none is not drawn. Rows that hold a `Date` put the axis in time without `time`. Ticks on local boundaries from the minute to the month, labels by level in en-GB with a 24-hour clock - `15:00`, `17 Mar`, `Mar 2026` - and the date on the first tick and on the first of a new day (`17 Mar 00:00`); a day tick there carries the year. The tooltip's x value carries the seconds where the readings lie less than a minute apart. Another language is a `tickFormat`, which is handed the instant in milliseconds. `calendar` implies it.<br>Shown in: [Align a second axis to the grid](#axis), [Draw a first chart (Installation)](#installation), [Size a chart by its container (Installation)](#installation) and 36 more |
@@ -2949,9 +2946,9 @@ The keys of [Chart](#chart-1) apply here.
2949
2946
  | `zoomable` | `boolean` | `false` | Zoom and pan: Ctrl or ⌘ with the wheel - and a pinch - zoom around the pointer, a drag, a horizontal wheel or Shift with the wheel pan, a double click goes back to `domain`; with the focus on the chart, + and − zoom, Shift with ← or → pans and 0 goes back. The span shown is part of the chart's view, by this axis' `id` (`useChart`'s `initialView`, `onViewChange`, `setView`, `setDomain`). The plain wheel always scrolls the page.<br>Shown in: [Read it by keyboard and screen reader (Chart)](#chart-1), [Gestures and keys (Zoom and pan)](#zoom-and-pan), [Limit the zoom (Zoom and pan)](#zoom-and-pan) and 6 more |
2950
2947
  | `zoomLimits` | `ZoomLimits` | at most the data's extent or this axis' own `domain`, whichever is wider, at least three data steps | The narrowest and the widest span zoom may reach, in the axis' units - milliseconds on a time axis.<br>Shown in: [Limit the zoom (Zoom and pan)](#zoom-and-pan) |
2951
2948
  | `id` | `string` | `"x"` | Axis id, through which series bind themselves.<br>Shown in: [Add axes for other magnitudes](#axis), [Align a second axis to the grid](#axis), [Bind every kind of series to a second axis](#axis) and 7 more |
2952
- | `label` | `string` | — | Axis title.<br>Shown in: [Fix the domain and name the ticks](#axis), [Add axes for other magnitudes](#axis), [Leave out the hours nobody works](#axis) and 66 more |
2949
+ | `label` | `string` | — | Axis title.<br>Shown in: [Fix the domain and name the ticks](#axis), [Add axes for other magnitudes](#axis), [Leave out the hours nobody works](#axis) and 67 more |
2953
2950
  | `tickCount` | `number` | — | Target value; the 1-2-5 algorithm may deviate.<br>Shown in: [Fix the domain and name the ticks](#axis), [Leave out the hours nobody works](#axis), [Mix series kinds (Chart)](#chart-1) and 8 more |
2954
- | `tickFormat` | `(v: number) => string` | — | Label of a tick. Compared by its source text: one that reads a changed closure variable under the same text does not relabel the axis - give it a new text or remount. A bound `Intl.NumberFormat#format` is compared by identity.<br>Shown in: [Fix the domain and name the ticks](#axis), [Leave out the hours nobody works](#axis), [Bind every kind of series to a second axis](#axis) and 21 more |
2951
+ | `tickFormat` | `ValueFormat` | — | Label of a tick. Compared by its source text: one that reads a changed closure variable under the same text does not relabel the axis - give it a new text or remount. A bound `Intl.NumberFormat#format` is compared by identity. Or `NumberOptions`: Intl's options in the wording's locale, compared by value; the step's places stay unless the options set digits.<br>Shown in: [Fix the domain and name the ticks](#axis), [Leave out the hours nobody works](#axis), [Bind every kind of series to a second axis](#axis) and 21 more |
2955
2952
  | `grid` | `boolean` | only on the first axis of its orientation | Grid lines.<br>Shown in: [One vehicle through the day (StateBand)](#stateband), [Give each one a lane (StateBand)](#stateband), [See where the vans stand idle (Scenarios)](#see-where-the-vans-stand-idle) |
2956
2953
  | `ticks` | `readonly number[]` | — | Fixed tick values instead of the 1-2-5 algorithm, on a numeric axis: an hour axis' named hours, a lane grid. An axis that reads categories needs none - every category has its tick.<br>Shown in: [Bind every kind of series to a second axis](#axis), [Mark bands, limits and cells (Chart)](#chart-1), [Compare progress with a plan (Line)](#line) and 7 more |
2957
2954
  | `categories` | `readonly string[]` | — | The categories of the axis, in their order. Without it they stand in the order the rows first bring them. With it the list is the order and the extent: a category no row carries keeps its empty place, and a row whose category is not listed is not drawn - DEV names it. It makes the axis a category axis whatever the rows hold ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)).<br>Shown in: [Fix the order and keep an empty place](#axis) |
@@ -2965,9 +2962,9 @@ The keys of [Chart](#chart-1) apply here.
2965
2962
  | `alignTicks` | `boolean` | — | On a further y axis: take the first y axis' tick count and widen this axis' domain until its ticks fall on that one's grid lines, the steps still 1-2-5 - one grid serves both. `tickCount` then does not apply; on the first y axis it changes nothing.<br>Shown in: [Align a second axis to the grid](#axis) |
2966
2963
  | `position` | `"left" \| "right"` | `"left"` | Which edge the axis stands at. Several y axes per side are stacked outwards, so that extents of clearly different magnitude stay readable.<br>Shown in: [Add axes for other magnitudes](#axis), [Align a second axis to the grid](#axis), [Bind every kind of series to a second axis](#axis) and 7 more |
2967
2964
  | `id` | `string` | `"y"` | Axis id, through which series bind themselves.<br>Shown in: [Add axes for other magnitudes](#axis), [Align a second axis to the grid](#axis), [Bind every kind of series to a second axis](#axis) and 7 more |
2968
- | `label` | `string` | — | Axis title.<br>Shown in: [Fix the domain and name the ticks](#axis), [Add axes for other magnitudes](#axis), [Leave out the hours nobody works](#axis) and 66 more |
2965
+ | `label` | `string` | — | Axis title.<br>Shown in: [Fix the domain and name the ticks](#axis), [Add axes for other magnitudes](#axis), [Leave out the hours nobody works](#axis) and 67 more |
2969
2966
  | `tickCount` | `number` | — | Target value; the 1-2-5 algorithm may deviate.<br>Shown in: [Fix the domain and name the ticks](#axis), [Leave out the hours nobody works](#axis), [Mix series kinds (Chart)](#chart-1) and 8 more |
2970
- | `tickFormat` | `(v: number) => string` | — | Label of a tick. Compared by its source text: one that reads a changed closure variable under the same text does not relabel the axis - give it a new text or remount. A bound `Intl.NumberFormat#format` is compared by identity.<br>Shown in: [Fix the domain and name the ticks](#axis), [Leave out the hours nobody works](#axis), [Bind every kind of series to a second axis](#axis) and 21 more |
2967
+ | `tickFormat` | `ValueFormat` | — | Label of a tick. Compared by its source text: one that reads a changed closure variable under the same text does not relabel the axis - give it a new text or remount. A bound `Intl.NumberFormat#format` is compared by identity. Or `NumberOptions`: Intl's options in the wording's locale, compared by value; the step's places stay unless the options set digits.<br>Shown in: [Fix the domain and name the ticks](#axis), [Leave out the hours nobody works](#axis), [Bind every kind of series to a second axis](#axis) and 21 more |
2971
2968
  | `grid` | `boolean` | only on the first axis of its orientation | Grid lines.<br>Shown in: [One vehicle through the day (StateBand)](#stateband), [Give each one a lane (StateBand)](#stateband), [See where the vans stand idle (Scenarios)](#see-where-the-vans-stand-idle) |
2972
2969
  | `ticks` | `readonly number[]` | — | Fixed tick values instead of the 1-2-5 algorithm, on a numeric axis: an hour axis' named hours, a lane grid. An axis that reads categories needs none - every category has its tick.<br>Shown in: [Bind every kind of series to a second axis](#axis), [Mark bands, limits and cells (Chart)](#chart-1), [Compare progress with a plan (Line)](#line) and 7 more |
2973
2970
  | `categories` | `readonly string[]` | — | The categories of the axis, in their order. Without it they stand in the order the rows first bring them. With it the list is the order and the extent: a category no row carries keeps its empty place, and a row whose category is not listed is not drawn - DEV names it. It makes the axis a category axis whatever the rows hold ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)).<br>Shown in: [Fix the order and keep an empty place](#axis) |
@@ -3022,12 +3019,32 @@ schedule's shape.
3022
3019
  | `min` *required* | `number` | — | <br>Shown in: [Limit the zoom (Zoom and pan)](#zoom-and-pan) |
3023
3020
  | `max` *required* | `number` | — | <br>Shown in: [Limit the zoom (Zoom and pan)](#zoom-and-pan) |
3024
3021
 
3022
+ ###### `ValueFormat`
3023
+
3024
+ A value format: a function, or Intl's options written in the chart's
3025
+ wording's locale.
3026
+
3027
+ ```ts
3028
+ type ValueFormat = ((value: number) => string) | NumberOptions;
3029
+ ```
3030
+
3031
+ ###### `NumberOptions`
3032
+
3033
+ The options of `Intl.NumberFormat`, plus `decimals` for a fixed number of
3034
+ places (minimum and maximum alike; an Intl digit option given beside it
3035
+ wins). The same shape as core's, declared here because the charts import
3036
+ nothing.
3037
+
3038
+ | Prop | Type | Default | Description |
3039
+ |---|---|---|---|
3040
+ | `decimals` | `number` | — | |
3041
+
3025
3042
  #### Known limits
3026
3043
 
3027
- - No logarithmic and no band scale: compute the logarithm in `value`; a category is a place on the linear scale ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.42.0/docs/adr/0032-what-umriss-is-not.md), [ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)).
3044
+ - No logarithmic and no band scale: compute the logarithm in `value`; a category is a place on the linear scale ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.43.0/docs/adr/0032-what-umriss-is-not.md), [ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)).
3028
3045
  - umriss shows the reader's time ([ADR-0060](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0060-validation-and-a-layout-editor-are-never-time-zones-not-yet.md)).
3029
3046
 
3030
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
3047
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
3031
3048
 
3032
3049
  ### Zoom and pan
3033
3050
 
@@ -3155,7 +3172,7 @@ const SEARCH_WEEK = week("search", 60_000);
3155
3172
  export default function GesturesAndKeys() {
3156
3173
  const { Chart, XAxis, YAxis, Line } = useChart(SEARCH_WEEK);
3157
3174
  return (
3158
- <Chart height={260} ariaLabel="Search latency over last week, a reading a minute, zoomable">
3175
+ <Chart height={260} aria-label="Search latency over last week, a reading a minute, zoomable">
3159
3176
  {/* The span shown is the chart's own: `domain` is where it starts. */}
3160
3177
  <XAxis value="t" time domain="data" zoomable />
3161
3178
  <YAxis label="ms" />
@@ -3274,7 +3291,7 @@ const SEARCH_WEEK = week("search", 60_000);
3274
3291
  export default function ZoomLimits() {
3275
3292
  const { Chart, XAxis, YAxis, Line } = useChart(SEARCH_WEEK);
3276
3293
  return (
3277
- <Chart height={260} ariaLabel="Search latency over last week, zoomable between an hour and two days">
3294
+ <Chart height={260} aria-label="Search latency over last week, zoomable between an hour and two days">
3278
3295
  {/* Never closer than an hour, never wider than two days - the week
3279
3296
  shows at the start and once more after a double click. */}
3280
3297
  <XAxis value="t" time domain="data" zoomable zoomLimits={{ min: HOUR, max: 2 * DAY }} />
@@ -3398,7 +3415,7 @@ export default function VisibleDomain() {
3398
3415
  initialView: { domains: { x: [SATURDAY + 16 * HOUR, SATURDAY + 22 * HOUR] } },
3399
3416
  });
3400
3417
  return (
3401
- <Chart height={260} ariaLabel="Search latency on Saturday evening, the y axis fitted to it">
3418
+ <Chart height={260} aria-label="Search latency on Saturday evening, the y axis fitted to it">
3402
3419
  <XAxis value="t" time domain="data" zoomable />
3403
3420
  <YAxis label="ms" domain="visible" />
3404
3421
  <Line value="p95" name="p95" />
@@ -3545,7 +3562,7 @@ function Channel({ channel: c, peers }: { channel: (typeof CHANNELS)[number]; pe
3545
3562
  return () => void peers.delete(c.name);
3546
3563
  }, [peers, c.name, setView]);
3547
3564
  return (
3548
- <Chart height={140} syncId="images" ariaLabel={`Image service ${c.name} over last week`}>
3565
+ <Chart height={140} syncId="images" aria-label={`Image service ${c.name} over last week`}>
3549
3566
  <XAxis value="t" time domain="data" zoomable />
3550
3567
  <YAxis label={c.unit} domain="visible" tickCount={4} />
3551
3568
  <Line value={c.value} name={c.name} />
@@ -3565,7 +3582,7 @@ The keys of [Chart](#chart-1) apply here.
3565
3582
 
3566
3583
  | Prop | Type | Default | Description |
3567
3584
  |---|---|---|---|
3568
- | `value` *required* | `AxisField``<T> \| ((d: T, index: number) => ``AxisValue``)` | — | Where a row lies on this axis: a field of the row, compared by its name, or a function of it, compared by its source text as a series' is (`ValueFunction`) - with the same closure limit. A number is a position, a `Date` an instant, a string a category: every distinct one gets a place of its own, in the order the rows bring them, a tick and its name as the label ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)). The type decides, never the content - `"2024"` is a category, `2024` a number - and a row without a value has no place. The axis reads the rows of every series bound to it; where those bring their own `data`, name their row: `<XAxis<HourCount> value="hour" />`.<br>Shown in: [Gestures and keys](#zoom-and-pan), [Limit the zoom](#zoom-and-pan), [Fit the y axis to what is visible](#zoom-and-pan) and 74 more |
3585
+ | `value` *required* | `AxisField``<T> \| ((d: T, index: number) => ``AxisValue``)` | — | Where a row lies on this axis: a field of the row, compared by its name, or a function of it, compared by its source text as a series' is (`ValueFunction`) - with the same closure limit. A number is a position, a `Date` an instant, a string a category: every distinct one gets a place of its own, in the order the rows bring them, a tick and its name as the label ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)). The type decides, never the content - `"2024"` is a category, `2024` a number - and a row without a value has no place. The axis reads the rows of every series bound to it; where those bring their own `data`, name their row: `<XAxis<HourCount> value="hour" />`.<br>Shown in: [Gestures and keys](#zoom-and-pan), [Limit the zoom](#zoom-and-pan), [Fit the y axis to what is visible](#zoom-and-pan) and 75 more |
3569
3586
  | `domain` | `"nice" \| "data" \| readonly [number, number] \| readonly [Date, Date]` | `"nice"` | `"nice"` widens the data's extent to ticks - to named `ticks` where there are any, on a time axis to whole units of a step of hours or longer -, `"data"` keeps it, a pair is fixed: numbers, on a time axis milliseconds or two `Date`s, on a category axis places. On a `zoomable` axis it is the start: what the axis shows while the chart's view names no span for it.<br>Shown in: [Gestures and keys](#zoom-and-pan), [Limit the zoom](#zoom-and-pan), [Fit the y axis to what is visible](#zoom-and-pan) and 11 more |
3570
3587
  | `position` | `"bottom" \| "top"` | `"bottom"` | Which edge the axis stands at. A second x axis on the opposite edge is how a series counts in a unit of its own.<br>Shown in: [Add axes for other magnitudes (Axis)](#axis), [Bind every kind of series to a second axis (Axis)](#axis) |
3571
3588
  | `time` | `boolean` | — | The values are instants: milliseconds since the epoch, or with `time` said ISO 8601 strings as an API sends them, read by the platform's rule - a date with a time and no offset is local time, a date alone is UTC; a string that is none is not drawn. Rows that hold a `Date` put the axis in time without `time`. Ticks on local boundaries from the minute to the month, labels by level in en-GB with a 24-hour clock - `15:00`, `17 Mar`, `Mar 2026` - and the date on the first tick and on the first of a new day (`17 Mar 00:00`); a day tick there carries the year. The tooltip's x value carries the seconds where the readings lie less than a minute apart. Another language is a `tickFormat`, which is handed the instant in milliseconds. `calendar` implies it.<br>Shown in: [Gestures and keys](#zoom-and-pan), [Limit the zoom](#zoom-and-pan), [Fit the y axis to what is visible](#zoom-and-pan) and 36 more |
@@ -3573,9 +3590,9 @@ The keys of [Chart](#chart-1) apply here.
3573
3590
  | `zoomable` | `boolean` | `false` | Zoom and pan: Ctrl or ⌘ with the wheel - and a pinch - zoom around the pointer, a drag, a horizontal wheel or Shift with the wheel pan, a double click goes back to `domain`; with the focus on the chart, + and − zoom, Shift with ← or → pans and 0 goes back. The span shown is part of the chart's view, by this axis' `id` (`useChart`'s `initialView`, `onViewChange`, `setView`, `setDomain`). The plain wheel always scrolls the page.<br>Shown in: [Gestures and keys](#zoom-and-pan), [Limit the zoom](#zoom-and-pan), [Fit the y axis to what is visible](#zoom-and-pan) and 6 more |
3574
3591
  | `zoomLimits` | `ZoomLimits` | at most the data's extent or this axis' own `domain`, whichever is wider, at least three data steps | The narrowest and the widest span zoom may reach, in the axis' units - milliseconds on a time axis.<br>Shown in: [Limit the zoom](#zoom-and-pan) |
3575
3592
  | `id` | `string` | `"x"` | Axis id, through which series bind themselves.<br>Shown in: [Add axes for other magnitudes (Axis)](#axis), [Align a second axis to the grid (Axis)](#axis), [Bind every kind of series to a second axis (Axis)](#axis) and 7 more |
3576
- | `label` | `string` | — | Axis title.<br>Shown in: [Gestures and keys](#zoom-and-pan), [Limit the zoom](#zoom-and-pan), [Fit the y axis to what is visible](#zoom-and-pan) and 66 more |
3593
+ | `label` | `string` | — | Axis title.<br>Shown in: [Gestures and keys](#zoom-and-pan), [Limit the zoom](#zoom-and-pan), [Fit the y axis to what is visible](#zoom-and-pan) and 67 more |
3577
3594
  | `tickCount` | `number` | — | Target value; the 1-2-5 algorithm may deviate.<br>Shown in: [Keep charts in step](#zoom-and-pan), [Fix the domain and name the ticks (Axis)](#axis), [Leave out the hours nobody works (Axis)](#axis) and 8 more |
3578
- | `tickFormat` | `(v: number) => string` | — | Label of a tick. Compared by its source text: one that reads a changed closure variable under the same text does not relabel the axis - give it a new text or remount. A bound `Intl.NumberFormat#format` is compared by identity.<br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Leave out the hours nobody works (Axis)](#axis), [Bind every kind of series to a second axis (Axis)](#axis) and 21 more |
3595
+ | `tickFormat` | `ValueFormat` | — | Label of a tick. Compared by its source text: one that reads a changed closure variable under the same text does not relabel the axis - give it a new text or remount. A bound `Intl.NumberFormat#format` is compared by identity. Or `NumberOptions`: Intl's options in the wording's locale, compared by value; the step's places stay unless the options set digits.<br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Leave out the hours nobody works (Axis)](#axis), [Bind every kind of series to a second axis (Axis)](#axis) and 21 more |
3579
3596
  | `grid` | `boolean` | only on the first axis of its orientation | Grid lines.<br>Shown in: [One vehicle through the day (StateBand)](#stateband), [Give each one a lane (StateBand)](#stateband), [See where the vans stand idle (Scenarios)](#see-where-the-vans-stand-idle) |
3580
3597
  | `ticks` | `readonly number[]` | — | Fixed tick values instead of the 1-2-5 algorithm, on a numeric axis: an hour axis' named hours, a lane grid. An axis that reads categories needs none - every category has its tick.<br>Shown in: [Bind every kind of series to a second axis (Axis)](#axis), [Mark bands, limits and cells (Chart)](#chart-1), [Compare progress with a plan (Line)](#line) and 7 more |
3581
3598
  | `categories` | `readonly string[]` | — | The categories of the axis, in their order. Without it they stand in the order the rows first bring them. With it the list is the order and the extent: a category no row carries keeps its empty place, and a row whose category is not listed is not drawn - DEV names it. It makes the axis a category axis whatever the rows hold ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)).<br>Shown in: [Fix the order and keep an empty place (Axis)](#axis) |
@@ -3592,7 +3609,7 @@ The keys of [Chart](#chart-1) apply here.
3592
3609
  | Prop | Type | Default | Description |
3593
3610
  |---|---|---|---|
3594
3611
  | `onViewChange` | `(view: `[`ChartView`](#view)`) => void` | — | Every change the reader makes, once, always the whole view - the one to keep, or to hand another chart's `setView`. A pan or zoom is reported at most once per frame. Not called for the view the chart starts with, nor for one `setView` applies.<br>Shown in: [Keep charts in step](#zoom-and-pan), [Read it by keyboard and screen reader (Chart)](#chart-1) |
3595
- | `onSelect` | `(hit: ``TooltipHit``<Z>) => void` | — | The reader chose a point: a click on a hit (a tap too), or Enter on the Active point - the same hit the tooltip's `render` receives. Once per click; the end of a drag does not report, and the second click of a double click does not either. The chart holds no selection and draws none: what is chosen is the application's state. Needs a `<Tooltip>`, which makes the hits; the hit does not travel over `syncId`.<br>Shown in: [Choose a hit (Tooltip)](#tooltip) |
3612
+ | `onSelect` | `(hit: ``TooltipHit``<Z>) => void` | — | The reader chose a point: a click on a hit (a tap too), or Enter on the Active point - the same hit the tooltip's `render` receives. Once per click; the end of a drag does not report, and the second click of a double click does not either. The chart holds no selection and draws none: what is chosen is the application's state. Needs a `<Tooltip>`, which makes the hits; the hit does not travel over `syncId`.<br>Shown in: [Choose a hit (Tooltip)](#tooltip), [Drill down from a year to its days (Tooltip)](#tooltip) |
3596
3613
 
3597
3614
  ##### Types on this page
3598
3615
 
@@ -3643,6 +3660,15 @@ schedule's shape.
3643
3660
  | `min` *required* | `number` | — | <br>Shown in: [Limit the zoom](#zoom-and-pan) |
3644
3661
  | `max` *required* | `number` | — | <br>Shown in: [Limit the zoom](#zoom-and-pan) |
3645
3662
 
3663
+ ###### `ValueFormat`
3664
+
3665
+ A value format: a function, or Intl's options written in the chart's
3666
+ wording's locale.
3667
+
3668
+ ```ts
3669
+ type ValueFormat = ((value: number) => string) | NumberOptions;
3670
+ ```
3671
+
3646
3672
  ###### `TooltipHit<T = unknown>`
3647
3673
 
3648
3674
  What the pointer hit: the x value under it and one point per series there.
@@ -3651,10 +3677,21 @@ A custom tooltip's `render` receives it.
3651
3677
  | Prop | Type | Default | Description |
3652
3678
  |---|---|---|---|
3653
3679
  | `xValue` *required* | `number` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Write the tooltip yourself (Tooltip)](#tooltip), [Choose a hit (Tooltip)](#tooltip) |
3654
- | `points` *required* | `readonly ``TooltipPoint``<T>[]` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Write the tooltip yourself (Tooltip)](#tooltip), [Choose a hit (Tooltip)](#tooltip) |
3680
+ | `points` *required* | `readonly ``TooltipPoint``<T>[]` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Write the tooltip yourself (Tooltip)](#tooltip), [Choose a hit (Tooltip)](#tooltip) and 1 more |
3655
3681
  | `xPx` *required* | `number` | — | Pixel position of the crosshair (snapped to the data point). |
3656
3682
  | `yPx` *required* | `number` | — | Pixel position of the primary hit. |
3657
3683
 
3684
+ ###### `NumberOptions`
3685
+
3686
+ The options of `Intl.NumberFormat`, plus `decimals` for a fixed number of
3687
+ places (minimum and maximum alike; an Intl digit option given beside it
3688
+ wins). The same shape as core's, declared here because the charts import
3689
+ nothing.
3690
+
3691
+ | Prop | Type | Default | Description |
3692
+ |---|---|---|---|
3693
+ | `decimals` | `number` | — | |
3694
+
3658
3695
  ###### `TooltipPoint<T = unknown>`
3659
3696
 
3660
3697
  One hit per series in the tooltip.
@@ -3665,7 +3702,7 @@ One hit per series in the tooltip.
3665
3702
  | `color` *required* | `string` | — | |
3666
3703
  | `xValue` *required* | `number` | — | The point's x value on its own x axis - with several x axes it can differ from the hit's. |
3667
3704
  | `yValue` *required* | `number` | — | The point's y value; in a stack its own value, not its top - in a normalised stack its share in percent.<br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Choose a hit (Tooltip)](#tooltip) |
3668
- | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself (Tooltip)](#tooltip) |
3705
+ | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself (Tooltip)](#tooltip), [Drill down from a year to its days (Tooltip)](#tooltip) |
3669
3706
  | `index` *required* | `number` | — | |
3670
3707
  | `level` | `number` | — | Only for the matrix: its level, out of the level channel. It stands here and not in yValue, because yValue is the position on the y axis - for a matrix therefore the row. Putting both into one field would be exactly the overloading [ADR-0011](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0011-named-channels-never-overloaded.md) is written against. |
3671
3708
  | `segment` | `{ from: number; to: number; label: string }` | — | Only for state series: the section being pointed at. A duration does not stand here - it is the difference of two numbers the entry already carries, and formatting it would presuppose knowing what the x axis means. The caller knows that, not this library. |
@@ -3697,7 +3734,7 @@ The optional numbers of a box by name.
3697
3734
 
3698
3735
  - No zoom along y, no rectangle zoom and no navigator: the x span is the one zoom there is.
3699
3736
 
3700
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
3737
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
3701
3738
 
3702
3739
  ### View
3703
3740
 
@@ -3836,7 +3873,7 @@ export default function KeepAndRestore() {
3836
3873
  const { Chart, XAxis, YAxis, Line, view, setView } = useChart(SERIES[0]!.data);
3837
3874
  return (
3838
3875
  <>
3839
- <Chart height={260} ariaLabel="Latency of three services today">
3876
+ <Chart height={260} aria-label="Latency of three services today">
3840
3877
  <XAxis value="t" time zoomable />
3841
3878
  <YAxis label="ms" domain="visible" />
3842
3879
  {SERIES.map((one) => (
@@ -3893,7 +3930,7 @@ The keys of [Chart](#chart-1) apply here.
3893
3930
  | Prop | Type | Default | Description |
3894
3931
  |---|---|---|---|
3895
3932
  | `onViewChange` | `(view: ``ChartView``) => void` | — | Every change the reader makes, once, always the whole view - the one to keep, or to hand another chart's `setView`. A pan or zoom is reported at most once per frame. Not called for the view the chart starts with, nor for one `setView` applies.<br>Shown in: [Keep charts in step (Zoom and pan)](#zoom-and-pan), [Read it by keyboard and screen reader (Chart)](#chart-1) |
3896
- | `onSelect` | `(hit: ``TooltipHit``<Z>) => void` | — | The reader chose a point: a click on a hit (a tap too), or Enter on the Active point - the same hit the tooltip's `render` receives. Once per click; the end of a drag does not report, and the second click of a double click does not either. The chart holds no selection and draws none: what is chosen is the application's state. Needs a `<Tooltip>`, which makes the hits; the hit does not travel over `syncId`.<br>Shown in: [Choose a hit (Tooltip)](#tooltip) |
3933
+ | `onSelect` | `(hit: ``TooltipHit``<Z>) => void` | — | The reader chose a point: a click on a hit (a tap too), or Enter on the Active point - the same hit the tooltip's `render` receives. Once per click; the end of a drag does not report, and the second click of a double click does not either. The chart holds no selection and draws none: what is chosen is the application's state. Needs a `<Tooltip>`, which makes the hits; the hit does not travel over `syncId`.<br>Shown in: [Choose a hit (Tooltip)](#tooltip), [Drill down from a year to its days (Tooltip)](#tooltip) |
3897
3934
 
3898
3935
  ##### `ChartView`
3899
3936
 
@@ -3912,7 +3949,7 @@ A custom tooltip's `render` receives it.
3912
3949
  | Prop | Type | Default | Description |
3913
3950
  |---|---|---|---|
3914
3951
  | `xValue` *required* | `number` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Write the tooltip yourself (Tooltip)](#tooltip), [Choose a hit (Tooltip)](#tooltip) |
3915
- | `points` *required* | `readonly ``TooltipPoint``<T>[]` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Write the tooltip yourself (Tooltip)](#tooltip), [Choose a hit (Tooltip)](#tooltip) |
3952
+ | `points` *required* | `readonly ``TooltipPoint``<T>[]` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Write the tooltip yourself (Tooltip)](#tooltip), [Choose a hit (Tooltip)](#tooltip) and 1 more |
3916
3953
  | `xPx` *required* | `number` | — | Pixel position of the crosshair (snapped to the data point). |
3917
3954
  | `yPx` *required* | `number` | — | Pixel position of the primary hit. |
3918
3955
 
@@ -3934,7 +3971,7 @@ One hit per series in the tooltip.
3934
3971
  | `color` *required* | `string` | — | |
3935
3972
  | `xValue` *required* | `number` | — | The point's x value on its own x axis - with several x axes it can differ from the hit's. |
3936
3973
  | `yValue` *required* | `number` | — | The point's y value; in a stack its own value, not its top - in a normalised stack its share in percent.<br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Choose a hit (Tooltip)](#tooltip) |
3937
- | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself (Tooltip)](#tooltip) |
3974
+ | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself (Tooltip)](#tooltip), [Drill down from a year to its days (Tooltip)](#tooltip) |
3938
3975
  | `index` *required* | `number` | — | |
3939
3976
  | `level` | `number` | — | Only for the matrix: its level, out of the level channel. It stands here and not in yValue, because yValue is the position on the y axis - for a matrix therefore the row. Putting both into one field would be exactly the overloading [ADR-0011](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0011-named-channels-never-overloaded.md) is written against. |
3940
3977
  | `segment` | `{ from: number; to: number; label: string }` | — | Only for state series: the section being pointed at. A duration does not stand here - it is the difference of two numbers the entry already carries, and formatting it would presuppose knowing what the x axis means. The caller knows that, not this library. |
@@ -3966,7 +4003,7 @@ The optional numbers of a box by name.
3966
4003
 
3967
4004
  - The active point and the open data table are not part of the view: they are where the reader is, not how the chart is looked at.
3968
4005
 
3969
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
4006
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
3970
4007
 
3971
4008
  ## Series
3972
4009
 
@@ -4077,7 +4114,7 @@ const CHECKOUT = metrics("checkout");
4077
4114
  export default function OneSeries() {
4078
4115
  const { Chart, XAxis, YAxis, Line } = useChart(CHECKOUT);
4079
4116
  return (
4080
- <Chart height={280} ariaLabel="Checkout's 95th percentile latency today">
4117
+ <Chart height={280} aria-label="Checkout's 95th percentile latency today">
4081
4118
  <XAxis value="t" time />
4082
4119
  <YAxis label="ms" />
4083
4120
  <Line value="p95" name="Checkout p95" />
@@ -4190,7 +4227,7 @@ const IMAGES = metrics("images").map((d) => {
4190
4227
  export default function MultiSeries() {
4191
4228
  const { Chart, XAxis, YAxis, Line } = useChart(CHECKOUT);
4192
4229
  return (
4193
- <Chart height={300} ariaLabel="95th percentile latency of four services today">
4230
+ <Chart height={300} aria-label="95th percentile latency of four services today">
4194
4231
  <XAxis value="t" time />
4195
4232
  <YAxis label="ms" />
4196
4233
  <Line value="p95" name="Checkout" />
@@ -4317,7 +4354,7 @@ const CHECKOUT = metrics("checkout");
4317
4354
  export default function StepLine() {
4318
4355
  const { Chart, XAxis, YAxis, Line } = useChart(CHECKOUT);
4319
4356
  return (
4320
- <Chart height={260} ariaLabel="Checkout's requests and the instances serving them today">
4357
+ <Chart height={260} aria-label="Checkout's requests and the instances serving them today">
4321
4358
  <XAxis value="t" time />
4322
4359
  <YAxis label="Requests/min" />
4323
4360
  <YAxis id="instances" position="right" domain={[0, 15]} label="Instances" />
@@ -4405,7 +4442,7 @@ const day = (v: number) => new Date(v).toLocaleDateString("en-GB", { weekday: "s
4405
4442
  export default function BurnDown() {
4406
4443
  const { Chart, XAxis, YAxis, Line } = useChart(BURNDOWN);
4407
4444
  return (
4408
- <Chart height={260} ariaLabel="Sprint 14: hours left against the ideal">
4445
+ <Chart height={260} aria-label="Sprint 14: hours left against the ideal">
4409
4446
  <XAxis value="t" ticks={BURNDOWN.map((d) => d.t)} tickFormat={day} />
4410
4447
  <YAxis label="Hours left" />
4411
4448
  <Line value="ideal" name="Ideal" color="var(--uc-color-text)" dash={[4, 4]} />
@@ -4437,7 +4474,7 @@ The keys of [Chart](#chart-1) apply here.
4437
4474
  | `yAxisId` | `string` | `"y"` | Binding to a y axis.<br>Shown in: [Hold a value until it changes](#line), [Add axes for other magnitudes (Axis)](#axis), [Align a second axis to the grid (Axis)](#axis) and 4 more |
4438
4475
  | `data` | `readonly T[]` | — | Series-own data; overrides the container data.<br>Shown in: [Several series and a gap](#line), [Hold a value until it changes](#line), [Tell series apart without colour (Chart)](#chart-1) and 6 more |
4439
4476
  | `name` | `string` | "Series n", after its place in the chart | The name in legend and tooltip. Without one a warning stands in DEV - an unnamed series is a colour nobody can look up.<br>Shown in: [One series](#line), [Several series and a gap](#line), [Hold a value until it changes](#line) and 47 more |
4440
- | `format` | `(value: number) => string` | the y axis' `tickFormat`, else the built-in number format | The value as the tooltip writes it.<br>Shown in: [Write each value in its unit (Tooltip)](#tooltip), [Show the values as a table (Tooltip)](#tooltip), [Table a long series (Tooltip)](#tooltip) and 1 more |
4477
+ | `format` | `ValueFormat` | the y axis' `tickFormat`, else the built-in number format | The value as the tooltip writes it, as a function or `NumberOptions`.<br>Shown in: [Write each value in its unit (Tooltip)](#tooltip), [Show the values as a table (Tooltip)](#tooltip), [Table a long series (Tooltip)](#tooltip) and 1 more |
4441
4478
  | `color` | `string` | the palette's `--uc-series-N` | Any CSS colour value.<br>Shown in: [Compare progress with a plan](#line), [Fix the domain and name the ticks (Axis)](#axis), [Watch latency against its objective (Scenarios)](#watch-latency-against-its-objective) and 1 more |
4442
4479
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value; the theme resolves it.<br>Shown in: [Compare the budget with the forecast (Scenarios)](#compare-the-budget-with-the-forecast) |
4443
4480
  | `strokeWidth` | `number` | `1.5` | Line width in CSS pixels.<br>Shown in: [Hold a value until it changes](#line), [Compare progress with a plan](#line), [Mix series kinds (Chart)](#chart-1) and 9 more |
@@ -4456,6 +4493,15 @@ by its name, or a function, compared by its source text (`ValueFunction`).
4456
4493
  type Value<T> = NumberField<T> | ValueFunction<T>;
4457
4494
  ```
4458
4495
 
4496
+ ###### `ValueFormat`
4497
+
4498
+ A value format: a function, or Intl's options written in the chart's
4499
+ wording's locale.
4500
+
4501
+ ```ts
4502
+ type ValueFormat = ((value: number) => string) | NumberOptions;
4503
+ ```
4504
+
4459
4505
  ###### `NumberField<T>`
4460
4506
 
4461
4507
  The fields of a row whose value is a number, or absent.
@@ -4480,11 +4526,22 @@ data reference then. A native or bound function is compared by identity.
4480
4526
  type ValueFunction<T> = (d: T, index: number) => number | null | undefined;
4481
4527
  ```
4482
4528
 
4529
+ ###### `NumberOptions`
4530
+
4531
+ The options of `Intl.NumberFormat`, plus `decimals` for a fixed number of
4532
+ places (minimum and maximum alike; an Intl digit option given beside it
4533
+ wins). The same shape as core's, declared here because the charts import
4534
+ nothing.
4535
+
4536
+ | Prop | Type | Default | Description |
4537
+ |---|---|---|---|
4538
+ | `decimals` | `number` | — | |
4539
+
4483
4540
  #### Known limits
4484
4541
 
4485
- - No smoothing: a curve between two readings would invent values nobody measured ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.42.0/docs/adr/0032-what-umriss-is-not.md)).
4542
+ - No smoothing: a curve between two readings would invent values nobody measured ([ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.43.0/docs/adr/0032-what-umriss-is-not.md)).
4486
4543
 
4487
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
4544
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
4488
4545
 
4489
4546
  ### Area
4490
4547
 
@@ -4593,7 +4650,7 @@ const CHECKOUT = metrics("checkout");
4593
4650
  export default function Filled() {
4594
4651
  const { Chart, XAxis, YAxis, Area } = useChart(CHECKOUT);
4595
4652
  return (
4596
- <Chart height={260} ariaLabel="Checkout's requests per minute today">
4653
+ <Chart height={260} aria-label="Checkout's requests per minute today">
4597
4654
  <XAxis value="t" time />
4598
4655
  <YAxis label="Requests/min" />
4599
4656
  <Area value="requests" name="Requests" fillOpacity={0.35} strokeWidth={2} />
@@ -4725,7 +4782,7 @@ const EXPECTED = expected("checkout");
4725
4782
  export default function Corridor() {
4726
4783
  const { Chart, XAxis, YAxis, Area, Line } = useChart(CHECKOUT);
4727
4784
  return (
4728
- <Chart height={280} ariaLabel="Checkout's requests inside the range they are expected in">
4785
+ <Chart height={280} aria-label="Checkout's requests inside the range they are expected in">
4729
4786
  <XAxis value="t" time />
4730
4787
  <YAxis label="Requests/min" />
4731
4788
  {/* The outline runs along the upper edge; dashed, it reads as expected, not measured. */}
@@ -4797,7 +4854,7 @@ export default function StackedAreas() {
4797
4854
  const { Chart, XAxis, YAxis, Area } = useChart(PARCELS_PER_HOUR);
4798
4855
 
4799
4856
  return (
4800
- <Chart height={280} ariaLabel="Parcels loaded per hour at three depots, stacked">
4857
+ <Chart height={280} aria-label="Parcels loaded per hour at three depots, stacked">
4801
4858
  <XAxis value="t" time domain="data" />
4802
4859
  <YAxis label="Parcels per hour" />
4803
4860
  <Area value="north" name="North" stack="depots" fillOpacity={0.5} strokeWidth={1} />
@@ -4861,7 +4918,7 @@ const tonnes = (v: number) => `${v.toFixed(1)} t`;
4861
4918
  export default function Shares() {
4862
4919
  const { Chart, XAxis, YAxis, Area } = useChart(TONNES_PER_HOUR);
4863
4920
  return (
4864
- <Chart height={280} ariaLabel="Each depot's share of the freight loaded per hour">
4921
+ <Chart height={280} aria-label="Each depot's share of the freight loaded per hour">
4865
4922
  <XAxis value="t" time domain="data" />
4866
4923
  <YAxis label="Share of the hour" />
4867
4924
  <Area value="north" name="North" stack="depots" normalize format={tonnes} fillOpacity={0.5} strokeWidth={1} />
@@ -4894,7 +4951,7 @@ The keys of [Chart](#chart-1) apply here.
4894
4951
  | `yAxisId` | `string` | `"y"` | Binding to a y axis.<br>Shown in: [Bind every kind of series to a second axis (Axis)](#axis) |
4895
4952
  | `data` | `readonly T[]` | — | Series-own data; overrides the container data.<br>Shown in: [Fill a range between two edges](#area), [Bind every kind of series to a second axis (Axis)](#axis) |
4896
4953
  | `name` | `string` | "Series n", after its place in the chart | The name in legend and tooltip. Without one a warning stands in DEV - an unnamed series is a colour nobody can look up.<br>Shown in: [Fill down to zero](#area), [Fill a range between two edges](#area), [Stack areas](#area) and 5 more |
4897
- | `format` | `(value: number) => string` | the y axis' `tickFormat`, else the built-in number format | The value as the tooltip writes it.<br>Shown in: [Show shares of the whole over time](#area) |
4954
+ | `format` | `ValueFormat` | the y axis' `tickFormat`, else the built-in number format | The value as the tooltip writes it, as a function or `NumberOptions`.<br>Shown in: [Show shares of the whole over time](#area) |
4898
4955
  | `color` | `string` | the palette's `--uc-series-N` | Any CSS colour value. |
4899
4956
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value; the theme resolves it. `color` beats it. |
4900
4957
  | `stack` | `string` | — | The stack this series stands in: every Bar and Area with the same id on the same x and y axis stands on the ones registered before it. A gap stacks as zero; negative values stack downward from zero. The tooltip names each series' own value and the stack's total.<br>Shown in: [Stack areas](#area), [Show shares of the whole over time](#area) |
@@ -4914,6 +4971,15 @@ by its name, or a function, compared by its source text (`ValueFunction`).
4914
4971
  type Value<T> = NumberField<T> | ValueFunction<T>;
4915
4972
  ```
4916
4973
 
4974
+ ###### `ValueFormat`
4975
+
4976
+ A value format: a function, or Intl's options written in the chart's
4977
+ wording's locale.
4978
+
4979
+ ```ts
4980
+ type ValueFormat = ((value: number) => string) | NumberOptions;
4981
+ ```
4982
+
4917
4983
  ###### `NumberField<T>`
4918
4984
 
4919
4985
  The fields of a row whose value is a number, or absent.
@@ -4938,6 +5004,17 @@ data reference then. A native or bound function is compared by identity.
4938
5004
  type ValueFunction<T> = (d: T, index: number) => number | null | undefined;
4939
5005
  ```
4940
5006
 
5007
+ ###### `NumberOptions`
5008
+
5009
+ The options of `Intl.NumberFormat`, plus `decimals` for a fixed number of
5010
+ places (minimum and maximum alike; an Intl digit option given beside it
5011
+ wins). The same shape as core's, declared here because the charts import
5012
+ nothing.
5013
+
5014
+ | Prop | Type | Default | Description |
5015
+ |---|---|---|---|
5016
+ | `decimals` | `number` | — | |
5017
+
4941
5018
  ### Bar
4942
5019
 
4943
5020
  Bars along the x axis (a bar or column chart): one per category, grouped side by side, stacked, or as shares of a whole.
@@ -4973,7 +5050,7 @@ const SPEND = [
4973
5050
  export default function PerCategory() {
4974
5051
  const { Chart, XAxis, YAxis, Bar } = useChart(SPEND);
4975
5052
  return (
4976
- <Chart height={260} ariaLabel="Actual spend per cost centre in February">
5053
+ <Chart height={260} aria-label="Actual spend per cost centre in February">
4977
5054
  <XAxis value="costCentre" />
4978
5055
  <YAxis label="€" />
4979
5056
  <Bar value="actual" name="Actual" />
@@ -5006,7 +5083,7 @@ const FEBRUARY = [
5006
5083
  export default function Grouped() {
5007
5084
  const { Chart, XAxis, YAxis, Bar } = useChart(FEBRUARY);
5008
5085
  return (
5009
- <Chart height={280} ariaLabel="Budget and actual per cost centre in February">
5086
+ <Chart height={280} aria-label="Budget and actual per cost centre in February">
5010
5087
  <XAxis value="costCentre" />
5011
5088
  <YAxis label="€" />
5012
5089
  {/* A token, so that it follows the colour scheme. */}
@@ -5045,7 +5122,7 @@ const signed = (v: number) => (v > 0 ? `+${(v / 1000).toFixed(0)}k` : `${(v / 10
5045
5122
  export default function Deviation() {
5046
5123
  const { Chart, XAxis, YAxis, Bar } = useChart(DEVIATION);
5047
5124
  return (
5048
- <Chart height={280} ariaLabel="Forecast against budget for the year, per cost centre">
5125
+ <Chart height={280} aria-label="Forecast against budget for the year, per cost centre">
5049
5126
  <XAxis value="costCentre" />
5050
5127
  <YAxis tickFormat={signed} label="€" />
5051
5128
  <Bar value="euros" name="Over budget" />
@@ -5082,7 +5159,7 @@ export default function Stacked() {
5082
5159
  const { Chart, XAxis, YAxis, Bar } = useChart(DELAYS);
5083
5160
 
5084
5161
  return (
5085
- <Chart height={280} ariaLabel="Minutes late per working day, by cause">
5162
+ <Chart height={280} aria-label="Minutes late per working day, by cause">
5086
5163
  <XAxis value="day" />
5087
5164
  <YAxis label="Minutes late" />
5088
5165
  <Bar value="traffic" name="Traffic" stack="delay" />
@@ -5123,7 +5200,7 @@ const minutes = (v: number) => `${v} min`;
5123
5200
  export default function Percent() {
5124
5201
  const { Chart, XAxis, YAxis, Bar } = useChart(DELAYS);
5125
5202
  return (
5126
- <Chart height={280} ariaLabel="Share of each cause in the minutes late per working day">
5203
+ <Chart height={280} aria-label="Share of each cause in the minutes late per working day">
5127
5204
  <XAxis value="day" />
5128
5205
  <YAxis />
5129
5206
  <Bar value="traffic" name="Traffic" stack="delay" normalize format={minutes} />
@@ -5154,7 +5231,7 @@ export default function Histogram() {
5154
5231
  const bins = useMemo(() => bin(MINUTES_TO_ANSWER, { width: 20 }), []);
5155
5232
  const { Chart, XAxis, YAxis, Bar } = useChart(bins);
5156
5233
  return (
5157
- <Chart height={260} ariaLabel="Minutes to answer 40 support tickets, counted in bins of 20 minutes">
5234
+ <Chart height={260} aria-label="Minutes to answer 40 support tickets, counted in bins of 20 minutes">
5158
5235
  <XAxis value="x" label="minutes to answer" />
5159
5236
  <YAxis label="tickets" />
5160
5237
  <Bar value="count" name="Tickets" barWidth={0.94} />
@@ -5178,12 +5255,12 @@ The keys of [Chart](#chart-1) apply here.
5178
5255
 
5179
5256
  | Prop | Type | Default | Description |
5180
5257
  |---|---|---|---|
5181
- | `value` *required* | `Value``<T>` | — | Height - a number field of the row or a function of it; null/undefined/NaN/±Infinity means a gap.<br>Shown in: [One bar per category](#bar), [Group bars side by side](#bar), [Show bars above and below zero](#bar) and 13 more |
5258
+ | `value` *required* | `Value``<T>` | — | Height - a number field of the row or a function of it; null/undefined/NaN/±Infinity means a gap.<br>Shown in: [One bar per category](#bar), [Group bars side by side](#bar), [Show bars above and below zero](#bar) and 14 more |
5182
5259
  | `xAxisId` | `string` | `"x"` | Binding to an x axis.<br>Shown in: [Bind every kind of series to a second axis (Axis)](#axis) |
5183
5260
  | `yAxisId` | `string` | `"y"` | Binding to a y axis.<br>Shown in: [Bind every kind of series to a second axis (Axis)](#axis) |
5184
5261
  | `data` | `readonly T[]` | — | Series-own data; overrides the container data.<br>Shown in: [Bind every kind of series to a second axis (Axis)](#axis) |
5185
- | `name` | `string` | "Series n", after its place in the chart | The name in legend and tooltip. Without one a warning stands in DEV - an unnamed series is a colour nobody can look up.<br>Shown in: [One bar per category](#bar), [Group bars side by side](#bar), [Show bars above and below zero](#bar) and 13 more |
5186
- | `format` | `(value: number) => string` | the y axis' `tickFormat`, else the built-in number format | The value as the tooltip writes it.<br>Shown in: [Show shares of a whole](#bar), [Compare the budget with the forecast (Scenarios)](#compare-the-budget-with-the-forecast) |
5262
+ | `name` | `string` | "Series n", after its place in the chart | The name in legend and tooltip. Without one a warning stands in DEV - an unnamed series is a colour nobody can look up.<br>Shown in: [One bar per category](#bar), [Group bars side by side](#bar), [Show bars above and below zero](#bar) and 14 more |
5263
+ | `format` | `ValueFormat` | the y axis' `tickFormat`, else the built-in number format | The value as the tooltip writes it, as a function or `NumberOptions`.<br>Shown in: [Show shares of a whole](#bar), [Compare the budget with the forecast (Scenarios)](#compare-the-budget-with-the-forecast) |
5187
5264
  | `color` | `string` | the palette's `--uc-series-N` | Any CSS colour value.<br>Shown in: [Group bars side by side](#bar), [See where the vans stand idle (Scenarios)](#see-where-the-vans-stand-idle), [Compare the budget with the forecast (Scenarios)](#compare-the-budget-with-the-forecast) |
5188
5265
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value; the theme resolves it. `color` beats it.<br>Shown in: [Compare the budget with the forecast (Scenarios)](#compare-the-budget-with-the-forecast) |
5189
5266
  | `stack` | `string` | — | The stack this series stands in: every Bar and Area with the same id on the same x and y axis stands on the ones registered before it. A gap stacks as zero; negative values stack downward from zero. The tooltip names each series' own value and the stack's total.<br>Shown in: [Stack bars](#bar), [Show shares of a whole](#bar), [Read a stack's total in the table (Tooltip)](#tooltip) and 1 more |
@@ -5201,6 +5278,15 @@ by its name, or a function, compared by its source text (`ValueFunction`).
5201
5278
  type Value<T> = NumberField<T> | ValueFunction<T>;
5202
5279
  ```
5203
5280
 
5281
+ ###### `ValueFormat`
5282
+
5283
+ A value format: a function, or Intl's options written in the chart's
5284
+ wording's locale.
5285
+
5286
+ ```ts
5287
+ type ValueFormat = ((value: number) => string) | NumberOptions;
5288
+ ```
5289
+
5204
5290
  ###### `NumberField<T>`
5205
5291
 
5206
5292
  The fields of a row whose value is a number, or absent.
@@ -5225,11 +5311,22 @@ data reference then. A native or bound function is compared by identity.
5225
5311
  type ValueFunction<T> = (d: T, index: number) => number | null | undefined;
5226
5312
  ```
5227
5313
 
5314
+ ###### `NumberOptions`
5315
+
5316
+ The options of `Intl.NumberFormat`, plus `decimals` for a fixed number of
5317
+ places (minimum and maximum alike; an Intl digit option given beside it
5318
+ wins). The same shape as core's, declared here because the charts import
5319
+ nothing.
5320
+
5321
+ | Prop | Type | Default | Description |
5322
+ |---|---|---|---|
5323
+ | `decimals` | `number` | — | |
5324
+
5228
5325
  #### Known limits
5229
5326
 
5230
5327
  - Horizontal bars are the next kind to come ([ADR-0059](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0059-the-set-of-chart-kinds-is-open.md)); until then a `Matrix` or a `Table` ranks by name.
5231
5328
 
5232
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
5329
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
5233
5330
 
5234
5331
  ### Scatter
5235
5332
 
@@ -5384,7 +5481,7 @@ const ARRIVALS: Arrival[] = TOURS.flatMap((tour) =>
5384
5481
  export default function Arrivals() {
5385
5482
  const { Chart, XAxis, YAxis, Scatter } = useChart(ARRIVALS);
5386
5483
  return (
5387
- <Chart height={260} ariaLabel="Every stop of today's tours: minutes after its window closed">
5484
+ <Chart height={260} aria-label="Every stop of today's tours: minutes after its window closed">
5388
5485
  <XAxis value="t" time label="Arrival" />
5389
5486
  <YAxis label="Minutes late" />
5390
5487
  <Scatter value="late" name="Stop" radius={4} />
@@ -5465,7 +5562,7 @@ const OBJECTIVE = SERVICES.find((one) => one.id === "checkout")!.latencySlo;
5465
5562
  export default function AgainstATarget() {
5466
5563
  const { Chart, XAxis, YAxis, Scatter } = useChart(TRACES);
5467
5564
  return (
5468
- <Chart height={300} ariaLabel="Traced Checkout requests against the latency objective">
5565
+ <Chart height={300} aria-label="Traced Checkout requests against the latency objective">
5469
5566
  <XAxis value="t" time />
5470
5567
  <YAxis label="ms" />
5471
5568
  <LimitLine value={OBJECTIVE} severity="warning" label="Objective" />
@@ -5494,7 +5591,7 @@ The keys of [Chart](#chart-1) apply here.
5494
5591
  | `yAxisId` | `string` | `"y"` | Binding to a y axis.<br>Shown in: [Bind every kind of series to a second axis (Axis)](#axis) |
5495
5592
  | `data` | `readonly T[]` | — | Series-own data; overrides the container data.<br>Shown in: [Bind every kind of series to a second axis (Axis)](#axis) |
5496
5593
  | `name` | `string` | "Series n", after its place in the chart | The name in legend and tooltip. Without one a warning stands in DEV - an unnamed series is a colour nobody can look up.<br>Shown in: [Plot single measurements](#scatter), [Mark samples that miss a target](#scatter), [Mix series kinds (Chart)](#chart-1) and 3 more |
5497
- | `format` | `(value: number) => string` | the y axis' `tickFormat`, else the built-in number format | The value as the tooltip writes it. |
5594
+ | `format` | `ValueFormat` | the y axis' `tickFormat`, else the built-in number format | The value as the tooltip writes it, as a function or `NumberOptions`. |
5498
5595
  | `color` | `string` | the palette's `--uc-series-N` | Any CSS colour value. |
5499
5596
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value; the theme resolves it.<br>Shown in: [Mark samples that miss a target](#scatter) |
5500
5597
  | `radius` | `number` | `3` | Point radius in CSS pixels.<br>Shown in: [Plot single measurements](#scatter), [Mark samples that miss a target](#scatter) |
@@ -5510,6 +5607,15 @@ by its name, or a function, compared by its source text (`ValueFunction`).
5510
5607
  type Value<T> = NumberField<T> | ValueFunction<T>;
5511
5608
  ```
5512
5609
 
5610
+ ###### `ValueFormat`
5611
+
5612
+ A value format: a function, or Intl's options written in the chart's
5613
+ wording's locale.
5614
+
5615
+ ```ts
5616
+ type ValueFormat = ((value: number) => string) | NumberOptions;
5617
+ ```
5618
+
5513
5619
  ###### `NumberField<T>`
5514
5620
 
5515
5621
  The fields of a row whose value is a number, or absent.
@@ -5534,6 +5640,17 @@ data reference then. A native or bound function is compared by identity.
5534
5640
  type ValueFunction<T> = (d: T, index: number) => number | null | undefined;
5535
5641
  ```
5536
5642
 
5643
+ ###### `NumberOptions`
5644
+
5645
+ The options of `Intl.NumberFormat`, plus `decimals` for a fixed number of
5646
+ places (minimum and maximum alike; an Intl digit option given beside it
5647
+ wins). The same shape as core's, declared here because the charts import
5648
+ nothing.
5649
+
5650
+ | Prop | Type | Default | Description |
5651
+ |---|---|---|---|
5652
+ | `decimals` | `number` | — | |
5653
+
5537
5654
  ### BoxPlot
5538
5655
 
5539
5656
  A distribution per position (a box plot, box-and-whisker): quartiles, median and whiskers, per service, per group or per hour. Reach for it to compare spreads, not single values.
@@ -5574,7 +5691,7 @@ const RESPONSE_TIMES: ResponseTime[] = [
5574
5691
  export default function Minimal() {
5575
5692
  const { Chart, XAxis, YAxis, BoxPlot } = useChart(RESPONSE_TIMES);
5576
5693
  return (
5577
- <Chart height={260} ariaLabel="Response time per service over the last hour">
5694
+ <Chart height={260} aria-label="Response time per service over the last hour">
5578
5695
  <XAxis value="service" />
5579
5696
  <YAxis tickFormat={(v) => `${v} ms`} />
5580
5697
  <BoxPlot
@@ -5720,7 +5837,7 @@ const BOXES = hourBoxes();
5720
5837
  export default function OverTime() {
5721
5838
  const { Chart, XAxis, YAxis, BoxPlot } = useChart(BOXES);
5722
5839
  return (
5723
- <Chart height={280} ariaLabel="Checkout median latency per hour today">
5840
+ <Chart height={280} aria-label="Checkout median latency per hour today">
5724
5841
  <XAxis value="hour" time />
5725
5842
  <YAxis tickFormat={(v) => `${v} ms`} />
5726
5843
  <BoxPlot
@@ -5766,7 +5883,7 @@ const RESPONSE_TIMES: ResponseTime[] = [
5766
5883
  export default function WithOutliers() {
5767
5884
  const { Chart, XAxis, YAxis, BoxPlot } = useChart(RESPONSE_TIMES);
5768
5885
  return (
5769
- <Chart height={300} ariaLabel="Response time per service over the last hour, with outliers">
5886
+ <Chart height={300} aria-label="Response time per service over the last hour, with outliers">
5770
5887
  <XAxis value="service" />
5771
5888
  <YAxis tickFormat={(v) => `${v} ms`} />
5772
5889
  <BoxPlot
@@ -5814,7 +5931,7 @@ const notch = (d: Batch) => (1.57 * (d.q3 - d.q1)) / Math.sqrt(d.n);
5814
5931
  export default function MeanAndNotches() {
5815
5932
  const { Chart, XAxis, YAxis, BoxPlot } = useChart(BATCHES);
5816
5933
  return (
5817
- <Chart height={300} ariaLabel="Breaking strength per clay supplier, with mean and notches">
5934
+ <Chart height={300} aria-label="Breaking strength per clay supplier, with mean and notches">
5818
5935
  <XAxis value="supplier" />
5819
5936
  <YAxis tickFormat={(v) => `${v} N`} />
5820
5937
  <BoxPlot
@@ -5872,7 +5989,7 @@ const AFTER: Spread[] = [
5872
5989
  export default function Grouped() {
5873
5990
  const { Chart, XAxis, YAxis, BoxPlot } = useChart(BEFORE);
5874
5991
  return (
5875
- <Chart height={280} ariaLabel="Response time per service before and after the release">
5992
+ <Chart height={280} aria-label="Response time per service before and after the release">
5876
5993
  <XAxis value="service" />
5877
5994
  <YAxis tickFormat={(v) => `${v} ms`} />
5878
5995
  {[
@@ -5928,7 +6045,7 @@ const THIS_WEEK: Spread[] = [
5928
6045
  export default function AgainstTheObjective() {
5929
6046
  const { Chart, XAxis, YAxis, BoxPlot } = useChart(LAST_WEEK);
5930
6047
  return (
5931
- <Chart height={280} ariaLabel="Checkout 95th percentile per region and week against its objective">
6048
+ <Chart height={280} aria-label="Checkout 95th percentile per region and week against its objective">
5932
6049
  <XAxis value="region" />
5933
6050
  <YAxis tickFormat={(v) => `${v} ms`} />
5934
6051
  <BoxPlot
@@ -5981,7 +6098,7 @@ const mm = (v: number) => `${v.toFixed(2)} mm`;
5981
6098
  export default function Detailed() {
5982
6099
  const { Chart, XAxis, YAxis, BoxPlot, Line } = useChart(BOXES);
5983
6100
  return (
5984
- <Chart height={320} ariaLabel="Tile length per shift, with the medians as a line">
6101
+ <Chart height={320} aria-label="Tile length per shift, with the medians as a line">
5985
6102
  <XAxis value="shift" label="Shift" />
5986
6103
  <YAxis tickFormat={mm} />
5987
6104
  <BoxPlot
@@ -6026,7 +6143,7 @@ The keys of [Chart](#chart-1) apply here.
6026
6143
  | `yAxisId` | `string` | `"y"` | Binding to a y axis. |
6027
6144
  | `data` | `readonly T[]` | — | Series-own data; overrides the container data.<br>Shown in: [Before and after, side by side](#boxplot), [Against the objective](#boxplot) |
6028
6145
  | `name` | `string` | "Series n", after its place in the chart | The name in legend and tooltip. Without one a warning stands in DEV.<br>Shown in: [One box per service](#boxplot), [A box per hour on a time axis](#boxplot), [With outliers](#boxplot) and 4 more |
6029
- | `format` | `(value: number) => string` | the y axis' `tickFormat`, else the built-in number format | Every number of the box as the tooltip and the table write it.<br>Shown in: [Mean and notches: do two medians differ?](#boxplot) |
6146
+ | `format` | `ValueFormat` | the y axis' `tickFormat`, else the built-in number format | Every number of the box as the tooltip and the table write it, as a function or `NumberOptions`.<br>Shown in: [Mean and notches: do two medians differ?](#boxplot) |
6030
6147
  | `color` | `string` | the palette's `--uc-series-N` | Any CSS colour value. |
6031
6148
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value; the theme resolves it. `color` beats it. Set by the caller: a box out of its specification is the caller's judgement, not the library's.<br>Shown in: [Against the objective](#boxplot) |
6032
6149
  | `boxWidth` | `number` | `0.8` | Width as a fraction of the step, as a bar's: boxes and bars on one x axis share this fraction and stand side by side.<br>Shown in: [Mean and notches: do two medians differ?](#boxplot), [A box per shift, its medians as a line](#boxplot) |
@@ -6050,6 +6167,15 @@ A list read from the row: the name of a list field or a function.
6050
6167
  type ListValue<T> = ListField<T> | ListFunction<T>;
6051
6168
  ```
6052
6169
 
6170
+ ###### `ValueFormat`
6171
+
6172
+ A value format: a function, or Intl's options written in the chart's
6173
+ wording's locale.
6174
+
6175
+ ```ts
6176
+ type ValueFormat = ((value: number) => string) | NumberOptions;
6177
+ ```
6178
+
6053
6179
  ###### `NumberField<T>`
6054
6180
 
6055
6181
  The fields of a row whose value is a number, or absent.
@@ -6096,11 +6222,22 @@ empty array is none; a value that is not finite is left out.
6096
6222
  type ListFunction<T> = (d: T, index: number) => readonly number[] | null | undefined;
6097
6223
  ```
6098
6224
 
6225
+ ###### `NumberOptions`
6226
+
6227
+ The options of `Intl.NumberFormat`, plus `decimals` for a fixed number of
6228
+ places (minimum and maximum alike; an Intl digit option given beside it
6229
+ wins). The same shape as core's, declared here because the charts import
6230
+ nothing.
6231
+
6232
+ | Prop | Type | Default | Description |
6233
+ |---|---|---|---|
6234
+ | `decimals` | `number` | — | |
6235
+
6099
6236
  #### Known limits
6100
6237
 
6101
6238
  - No horizontal boxes, no violin or jittered points, no box width by count: a density is smoothing, a jitter a picture that does not repeat.
6102
6239
 
6103
- What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.42.0/docs/adr/0032-what-umriss-is-not.md).
6240
+ What umriss deliberately does not build, and why: [ADR-0032](https://github.com/romanhaendler/umriss-ui/blob/charts-v0.43.0/docs/adr/0032-what-umriss-is-not.md).
6104
6241
 
6105
6242
  ### StateBand
6106
6243
 
@@ -6282,7 +6419,7 @@ const DAY = vehicleDay("v1");
6282
6419
  export default function OneVehicle() {
6283
6420
  const { Chart, XAxis, YAxis, StateBand } = useChart(DAY);
6284
6421
  return (
6285
- <Chart height={150} ariaLabel="States of van FP 214 K through the day">
6422
+ <Chart height={150} aria-label="States of van FP 214 K through the day">
6286
6423
  <XAxis value="t" time label="Time" />
6287
6424
  {/* Without a lane the band fills its y axis; one tick names it. */}
6288
6425
  <YAxis domain={[0, 1]} ticks={[0.5]} tickFormat={() => "FP 214 K"} grid={false} />
@@ -6480,7 +6617,7 @@ const STATES = vehicleDay("v2");
6480
6617
  export default function UnderACourse() {
6481
6618
  const { Chart, XAxis, YAxis, StateBand, Line } = useChart(BATTERY);
6482
6619
  return (
6483
- <Chart height={320} ariaLabel="An e-van's charge above what it was doing">
6620
+ <Chart height={320} aria-label="An e-van's charge above what it was doing">
6484
6621
  <XAxis value="t" time label="Time" />
6485
6622
  <YAxis domain={[-40, 100]} ticks={[0, 25, 50, 75, 100]} label="Charge %" />
6486
6623
  {/* The lane: the bottom unit of five, the rest is room for the course. */}
@@ -6660,7 +6797,7 @@ const FLEET = VEHICLES.map((vehicle) => ({ vehicle, day: vehicleDay(vehicle.id)
6660
6797
  export default function OneLaneEach() {
6661
6798
  const { Chart, XAxis, YAxis, StateBand } = useChart(FLEET[0]!.day);
6662
6799
  return (
6663
- <Chart height={300} ariaLabel="The states of all eight vehicles through the day">
6800
+ <Chart height={300} aria-label="The states of all eight vehicles through the day">
6664
6801
  <XAxis value="t" time label="Time" />
6665
6802
  <YAxis grid={false} />
6666
6803
  {FLEET.map(({ vehicle, day }) => (
@@ -6822,7 +6959,7 @@ const SUCCESS_BY_HOUR: readonly SuccessCell[] = (() => {
6822
6959
  export default function ByLimits() {
6823
6960
  const { Chart, XAxis, YAxis, Matrix } = useChart(SUCCESS_BY_HOUR);
6824
6961
  return (
6825
- <Chart height={280} ariaLabel="Successful requests per service and hour yesterday, by limits">
6962
+ <Chart height={280} aria-label="Successful requests per service and hour yesterday, by limits">
6826
6963
  <XAxis value="hour" ticks={[0, 6, 12, 18]} tickFormat={(v) => `${v}:00`} label="Hour" />
6827
6964
  <YAxis />
6828
6965
  <Matrix
@@ -6915,7 +7052,7 @@ const SUCCESS_BY_HOUR: readonly SuccessCell[] = (() => {
6915
7052
  export default function Gradient() {
6916
7053
  const { Chart, XAxis, YAxis, Matrix } = useChart(SUCCESS_BY_HOUR);
6917
7054
  return (
6918
- <Chart height={280} ariaLabel="Successful requests per service and hour yesterday, as a gradient">
7055
+ <Chart height={280} aria-label="Successful requests per service and hour yesterday, as a gradient">
6919
7056
  <XAxis value="hour" ticks={[0, 6, 12, 18]} tickFormat={(v) => `${v}:00`} label="Hour" />
6920
7057
  <YAxis />
6921
7058
  <Matrix value="service" level="success" name="Success rate" />
@@ -6942,7 +7079,7 @@ The keys of [Chart](#chart-1) apply here.
6942
7079
  | `yAxisId` | `string` | `"y"` | Binding to a y axis. |
6943
7080
  | `data` | `readonly T[]` | — | Series-own data; overrides the container data. |
6944
7081
  | `name` | `string` | "Series n", after its place in the chart | The name in legend and tooltip. Without one a warning stands in DEV - an unnamed series is a colour nobody can look up.<br>Shown in: [Colour cells by their limits](#matrix), [Colour cells along a gradient](#matrix), [Mark bands, limits and cells (Chart)](#chart-1) and 1 more |
6945
- | `format` | `(value: number) => string` | the built-in number format | The level as the tooltip writes it. The y axis' `tickFormat` writes the row, not the level.<br>Shown in: [Colour cells by their limits](#matrix) |
7082
+ | `format` | `ValueFormat` | the built-in number format | The level as the tooltip writes it, as a function or `NumberOptions`. The y axis' `tickFormat` writes the row, not the level.<br>Shown in: [Colour cells by their limits](#matrix) |
6946
7083
 
6947
7084
  ##### Types on this page
6948
7085
 
@@ -6991,6 +7128,15 @@ type MatrixColoring =
6991
7128
  };
6992
7129
  ```
6993
7130
 
7131
+ ###### `ValueFormat`
7132
+
7133
+ A value format: a function, or Intl's options written in the chart's
7134
+ wording's locale.
7135
+
7136
+ ```ts
7137
+ type ValueFormat = ((value: number) => string) | NumberOptions;
7138
+ ```
7139
+
6994
7140
  ###### `AxisValue`
6995
7141
 
6996
7142
  What an axis reads from a row ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)): a number - a position, or an
@@ -7042,6 +7188,17 @@ target.
7042
7188
  | `limits` | `readonly ``Limit``[]` | — | In any order; none, one or several.<br>Shown in: [Colour cells by their limits](#matrix), [Mark bands, limits and cells (Chart)](#chart-1) |
7043
7189
  | `target` | `number` | — | |
7044
7190
 
7191
+ ###### `NumberOptions`
7192
+
7193
+ The options of `Intl.NumberFormat`, plus `decimals` for a fixed number of
7194
+ places (minimum and maximum alike; an Intl digit option given beside it
7195
+ wins). The same shape as core's, declared here because the charts import
7196
+ nothing.
7197
+
7198
+ | Prop | Type | Default | Description |
7199
+ |---|---|---|---|
7200
+ | `decimals` | `number` | — | |
7201
+
7045
7202
  ###### `Limit`
7046
7203
 
7047
7204
  A bound: a value, a side and a severity. No more - a tolerance band is
@@ -7184,7 +7341,7 @@ const OBJECTIVE = SERVICES.find((one) => one.id === "checkout")!.latencySlo;
7184
7341
  export default function Objective() {
7185
7342
  const { Chart, XAxis, YAxis, Line } = useChart(CHECKOUT);
7186
7343
  return (
7187
- <Chart height={260} ariaLabel="Checkout's latency against its objective">
7344
+ <Chart height={260} aria-label="Checkout's latency against its objective">
7188
7345
  <XAxis value="t" time />
7189
7346
  <YAxis label="ms" />
7190
7347
  <LimitLine value={OBJECTIVE} severity="alarm" label="Objective" />
@@ -7288,7 +7445,7 @@ const CHECKOUT = metrics("checkout");
7288
7445
  export default function Band() {
7289
7446
  const { Chart, XAxis, YAxis, Line } = useChart(CHECKOUT);
7290
7447
  return (
7291
- <Chart height={260} ariaLabel="Checkout's error rate against its warning band and alarm limit">
7448
+ <Chart height={260} aria-label="Checkout's error rate against its warning band and alarm limit">
7292
7449
  <XAxis value="t" time />
7293
7450
  <YAxis label="Errors %" />
7294
7451
  <LimitBand from={1} to={2} severity="warning" label="Watch" />
@@ -7491,7 +7648,7 @@ const LANES = NORTH.map((vehicle) => ({ vehicle, day: vehicleDay(vehicle.id) }))
7491
7648
  export default function LimitsAndState() {
7492
7649
  const { Chart, XAxis, YAxis, StateBand, Line } = useChart(BATTERY);
7493
7650
  return (
7494
- <Chart height={340} ariaLabel="An e-van's charge against its limits, above the states of the North depot's vehicles">
7651
+ <Chart height={340} aria-label="An e-van's charge against its limits, above the states of the North depot's vehicles">
7495
7652
  <XAxis value="t" time label="Time" />
7496
7653
  <YAxis domain={[-100, 100]} ticks={[0, 20, 40, 60, 80, 100]} label="Charge %" />
7497
7654
  {/* The lanes take the lower places; the domain leaves the rest as room for the course. */}
@@ -7656,7 +7813,7 @@ const SHIFT_START = new Date(2026, 2, 17, 6).getTime();
7656
7813
  export default function KilnZone() {
7657
7814
  const { Chart, XAxis, YAxis, Line } = useChart(SHIFT.readings);
7658
7815
  return (
7659
- <Chart height={280} ariaLabel="Kiln K1 zone 3 against its tolerance and alarm limit over the early shift">
7816
+ <Chart height={280} aria-label="Kiln K1 zone 3 against its tolerance and alarm limit over the early shift">
7660
7817
  <XAxis value={(d) => SHIFT_START + d.minute * 60_000} time />
7661
7818
  <YAxis label="°C" />
7662
7819
  <LimitBand from={KILN.tolerance[0]} to={KILN.tolerance[1]} severity="warning" label="Tolerance" />
@@ -7762,7 +7919,7 @@ const CHECKOUT = metrics("checkout");
7762
7919
  export default function AlongTime() {
7763
7920
  const { Chart, XAxis, YAxis, Line } = useChart(CHECKOUT);
7764
7921
  return (
7765
- <Chart height={260} ariaLabel="Checkout's latency across a maintenance window and the start of a release freeze">
7922
+ <Chart height={260} aria-label="Checkout's latency across a maintenance window and the start of a release freeze">
7766
7923
  <XAxis value="t" time />
7767
7924
  <YAxis label="ms" />
7768
7925
  <LimitBand orientation="x" from={new Date(2026, 2, 17, 2)} to={new Date(2026, 2, 17, 3)} color="#5b7c99" label="Maintenance" />
@@ -7926,7 +8083,7 @@ const LIMITS = controlLimits(
7926
8083
  export default function SpecificationAndControl() {
7927
8084
  const { Chart, XAxis, YAxis, Line } = useChart(SAMPLES);
7928
8085
  return (
7929
- <Chart height={300} ariaLabel="The tile length against its specification and its control limits">
8086
+ <Chart height={300} aria-label="The tile length against its specification and its control limits">
7930
8087
  <XAxis value="minute" label="Minute of the shift" />
7931
8088
  <YAxis label="mm" />
7932
8089
  <LimitBand from={597.5} to={602.5} severity="warning" label="Specification" />
@@ -8177,7 +8334,7 @@ const REFERENCE_WINDOW = { kind: "referenceWindow", from: 0, to: 15 } as const;
8177
8334
  export default function ControlChartExample() {
8178
8335
  const { Chart, XAxis, YAxis } = useChart(SAMPLES);
8179
8336
  return (
8180
- <Chart height={300} ariaLabel="Control chart of the tile length after firing">
8337
+ <Chart height={300} aria-label="Control chart of the tile length after firing">
8181
8338
  <XAxis value="minute" label="Minute of the shift" />
8182
8339
  <YAxis label="mm" />
8183
8340
  {/* Specification limits are chosen, control limits are computed. */}
@@ -8362,7 +8519,7 @@ export default function ListTheViolations() {
8362
8519
  return (
8363
8520
  <div className="side-by-side">
8364
8521
  <div style={{ flex: "1 1 320px" }}>
8365
- <Chart height={240} ariaLabel="Control chart of the tile length, its violations listed beside it">
8522
+ <Chart height={240} aria-label="Control chart of the tile length, its violations listed beside it">
8366
8523
  <XAxis value="minute" label="Minute of the shift" />
8367
8524
  <YAxis label="mm" />
8368
8525
  <ControlChart value="length" data={SAMPLES} origin={REFERENCE_WINDOW} name="Tile length" onViolations={setFound} />
@@ -8553,7 +8710,7 @@ export default function ChooseTheRules() {
8553
8710
  return (
8554
8711
  <div className="side-by-side">
8555
8712
  <div style={{ flex: "1 1 320px" }}>
8556
- <Chart height={240} ariaLabel="Control chart of the tile length under three of the four rules">
8713
+ <Chart height={240} aria-label="Control chart of the tile length under three of the four rules">
8557
8714
  <XAxis value="minute" label="Minute of the shift" />
8558
8715
  <YAxis label="mm" />
8559
8716
  <ControlChart
@@ -8604,7 +8761,7 @@ The keys of [Chart](#chart-1) apply here.
8604
8761
  | `xAxisId` | `string` | `"x"` | Binding to an x axis. |
8605
8762
  | `yAxisId` | `string` | `"y"` | Binding to a y axis. |
8606
8763
  | `name` | `string` | "Series n", after its place in the chart | The name in legend and tooltip. Without one a warning stands in DEV - an unnamed series is a colour nobody can look up.<br>Shown in: [Chart a process against its control limits](#controlchart), [List the rule violations](#controlchart), [Choose the rules](#controlchart) and 1 more |
8607
- | `format` | `(value: number) => string` | the y axis' `tickFormat`, else the built-in number format | The value as the tooltip writes it - on the line and on the violations. |
8764
+ | `format` | `ValueFormat` | the y axis' `tickFormat`, else the built-in number format | The value as the tooltip writes it - on the line and on the violations., as a function or `NumberOptions`. |
8608
8765
  | `color` | `string` | the palette's `--uc-series-N` | Any CSS colour value. |
8609
8766
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value for the line; the theme resolves it. The violations are always "alarm". |
8610
8767
  | `rules` | `Partial<``RuleOptions``>` | — | Switchable one by one; run lengths are parameters.<br>Shown in: [Choose the rules](#controlchart) |
@@ -8643,6 +8800,15 @@ type ControlLimitOrigin =
8643
8800
  | { readonly kind: "referenceWindow"; readonly from: number; readonly to: number };
8644
8801
  ```
8645
8802
 
8803
+ ###### `ValueFormat`
8804
+
8805
+ A value format: a function, or Intl's options written in the chart's
8806
+ wording's locale.
8807
+
8808
+ ```ts
8809
+ type ValueFormat = ((value: number) => string) | NumberOptions;
8810
+ ```
8811
+
8646
8812
  ###### `RuleOptions`
8647
8813
 
8648
8814
  Which rules run, and with which run lengths.
@@ -8689,6 +8855,17 @@ data reference then. A native or bound function is compared by identity.
8689
8855
  type ValueFunction<T> = (d: T, index: number) => number | null | undefined;
8690
8856
  ```
8691
8857
 
8858
+ ###### `NumberOptions`
8859
+
8860
+ The options of `Intl.NumberFormat`, plus `decimals` for a fixed number of
8861
+ places (minimum and maximum alike; an Intl digit option given beside it
8862
+ wins). The same shape as core's, declared here because the charts import
8863
+ nothing.
8864
+
8865
+ | Prop | Type | Default | Description |
8866
+ |---|---|---|---|
8867
+ | `decimals` | `number` | — | |
8868
+
8692
8869
  ###### `RuleName`
8693
8870
 
8694
8871
  Name of a rule, the way it is switched on and off in `violations`.
@@ -8735,7 +8912,7 @@ export default function ParetoExample() {
8735
8912
  const { entries } = useMemo(() => pareto(FAILED_DELIVERIES, { collectRank: 6, remainderName: "Other" }), []);
8736
8913
  const { Chart, XAxis, YAxis, Bar, Line } = useChart(entries);
8737
8914
  return (
8738
- <Chart height={300} ariaLabel="Failed deliveries by cause, sorted, with the cumulative share">
8915
+ <Chart height={300} aria-label="Failed deliveries by cause, sorted, with the cumulative share">
8739
8916
  <XAxis value="name" />
8740
8917
  <YAxis label="Failed deliveries" />
8741
8918
  <YAxis
@@ -8768,12 +8945,6 @@ From `ChartProps` — the full table stands on [Chart](#chart-1).
8768
8945
  | [`height`](#chart-1) | `number` | fills the frame, at least 300 px | Height in CSS pixels, fixed. Left out, the chart fills its frame - a stretched grid cell, a card the grid stretches, a container of definite height - and never goes below 300 px, which is also its height in a frame without one. A different floor goes through `style`'s `minHeight` ([ADR-0050](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0050-a-chart-fills-its-frame-never-below-its-floor.md)). |
8769
8946
  | [`children`](#chart-1) | `ReactNode` | — | The axes, series and companions of this chart. They draw nothing themselves: each registers its configuration and the scene draws it, and the order in the JSX is the drawing order. |
8770
8947
 
8771
- ###### Accessibility
8772
-
8773
- | Prop | Type | Default | Description |
8774
- |---|---|---|---|
8775
- | [`ariaLabel`](#chart-1) | `string` | — | Required in DEV through a warning. |
8776
-
8777
8948
  From `XAxisProps` — the full table stands on [Axis](#axis).
8778
8949
 
8779
8950
  | Prop | Type | Default | Description |
@@ -8781,7 +8952,7 @@ From `XAxisProps` — the full table stands on [Axis](#axis).
8781
8952
  | [`value`](#axis) *required* | [`AxisField`](#axis)`<T> \| ((d: T, index: number) => `[`AxisValue`](#axis)`)` | — | Where a row lies on this axis: a field of the row, compared by its name, or a function of it, compared by its source text as a series' is (`ValueFunction`) - with the same closure limit. A number is a position, a `Date` an instant, a string a category: every distinct one gets a place of its own, in the order the rows bring them, a tick and its name as the label ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)). The type decides, never the content - `"2024"` is a category, `2024` a number - and a row without a value has no place. The axis reads the rows of every series bound to it; where those bring their own `data`, name their row: `<XAxis<HourCount> value="hour" />`. |
8782
8953
  | [`id`](#axis) | `string` | `"x"` | Axis id, through which series bind themselves. |
8783
8954
  | [`label`](#axis) | `string` | — | Axis title. |
8784
- | [`tickFormat`](#axis) | `(v: number) => string` | — | Label of a tick. Compared by its source text: one that reads a changed closure variable under the same text does not relabel the axis - give it a new text or remount. A bound `Intl.NumberFormat#format` is compared by identity. |
8955
+ | [`tickFormat`](#axis) | [`ValueFormat`](#axis) | — | Label of a tick. Compared by its source text: one that reads a changed closure variable under the same text does not relabel the axis - give it a new text or remount. A bound `Intl.NumberFormat#format` is compared by identity. Or `NumberOptions`: Intl's options in the wording's locale, compared by value; the step's places stay unless the options set digits. |
8785
8956
 
8786
8957
  From `YAxisProps` — the full table stands on [Axis](#axis).
8787
8958
 
@@ -8791,7 +8962,7 @@ From `YAxisProps` — the full table stands on [Axis](#axis).
8791
8962
  | [`position`](#axis) | `"left" \| "right"` | `"left"` | Which edge the axis stands at. Several y axes per side are stacked outwards, so that extents of clearly different magnitude stay readable. |
8792
8963
  | [`id`](#axis) | `string` | `"y"` | Axis id, through which series bind themselves. |
8793
8964
  | [`label`](#axis) | `string` | — | Axis title. |
8794
- | [`tickFormat`](#axis) | `(v: number) => string` | — | Label of a tick. Compared by its source text: one that reads a changed closure variable under the same text does not relabel the axis - give it a new text or remount. A bound `Intl.NumberFormat#format` is compared by identity. |
8965
+ | [`tickFormat`](#axis) | [`ValueFormat`](#axis) | — | Label of a tick. Compared by its source text: one that reads a changed closure variable under the same text does not relabel the axis - give it a new text or remount. A bound `Intl.NumberFormat#format` is compared by identity. Or `NumberOptions`: Intl's options in the wording's locale, compared by value; the step's places stay unless the options set digits. |
8795
8966
 
8796
8967
  From `LineProps` — the full table stands on [Line](#line).
8797
8968
 
@@ -8954,7 +9125,7 @@ const SIGN_IN = metrics("sign-in");
8954
9125
  function Latencies({ mode }: { mode: "x" | "nearest" }) {
8955
9126
  const { Chart, XAxis, YAxis, Line } = useChart(SEARCH);
8956
9127
  return (
8957
- <Chart height={220} ariaLabel={`Search and sign-in latency, tooltip mode ${mode}`}>
9128
+ <Chart height={220} aria-label={`Search and sign-in latency, tooltip mode ${mode}`}>
8958
9129
  <XAxis value="t" time tickCount={4} />
8959
9130
  <YAxis label="ms" />
8960
9131
  <Line value="p95" name="Search" />
@@ -9095,7 +9266,7 @@ function AgainstObjective({ hit }: { hit: TooltipHit<MetricPoint> }) {
9095
9266
  export default function OwnContent() {
9096
9267
  const { Chart, XAxis, YAxis, Line } = useChart(CHECKOUT);
9097
9268
  return (
9098
- <Chart height={280} ariaLabel="Checkout and billing latency, the tooltip against their objectives">
9269
+ <Chart height={280} aria-label="Checkout and billing latency, the tooltip against their objectives">
9099
9270
  <XAxis value="t" time />
9100
9271
  <YAxis label="ms" />
9101
9272
  <Line value="p95" name="Checkout" />
@@ -9109,7 +9280,7 @@ export default function OwnContent() {
9109
9280
 
9110
9281
  ##### Write each value in its unit
9111
9282
 
9112
- `format` on a series writes its value in the tooltip; without it the y axis' `tickFormat` does, and without that a bare number.
9283
+ `format` on a series writes its value in the tooltip; without it the y axis' `tickFormat` does, and without that a bare number. Either is a function or Intl's options, written in the chart's wording's locale - the same object a table column takes.
9113
9284
 
9114
9285
  ```tsx
9115
9286
  import { Legend, Tooltip, useChart } from "@umriss-ui/charts";
@@ -9198,12 +9369,12 @@ function metrics(serviceId: string): MetricPoint[] {
9198
9369
  const CHECKOUT = metrics("checkout");
9199
9370
 
9200
9371
  const milliseconds = (v: number) => `${v.toFixed(0)} ms`;
9201
- const percent = (v: number) => `${v.toFixed(2)} %`;
9372
+ const percent = { decimals: 2, style: "unit", unit: "percent" } as const;
9202
9373
 
9203
9374
  export default function ValueFormat() {
9204
9375
  const { Chart, XAxis, YAxis, Line } = useChart(CHECKOUT);
9205
9376
  return (
9206
- <Chart height={260} ariaLabel="Checkout's latency and error rate with their units">
9377
+ <Chart height={260} aria-label="Checkout's latency and error rate with their units">
9207
9378
  <XAxis value="t" time />
9208
9379
  <YAxis label="ms" />
9209
9380
  <YAxis id="errors" position="right" label="Errors %" />
@@ -9292,7 +9463,7 @@ const hours = (v: number) => `${v.toFixed(0)} h`;
9292
9463
  export default function ValuesAsTable() {
9293
9464
  const { Chart, XAxis, YAxis, Line } = useChart(BURNDOWN);
9294
9465
  return (
9295
- <Chart height={260} ariaLabel="Sprint 14: hours left against the ideal">
9466
+ <Chart height={260} aria-label="Sprint 14: hours left against the ideal">
9296
9467
  <XAxis value="t" ticks={BURNDOWN.map((d) => d.t)} tickFormat={day} />
9297
9468
  <YAxis label="Hours left" />
9298
9469
  <Line value="ideal" name="Ideal" format={hours} />
@@ -9413,7 +9584,7 @@ const milliseconds = (v: number) => `${v.toFixed(0)} ms`;
9413
9584
  export default function WeekAsTable() {
9414
9585
  const { Chart, XAxis, YAxis, Line } = useChart(SEARCH_WEEK);
9415
9586
  return (
9416
- <Chart height={240} ariaLabel="Search latency over last week, a reading a minute">
9587
+ <Chart height={240} aria-label="Search latency over last week, a reading a minute">
9417
9588
  <XAxis value="t" time domain="data" zoomable label="Time" />
9418
9589
  <YAxis label="ms" />
9419
9590
  <Line value="p95" name="p95" format={milliseconds} />
@@ -9443,7 +9614,7 @@ const LATENCY = [
9443
9614
  export default function Limits() {
9444
9615
  const { Chart, XAxis, YAxis, Line } = useChart(LATENCY);
9445
9616
  return (
9446
- <Chart height={260} ariaLabel="Checkout's latency against its objective and its warning band">
9617
+ <Chart height={260} aria-label="Checkout's latency against its objective and its warning band">
9447
9618
  <XAxis value="x" tickFormat={(v) => LATENCY[Math.round(v)]?.time ?? ""} />
9448
9619
  <YAxis label="ms" tickFormat={(v) => `${v} ms`} />
9449
9620
  <LimitBand from={250} to={300} severity="warning" label="Warning band" />
@@ -9477,7 +9648,7 @@ export default function ChooseAHit() {
9477
9648
  const { Chart, XAxis, YAxis, Line } = useChart(ORDERS, { onSelect: setChosen });
9478
9649
  return (
9479
9650
  <>
9480
- <Chart height={260} ariaLabel="Orders per hour, by channel">
9651
+ <Chart height={260} aria-label="Orders per hour, by channel">
9481
9652
  <XAxis value="hour" tickFormat={(v) => `${v}:00`} />
9482
9653
  <YAxis label="orders" />
9483
9654
  <Line value="web" name="Web" />
@@ -9501,6 +9672,114 @@ export default function ChooseAHit() {
9501
9672
  }
9502
9673
  ```
9503
9674
 
9675
+ ##### Drill down from a year to its days
9676
+
9677
+ `onSelect` on a bar drills in: a year opens its months, a month its days. The chart keeps no level and draws none - the stack of levels is this example's state, and the rows and the categories follow it ([ADR-0056](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0056-a-chart-reports-a-chosen-hit-and-holds-no-selection.md)).
9678
+
9679
+ ```tsx
9680
+ import { useState } from "react";
9681
+ import { Tooltip, useChart } from "@umriss-ui/charts";
9682
+
9683
+ /* Orders per day for 2024 and 2025, written out here so the example runs on its own: each day's
9684
+ count follows from its date, and a month and a year are the sums of the days in them. */
9685
+ const YEARS = [2024, 2025];
9686
+ const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
9687
+ const DAYS = YEARS.flatMap((year) =>
9688
+ MONTHS.flatMap((_, month) => {
9689
+ const length = new Date(year, month + 1, 0).getDate();
9690
+ return Array.from({ length }, (_, index) => {
9691
+ const day = index + 1;
9692
+ const weekend = [0, 6].includes(new Date(year, month, day).getDay());
9693
+ return { year, month, day, orders: ((year * 31 + month * 7 + day * 13) % 40) + (weekend ? 30 : 70) };
9694
+ });
9695
+ }),
9696
+ );
9697
+
9698
+ interface Level {
9699
+ year: number;
9700
+ month?: number;
9701
+ }
9702
+
9703
+ interface Row {
9704
+ level: "year" | "month" | "day";
9705
+ year: number;
9706
+ month: number;
9707
+ label: string;
9708
+ orders: number;
9709
+ }
9710
+
9711
+ function sum(days: readonly { orders: number }[]): number {
9712
+ return days.reduce((total, one) => total + one.orders, 0);
9713
+ }
9714
+
9715
+ /* The rows of the level the stack stands on: the years, the months of one year, or the days of one month. */
9716
+ function rowsAt(stack: readonly Level[]): Row[] {
9717
+ const top = stack.at(-1);
9718
+ if (top === undefined) {
9719
+ return YEARS.map((year) => ({
9720
+ level: "year",
9721
+ year,
9722
+ month: 0,
9723
+ label: String(year),
9724
+ orders: sum(DAYS.filter((d) => d.year === year)),
9725
+ }));
9726
+ }
9727
+ if (top.month === undefined) {
9728
+ return MONTHS.map((name, month) => ({
9729
+ level: "month",
9730
+ year: top.year,
9731
+ month,
9732
+ label: name,
9733
+ orders: sum(DAYS.filter((d) => d.year === top.year && d.month === month)),
9734
+ }));
9735
+ }
9736
+ return DAYS.filter((d) => d.year === top.year && d.month === top.month).map((d) => ({
9737
+ level: "day",
9738
+ year: d.year,
9739
+ month: d.month,
9740
+ label: String(d.day),
9741
+ orders: d.orders,
9742
+ }));
9743
+ }
9744
+
9745
+ export default function DrillDown() {
9746
+ const [stack, setStack] = useState<Level[]>([]);
9747
+ const rows = rowsAt(stack);
9748
+ const top = stack.at(-1);
9749
+ const where = top === undefined ? "all years" : top.month === undefined ? String(top.year) : `${MONTHS[top.month]} ${top.year}`;
9750
+ /* The level the back button returns to: the months of a year are named by the year, the years by "all years". */
9751
+ const above = stack.length <= 1 ? "all years" : String(stack[0]!.year);
9752
+ const { Chart, XAxis, YAxis, Bar } = useChart(rows, {
9753
+ onSelect: (hit) => {
9754
+ const row = hit.points[0]?.datum;
9755
+ if (row === undefined || row.level === "day") return;
9756
+ setStack((current) => [...current, row.level === "year" ? { year: row.year } : { year: row.year, month: row.month }]);
9757
+ },
9758
+ });
9759
+ return (
9760
+ <>
9761
+ <dl className="metrics">
9762
+ <div>
9763
+ <dt>Showing</dt>
9764
+ <dd>{where}</dd>
9765
+ </div>
9766
+ </dl>
9767
+ {top !== undefined && (
9768
+ <button type="button" onClick={() => setStack((current) => current.slice(0, -1))}>
9769
+ Back to {above}
9770
+ </button>
9771
+ )}
9772
+ <Chart height={260} aria-label={`Orders, ${where}`}>
9773
+ <XAxis value="label" />
9774
+ <YAxis label="orders" />
9775
+ <Bar value="orders" name="Orders" />
9776
+ <Tooltip mode="x" />
9777
+ </Chart>
9778
+ </>
9779
+ );
9780
+ }
9781
+ ```
9782
+
9504
9783
  ##### Read a stack's total in the table
9505
9784
 
9506
9785
  The table of a stacked chart ends the stack's members with a Total column - the sum the tooltip shows at the same place, so nobody adds the row up by hand.
@@ -9520,7 +9799,7 @@ const TICKETS = [
9520
9799
  export default function TotalOfAStack() {
9521
9800
  const { Chart, XAxis, YAxis, Bar } = useChart(TICKETS);
9522
9801
  return (
9523
- <Chart height={260} ariaLabel="Tickets opened per working day, by channel">
9802
+ <Chart height={260} aria-label="Tickets opened per working day, by channel">
9524
9803
  <XAxis value="day" />
9525
9804
  <YAxis label="Tickets" />
9526
9805
  <Bar value="mail" name="Mail" stack="tickets" />
@@ -9544,7 +9823,7 @@ The keys of [Chart](#chart-1) apply here.
9544
9823
 
9545
9824
  | Prop | Type | Default | Description |
9546
9825
  |---|---|---|---|
9547
- | `mode` | `"x" \| "nearest"` | `"x"` | "x": every series at the x position; "nearest": only the nearest point.<br>Shown in: [Report every series or the nearest](#tooltip), [Write the tooltip yourself](#tooltip), [Write each value in its unit](#tooltip) and 61 more |
9826
+ | `mode` | `"x" \| "nearest"` | `"x"` | "x": every series at the x position; "nearest": only the nearest point.<br>Shown in: [Report every series or the nearest](#tooltip), [Write the tooltip yourself](#tooltip), [Write each value in its unit](#tooltip) and 62 more |
9548
9827
  | `limits` | `boolean` | `false` | Adds a row for every labelled `LimitLine` and `LimitBand` on the hit's y axis - its label and value in that axis' format - to the built-in tooltip, and the same to what a key reads out. Limits are not series and stay out of the tooltip unless asked for.<br>Shown in: [Read the limits at the hit](#tooltip) |
9549
9828
  | `render` | `(hit: ``TooltipHit``<T>) => ReactNode` | the built-in tooltip | Render prop for content of one's own. Compared by its source text, like `tickFormat` - with the same closure limit.<br>Shown in: [Write the tooltip yourself](#tooltip), [Fix the domain and name the ticks (Axis)](#axis) |
9550
9829
 
@@ -9559,7 +9838,7 @@ The keys of [Chart](#chart-1) apply here.
9559
9838
  | Prop | Type | Default | Description |
9560
9839
  |---|---|---|---|
9561
9840
  | `onViewChange` | `(view: `[`ChartView`](#view)`) => void` | — | Every change the reader makes, once, always the whole view - the one to keep, or to hand another chart's `setView`. A pan or zoom is reported at most once per frame. Not called for the view the chart starts with, nor for one `setView` applies.<br>Shown in: [Keep charts in step (Zoom and pan)](#zoom-and-pan), [Read it by keyboard and screen reader (Chart)](#chart-1) |
9562
- | `onSelect` | `(hit: ``TooltipHit``<Z>) => void` | — | The reader chose a point: a click on a hit (a tap too), or Enter on the Active point - the same hit the tooltip's `render` receives. Once per click; the end of a drag does not report, and the second click of a double click does not either. The chart holds no selection and draws none: what is chosen is the application's state. Needs a `<Tooltip>`, which makes the hits; the hit does not travel over `syncId`.<br>Shown in: [Choose a hit](#tooltip) |
9841
+ | `onSelect` | `(hit: ``TooltipHit``<Z>) => void` | — | The reader chose a point: a click on a hit (a tap too), or Enter on the Active point - the same hit the tooltip's `render` receives. Once per click; the end of a drag does not report, and the second click of a double click does not either. The chart holds no selection and draws none: what is chosen is the application's state. Needs a `<Tooltip>`, which makes the hits; the hit does not travel over `syncId`.<br>Shown in: [Choose a hit](#tooltip), [Drill down from a year to its days](#tooltip) |
9563
9842
 
9564
9843
  ##### Types on this page
9565
9844
 
@@ -9571,7 +9850,7 @@ A custom tooltip's `render` receives it.
9571
9850
  | Prop | Type | Default | Description |
9572
9851
  |---|---|---|---|
9573
9852
  | `xValue` *required* | `number` | — | <br>Shown in: [Write the tooltip yourself](#tooltip), [Choose a hit](#tooltip), [Fix the domain and name the ticks (Axis)](#axis) |
9574
- | `points` *required* | `readonly ``TooltipPoint``<T>[]` | — | <br>Shown in: [Write the tooltip yourself](#tooltip), [Choose a hit](#tooltip), [Fix the domain and name the ticks (Axis)](#axis) |
9853
+ | `points` *required* | `readonly ``TooltipPoint``<T>[]` | — | <br>Shown in: [Write the tooltip yourself](#tooltip), [Choose a hit](#tooltip), [Drill down from a year to its days](#tooltip) and 1 more |
9575
9854
  | `xPx` *required* | `number` | — | Pixel position of the crosshair (snapped to the data point). |
9576
9855
  | `yPx` *required* | `number` | — | Pixel position of the primary hit. |
9577
9856
 
@@ -9585,7 +9864,7 @@ One hit per series in the tooltip.
9585
9864
  | `color` *required* | `string` | — | |
9586
9865
  | `xValue` *required* | `number` | — | The point's x value on its own x axis - with several x axes it can differ from the hit's. |
9587
9866
  | `yValue` *required* | `number` | — | The point's y value; in a stack its own value, not its top - in a normalised stack its share in percent.<br>Shown in: [Choose a hit](#tooltip), [Fix the domain and name the ticks (Axis)](#axis) |
9588
- | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself](#tooltip) |
9867
+ | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself](#tooltip), [Drill down from a year to its days](#tooltip) |
9589
9868
  | `index` *required* | `number` | — | |
9590
9869
  | `level` | `number` | — | Only for the matrix: its level, out of the level channel. It stands here and not in yValue, because yValue is the position on the y axis - for a matrix therefore the row. Putting both into one field would be exactly the overloading [ADR-0011](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0011-named-channels-never-overloaded.md) is written against. |
9591
9870
  | `segment` | `{ from: number; to: number; label: string }` | — | Only for state series: the section being pointed at. A duration does not stand here - it is the difference of two numbers the entry already carries, and formatting it would presuppose knowing what the x axis means. The caller knows that, not this library. |
@@ -9740,7 +10019,7 @@ export default function HideASeries() {
9740
10019
  Show all
9741
10020
  </button>
9742
10021
  </div>
9743
- <Chart height={260} ariaLabel="Latency of three services">
10022
+ <Chart height={260} aria-label="Latency of three services">
9744
10023
  <XAxis value="t" time />
9745
10024
  <YAxis label="ms" />
9746
10025
  {SERIES.map((one) => (
@@ -9832,7 +10111,7 @@ const monthName = (month: string) => MONTH_NAMES[Number(month.slice(5)) - 1] ??
9832
10111
  function Marketing({ placement }: { placement?: "bottom" }) {
9833
10112
  const { Chart, XAxis, YAxis, Line } = useChart(MARKETING);
9834
10113
  return (
9835
- <Chart height={220} ariaLabel={`Marketing's budget and forecast, legend ${placement ?? "above"}`}>
10114
+ <Chart height={220} aria-label={`Marketing's budget and forecast, legend ${placement ?? "above"}`}>
9836
10115
  <XAxis value="month" categoryLabel={monthName} />
9837
10116
  <YAxis label="€" />
9838
10117
  <Line value="budget" name="Budget" />
@@ -10118,7 +10397,7 @@ export default function Benchmark() {
10118
10397
  >
10119
10398
  <Chart
10120
10399
  height={320}
10121
- ariaLabel="Benchmark with three series"
10400
+ aria-label="Benchmark with three series"
10122
10401
  onPerf={onPerf}
10123
10402
  >
10124
10403
  <XAxis value={matrix ? "c" : "t"} label="Index" domain="data" zoomable />
@@ -10265,7 +10544,7 @@ export default function WeekOfSeconds() {
10265
10544
  <dd>{drawMs.toFixed(1)} ms</dd>
10266
10545
  </div>
10267
10546
  </dl>
10268
- <Chart height={300} ariaLabel="Search latency and requests over last week, a reading a second" onPerf={onPerf}>
10547
+ <Chart height={300} aria-label="Search latency and requests over last week, a reading a second" onPerf={onPerf}>
10269
10548
  <XAxis value="t" time domain="data" zoomable />
10270
10549
  <YAxis label="ms" domain="visible" />
10271
10550
  <YAxis id="requests" position="right" label="Requests/min" domain="visible" />
@@ -10298,12 +10577,6 @@ From `ChartProps` — the full table stands on [Chart](#chart-1).
10298
10577
  |---|---|---|---|
10299
10578
  | [`onPerf`](#chart-1) | `(perf: `[`ChartPerf`](#chart-1)`) => void` | — | Instrumentation for the benchmark page; not needed otherwise. |
10300
10579
 
10301
- ###### Accessibility
10302
-
10303
- | Prop | Type | Default | Description |
10304
- |---|---|---|---|
10305
- | [`ariaLabel`](#chart-1) | `string` | — | Required in DEV through a warning. |
10306
-
10307
10580
  From `XAxisProps` — the full table stands on [Axis](#axis).
10308
10581
 
10309
10582
  | Prop | Type | Default | Description |
@@ -10825,7 +11098,7 @@ An `Area` as the chart holds it once registered.
10825
11098
  | `xAxisId` *required* | `string` | — | From `SeriesBase`. |
10826
11099
  | `yAxisId` *required* | `string` | — | From `SeriesBase`. |
10827
11100
  | `name` | `string` | — | From `SeriesBase`. |
10828
- | `format` | `(value: number) => string` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. From `SeriesBase`. |
11101
+ | `format` | `ValueFormat` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. Or `NumberOptions`: Intl's options in the wording's locale, compared by value. From `SeriesBase`. |
10829
11102
  | `color` | `string` | — | From `SeriesBase`. |
10830
11103
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value: the theme resolves it, in both themes. For series that mean something rather than just being the next one - the violating points of a control chart, say. `color` beats it. From `SeriesBase`. |
10831
11104
  | `stack` | `string` | — | The stack this series stands in: series with the same id, on the same x and y axis, stack in registration order. From `Stackable`. |
@@ -10854,7 +11127,7 @@ An `XAxis` or `YAxis` as the chart holds it once registered.
10854
11127
  | `accessor` | `(d: T, index: number) => ``AxisValue` | — | Where a row lies along the axis; an x axis' only - along y the series place their rows. A number is a position or an instant, a string a category, a `Date` an instant ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)). |
10855
11128
  | `label` | `string` | — | |
10856
11129
  | `tickCount` | `number` | — | |
10857
- | `tickFormat` | `(v: number) => string` | — | |
11130
+ | `tickFormat` | `ValueFormat` | — | |
10858
11131
  | `domain` *required* | `"nice" \| "data" \| "visible" \| readonly [number, number]` | — | "visible" is a y axis' only: the extent of what its series show inside their x axis' domain. |
10859
11132
  | `grid` | `boolean` | only the first registered axis per orientation | Whether the axis draws its grid lines. |
10860
11133
  | `ticks` | `readonly number[]` | — | Fixed tick values instead of the 1-2-5 algorithm, on a numeric axis: an hour axis' named hours, a lane grid. |
@@ -10935,7 +11208,7 @@ A `Bar` as the chart holds it once registered.
10935
11208
  | `xAxisId` *required* | `string` | — | From `SeriesBase`. |
10936
11209
  | `yAxisId` *required* | `string` | — | From `SeriesBase`. |
10937
11210
  | `name` | `string` | — | From `SeriesBase`. |
10938
- | `format` | `(value: number) => string` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. From `SeriesBase`. |
11211
+ | `format` | `ValueFormat` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. Or `NumberOptions`: Intl's options in the wording's locale, compared by value. From `SeriesBase`. |
10939
11212
  | `color` | `string` | — | From `SeriesBase`. |
10940
11213
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value: the theme resolves it, in both themes. For series that mean something rather than just being the next one - the violating points of a control chart, say. `color` beats it. From `SeriesBase`. |
10941
11214
  | `stack` | `string` | — | The stack this series stands in: series with the same id, on the same x and y axis, stack in registration order. From `Stackable`. |
@@ -11030,7 +11303,7 @@ chose - the library draws them and computes none.
11030
11303
  | `xAxisId` *required* | `string` | — | From `SeriesBase`. |
11031
11304
  | `yAxisId` *required* | `string` | — | From `SeriesBase`. |
11032
11305
  | `name` | `string` | — | From `SeriesBase`. |
11033
- | `format` | `(value: number) => string` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. From `SeriesBase`. |
11306
+ | `format` | `ValueFormat` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. Or `NumberOptions`: Intl's options in the wording's locale, compared by value. From `SeriesBase`. |
11034
11307
  | `color` | `string` | — | From `SeriesBase`. |
11035
11308
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value: the theme resolves it, in both themes. For series that mean something rather than just being the next one - the violating points of a control chart, say. `color` beats it. From `SeriesBase`. |
11036
11309
 
@@ -11055,12 +11328,12 @@ is typed by that `data` instead.
11055
11328
 
11056
11329
  | Prop | Type | Default | Description |
11057
11330
  |---|---|---|---|
11058
- | `Chart` *required* | `(props: `[`ChartProps`](#chart-1)`) => ReactNode` | — | The chart over the hook's rows. One per `useChart`: it holds the view, and a second chart is a second call.<br>Shown in: [Draw a first chart (Installation)](#installation), [Size a chart by its container (Installation)](#installation), [Mix series kinds (Chart)](#chart-1) and 74 more |
11059
- | `XAxis` *required* | `<T = Z>(props: `[`XAxisProps`](#axis)`<T>) => null` | — | An x axis reads the rows of every series bound to it: the hook's, or with `<XAxis<Row>>` those the series bring as their own `data`.<br>Shown in: [Draw a first chart (Installation)](#installation), [Size a chart by its container (Installation)](#installation), [Mix series kinds (Chart)](#chart-1) and 74 more |
11060
- | `YAxis` *required* | `(props: `[`YAxisProps`](#axis)`) => null` | — | <br>Shown in: [Draw a first chart (Installation)](#installation), [Size a chart by its container (Installation)](#installation), [Mix series kinds (Chart)](#chart-1) and 74 more |
11331
+ | `Chart` *required* | `(props: `[`ChartProps`](#chart-1)`) => ReactNode` | — | The chart over the hook's rows. One per `useChart`: it holds the view, and a second chart is a second call.<br>Shown in: [Draw a first chart (Installation)](#installation), [Size a chart by its container (Installation)](#installation), [Mix series kinds (Chart)](#chart-1) and 75 more |
11332
+ | `XAxis` *required* | `<T = Z>(props: `[`XAxisProps`](#axis)`<T>) => null` | — | An x axis reads the rows of every series bound to it: the hook's, or with `<XAxis<Row>>` those the series bring as their own `data`.<br>Shown in: [Draw a first chart (Installation)](#installation), [Size a chart by its container (Installation)](#installation), [Mix series kinds (Chart)](#chart-1) and 75 more |
11333
+ | `YAxis` *required* | `(props: `[`YAxisProps`](#axis)`) => null` | — | <br>Shown in: [Draw a first chart (Installation)](#installation), [Size a chart by its container (Installation)](#installation), [Mix series kinds (Chart)](#chart-1) and 75 more |
11061
11334
  | `Line` *required* | `<T = Z>(props: `[`LineProps`](#line)`<T>) => null` | — | <br>Shown in: [Draw a first chart (Installation)](#installation), [Size a chart by its container (Installation)](#installation), [Mix series kinds (Chart)](#chart-1) and 47 more |
11062
11335
  | `Area` *required* | `<T = Z>(props: `[`AreaProps`](#area)`<T>) => null` | — | <br>Shown in: [Mix series kinds (Chart)](#chart-1), [Mark every kind (Chart)](#chart-1), [Bind every kind of series to a second axis (Axis)](#axis) and 5 more |
11063
- | `Bar` *required* | `<T = Z>(props: `[`BarProps`](#bar)`<T>) => null` | — | <br>Shown in: [Mix series kinds (Chart)](#chart-1), [Mark every kind (Chart)](#chart-1), [Bind every kind of series to a second axis (Axis)](#axis) and 13 more |
11336
+ | `Bar` *required* | `<T = Z>(props: `[`BarProps`](#bar)`<T>) => null` | — | <br>Shown in: [Mix series kinds (Chart)](#chart-1), [Mark every kind (Chart)](#chart-1), [Bind every kind of series to a second axis (Axis)](#axis) and 14 more |
11064
11337
  | `Scatter` *required* | `<T = Z>(props: `[`ScatterProps`](#scatter)`<T>) => null` | — | <br>Shown in: [Mix series kinds (Chart)](#chart-1), [Mark every kind (Chart)](#chart-1), [Bind every kind of series to a second axis (Axis)](#axis) and 3 more |
11065
11338
  | `StateBand` *required* | `<T = Z>(props: `[`StateBandProps`](#stateband)`<T> & ``OneLane``) => null` | — | <br>Shown in: [Mark bands, limits and cells (Chart)](#chart-1), [One vehicle through the day (StateBand)](#stateband), [Lay a state under a course (StateBand)](#stateband) and 4 more |
11066
11339
  | `Matrix` *required* | `<T = Z>(props: `[`MatrixProps`](#matrix)`<T>) => null` | — | <br>Shown in: [Mark bands, limits and cells (Chart)](#chart-1), [Colour cells by their limits (Matrix)](#matrix), [Colour cells along a gradient (Matrix)](#matrix) and 1 more |
@@ -11311,7 +11584,7 @@ A `Line` as the chart holds it once registered.
11311
11584
  | `xAxisId` *required* | `string` | — | From `SeriesBase`. |
11312
11585
  | `yAxisId` *required* | `string` | — | From `SeriesBase`. |
11313
11586
  | `name` | `string` | — | From `SeriesBase`. |
11314
- | `format` | `(value: number) => string` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. From `SeriesBase`. |
11587
+ | `format` | `ValueFormat` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. Or `NumberOptions`: Intl's options in the wording's locale, compared by value. From `SeriesBase`. |
11315
11588
  | `color` | `string` | — | From `SeriesBase`. |
11316
11589
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value: the theme resolves it, in both themes. For series that mean something rather than just being the next one - the violating points of a control chart, say. `color` beats it. From `SeriesBase`. |
11317
11590
 
@@ -11402,7 +11675,7 @@ series kind whose base accessor reads more than a number.
11402
11675
  | `xAxisId` *required* | `string` | — | From `SeriesBase`. |
11403
11676
  | `yAxisId` *required* | `string` | — | From `SeriesBase`. |
11404
11677
  | `name` | `string` | — | From `SeriesBase`. |
11405
- | `format` | `(value: number) => string` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. From `SeriesBase`. |
11678
+ | `format` | `ValueFormat` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. Or `NumberOptions`: Intl's options in the wording's locale, compared by value. From `SeriesBase`. |
11406
11679
  | `color` | `string` | — | From `SeriesBase`. |
11407
11680
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value: the theme resolves it, in both themes. For series that mean something rather than just being the next one - the violating points of a control chart, say. `color` beats it. From `SeriesBase`. |
11408
11681
 
@@ -11418,6 +11691,17 @@ type NumberField<T> = NoInfer<
11418
11691
  >;
11419
11692
  ```
11420
11693
 
11694
+ ##### `NumberOptions`
11695
+
11696
+ The options of `Intl.NumberFormat`, plus `decimals` for a fixed number of
11697
+ places (minimum and maximum alike; an Intl digit option given beside it
11698
+ wins). The same shape as core's, declared here because the charts import
11699
+ nothing.
11700
+
11701
+ | Prop | Type | Default | Description |
11702
+ |---|---|---|---|
11703
+ | `decimals` | `number` | — | |
11704
+
11421
11705
  ##### `OneLane`
11422
11706
 
11423
11707
  A lane is named or given by its edges: the compiler refuses both at once.
@@ -11598,7 +11882,7 @@ A `Scatter` as the chart holds it once registered.
11598
11882
  | `xAxisId` *required* | `string` | — | From `SeriesBase`. |
11599
11883
  | `yAxisId` *required* | `string` | — | From `SeriesBase`. |
11600
11884
  | `name` | `string` | — | From `SeriesBase`. |
11601
- | `format` | `(value: number) => string` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. From `SeriesBase`. |
11885
+ | `format` | `ValueFormat` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. Or `NumberOptions`: Intl's options in the wording's locale, compared by value. From `SeriesBase`. |
11602
11886
  | `color` | `string` | — | From `SeriesBase`. |
11603
11887
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value: the theme resolves it, in both themes. For series that mean something rather than just being the next one - the violating points of a control chart, say. `color` beats it. From `SeriesBase`. |
11604
11888
 
@@ -11614,7 +11898,7 @@ which axes it stands on, and how it is named and coloured.
11614
11898
  | `xAxisId` *required* | `string` | — | |
11615
11899
  | `yAxisId` *required* | `string` | — | |
11616
11900
  | `name` | `string` | — | |
11617
- | `format` | `(value: number) => string` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. |
11901
+ | `format` | `ValueFormat` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. Or `NumberOptions`: Intl's options in the wording's locale, compared by value. |
11618
11902
  | `color` | `string` | — | |
11619
11903
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value: the theme resolves it, in both themes. For series that mean something rather than just being the next one - the violating points of a control chart, say. `color` beats it. |
11620
11904
 
@@ -11691,7 +11975,7 @@ an index into `states`.
11691
11975
  | `xAxisId` *required* | `string` | — | From `SeriesBase`. |
11692
11976
  | `yAxisId` *required* | `string` | — | From `SeriesBase`. |
11693
11977
  | `name` | `string` | — | From `SeriesBase`. |
11694
- | `format` | `(value: number) => string` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. From `SeriesBase`. |
11978
+ | `format` | `ValueFormat` | — | The value in the tooltip; before the y axis' `tickFormat`. Compared by its source text, as an accessor is. Or `NumberOptions`: Intl's options in the wording's locale, compared by value. From `SeriesBase`. |
11695
11979
  | `color` | `string` | — | From `SeriesBase`. |
11696
11980
  | `tone` | `"ok" \| "warning" \| "alarm"` | — | A role instead of a colour value: the theme resolves it, in both themes. For series that mean something rather than just being the next one - the violating points of a control chart, say. `color` beats it. From `SeriesBase`. |
11697
11981
 
@@ -11713,7 +11997,7 @@ A custom tooltip's `render` receives it.
11713
11997
  | Prop | Type | Default | Description |
11714
11998
  |---|---|---|---|
11715
11999
  | `xValue` *required* | `number` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Write the tooltip yourself (Tooltip)](#tooltip), [Choose a hit (Tooltip)](#tooltip) |
11716
- | `points` *required* | `readonly ``TooltipPoint``<T>[]` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Write the tooltip yourself (Tooltip)](#tooltip), [Choose a hit (Tooltip)](#tooltip) |
12000
+ | `points` *required* | `readonly ``TooltipPoint``<T>[]` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Write the tooltip yourself (Tooltip)](#tooltip), [Choose a hit (Tooltip)](#tooltip) and 1 more |
11717
12001
  | `xPx` *required* | `number` | — | Pixel position of the crosshair (snapped to the data point). |
11718
12002
  | `yPx` *required* | `number` | — | Pixel position of the primary hit. |
11719
12003
 
@@ -11729,7 +12013,7 @@ One hit per series in the tooltip.
11729
12013
  | `color` *required* | `string` | — | |
11730
12014
  | `xValue` *required* | `number` | — | The point's x value on its own x axis - with several x axes it can differ from the hit's. |
11731
12015
  | `yValue` *required* | `number` | — | The point's y value; in a stack its own value, not its top - in a normalised stack its share in percent.<br>Shown in: [Fix the domain and name the ticks (Axis)](#axis), [Choose a hit (Tooltip)](#tooltip) |
11732
- | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself (Tooltip)](#tooltip) |
12016
+ | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself (Tooltip)](#tooltip), [Drill down from a year to its days (Tooltip)](#tooltip) |
11733
12017
  | `index` *required* | `number` | — | |
11734
12018
  | `level` | `number` | — | Only for the matrix: its level, out of the level channel. It stands here and not in yValue, because yValue is the position on the y axis - for a matrix therefore the row. Putting both into one field would be exactly the overloading [ADR-0011](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0011-named-channels-never-overloaded.md) is written against. |
11735
12019
  | `segment` | `{ from: number; to: number; label: string }` | — | Only for state series: the section being pointed at. A duration does not stand here - it is the difference of two numbers the entry already carries, and formatting it would presuppose knowing what the x axis means. The caller knows that, not this library. |
@@ -11748,6 +12032,17 @@ by its name, or a function, compared by its source text (`ValueFunction`).
11748
12032
  type Value<T> = NumberField<T> | ValueFunction<T>;
11749
12033
  ```
11750
12034
 
12035
+ ##### `ValueFormat`
12036
+
12037
+ A value format: a function, or Intl's options written in the chart's
12038
+ wording's locale.
12039
+
12040
+ ```ts
12041
+ type ValueFormat = ((value: number) => string) | NumberOptions;
12042
+ ```
12043
+
12044
+ Used on [Tooltip](#tooltip).
12045
+
11751
12046
  ##### `ValueFunction<T>`
11752
12047
 
11753
12048
  A **value** computed from the row - the function form of `Value`.