@umriss-ui/charts 0.14.0 → 0.16.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.14.0
1
+ # @umriss-ui/charts 0.16.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
 
@@ -2180,14 +2180,14 @@ Under forced colours the chart draws in the system colours and tells its series
2180
2180
  | Prop | Type | Default | Description |
2181
2181
  |---|---|---|---|
2182
2182
  | `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/in-its-container) |
2183
- | `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/mixed), [Say there is nothing to show](#/chart/empty), [Read it by keyboard and screen reader](#/chart/keyboard-and-screen-reader) and 70 more |
2183
+ | `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/mixed), [Say there is nothing to show](#/chart/empty), [Read it by keyboard and screen reader](#/chart/keyboard-and-screen-reader) and 74 more |
2184
2184
  | `padding` | `number \| Partial<`[`Padding`](#type-Padding)`>` | — | Outer spacing of the plot area in CSS pixels.<br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration) |
2185
2185
  | `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/empty) |
2186
2186
  | `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/loading) |
2187
2187
  | `wording` | `Partial<`[`ChartsWording`](#type-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. German: `GERMAN_CHARTS_WORDING` from `@umriss-ui/charts/wording/de`.<br>Shown in: [Read it by keyboard and screen reader](#/chart/keyboard-and-screen-reader) |
2188
2188
  | `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/told-apart-without-colour), [Mark every kind](#/chart/marks-on-every-kind), [Mark bands, limits and cells](#/chart/bands-limits-and-cells) |
2189
2189
  | `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 handing each other their view (`useChart`'s `initialView` and `onViewChange`).<br>Shown in: [Keep charts in step (Zoom and pan)](#/zoom-and-pan/cursor-sync), [Watch latency against its objective (Scenarios)](#/scenarios/watch-latency-against-its-objective), [Hold the tile length in control (Scenarios)](#/scenarios/hold-the-tile-length-in-control) |
2190
- | `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/mixed), [Say there is nothing to show](#/chart/empty), [Read it by keyboard and screen reader](#/chart/keyboard-and-screen-reader) and 70 more |
2190
+ | `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/mixed), [Say there is nothing to show](#/chart/empty), [Read it by keyboard and screen reader](#/chart/keyboard-and-screen-reader) and 74 more |
2191
2191
 
2192
2192
  ###### Events
2193
2193
 
@@ -2199,7 +2199,7 @@ Under forced colours the chart draws in the system colours and tells its series
2199
2199
 
2200
2200
  | Prop | Type | Default | Description |
2201
2201
  |---|---|---|---|
2202
- | `ariaLabel` | `string` | — | Required in DEV through a warning.<br>Shown in: [Mix series kinds](#/chart/mixed), [Say there is nothing to show](#/chart/empty), [Read it by keyboard and screen reader](#/chart/keyboard-and-screen-reader) and 70 more |
2202
+ | `ariaLabel` | `string` | — | Required in DEV through a warning.<br>Shown in: [Mix series kinds](#/chart/mixed), [Say there is nothing to show](#/chart/empty), [Read it by keyboard and screen reader](#/chart/keyboard-and-screen-reader) and 74 more |
2203
2203
 
2204
2204
  ###### Styling
2205
2205
 
@@ -2232,10 +2232,12 @@ Everything the charts put out as text.
2232
2232
  | `seriesList` *required* | `(names: readonly string[]) => string` | — | The summary's visible series, by name. |
2233
2233
  | `visibleRange` *required* | `(from: string, to: string) => string` | — | The summary's visible stretch of the x axis, both ends formatted. |
2234
2234
  | `seriesExtent` *required* | `(name: string, min: string, max: string) => string` | — | The summary's lowest and highest value of one series in that stretch. |
2235
+ | `limitList` *required* | `(limits: readonly { name: string; value: string }[]) => string` | — | The summary's labelled limits, each with its value in its axis' format (a band's two edges as one value). |
2235
2236
  | `readout` *required* | `(x: string, rows: readonly { name: string; value: string; x: string }[]) => string` | — | What is read after a key: the position, then each series with its value (and its own x where it stands on another x axis), the emphasised one first. |
2236
2237
  | `stackTotal` *required* | `string` | — | The last row of a stack in the tooltip and the readout: the sum of its members at the point (charts-stacking K4). |
2237
2238
  | `percent` *required* | `(value: string) => string` | — | A share in a normalised stack, its number already formatted (K5). |
2238
2239
  | `walkHelp` *required* | `string` | — | The keys that walk the chart. |
2240
+ | `selectHelp` *required* | `string` | — | The key that reports the Active point - only where `onSelect` is given. |
2239
2241
  | `zoomHelp` *required* | `string` | — | The keys that zoom and pan it - only where the chart can be zoomed. |
2240
2242
  | `legendHelp` *required* | `string` | — | The keys of the legend's entries - only where it has one to press. |
2241
2243
  | `allShown` *required* | `string` | — | Read out where hiding would leave nothing to see, and every series is shown instead. |
@@ -2946,7 +2948,7 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
2946
2948
 
2947
2949
  | Prop | Type | Default | Description |
2948
2950
  |---|---|---|---|
2949
- | `value` *required* | [`AxisField`](#type-AxisField)`<T> \| ((d: T, index: number) => `[`AxisValue`](#type-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/configuration), [Add axes for other magnitudes](#/axis/axes), [Leave out the hours nobody works](#/axis/working-time) and 70 more |
2951
+ | `value` *required* | [`AxisField`](#type-AxisField)`<T> \| ((d: T, index: number) => `[`AxisValue`](#type-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/configuration), [Add axes for other magnitudes](#/axis/axes), [Leave out the hours nobody works](#/axis/working-time) and 74 more |
2950
2952
  | `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/every-kind-on-a-second-axis), [Read it by keyboard and screen reader (Chart)](#/chart/keyboard-and-screen-reader), [Gestures and keys (Zoom and pan)](#/zoom-and-pan/gestures-and-keys) and 10 more |
2951
2953
  | `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/axes), [Bind every kind of series to a second axis](#/axis/every-kind-on-a-second-axis) |
2952
2954
  | `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/aligned-ticks), [Draw a first chart (Installation)](#/installation/first-chart), [Size a chart by its container (Installation)](#/installation/in-its-container) and 36 more |
@@ -2954,9 +2956,9 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
2954
2956
  | `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`, `setDomain`). The plain wheel always scrolls the page.<br>Shown in: [Read it by keyboard and screen reader (Chart)](#/chart/keyboard-and-screen-reader), [Gestures and keys (Zoom and pan)](#/zoom-and-pan/gestures-and-keys), [Limit the zoom (Zoom and pan)](#/zoom-and-pan/zoom-limits) and 5 more |
2955
2957
  | `zoomLimits` | [`ZoomLimits`](#type-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/zoom-limits) |
2956
2958
  | `id` | `string` | `"x"` | Axis id, through which series bind themselves.<br>Shown in: [Add axes for other magnitudes](#/axis/axes), [Align a second axis to the grid](#/axis/aligned-ticks), [Bind every kind of series to a second axis](#/axis/every-kind-on-a-second-axis) and 7 more |
2957
- | `label` | `string` | — | Axis title.<br>Shown in: [Fix the domain and name the ticks](#/axis/configuration), [Add axes for other magnitudes](#/axis/axes), [Leave out the hours nobody works](#/axis/working-time) and 62 more |
2959
+ | `label` | `string` | — | Axis title.<br>Shown in: [Fix the domain and name the ticks](#/axis/configuration), [Add axes for other magnitudes](#/axis/axes), [Leave out the hours nobody works](#/axis/working-time) and 66 more |
2958
2960
  | `tickCount` | `number` | — | Target value; the 1-2-5 algorithm may deviate.<br>Shown in: [Fix the domain and name the ticks](#/axis/configuration), [Leave out the hours nobody works](#/axis/working-time), [Mix series kinds (Chart)](#/chart/mixed) and 8 more |
2959
- | `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/configuration), [Leave out the hours nobody works](#/axis/working-time), [Bind every kind of series to a second axis](#/axis/every-kind-on-a-second-axis) and 19 more |
2961
+ | `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/configuration), [Leave out the hours nobody works](#/axis/working-time), [Bind every kind of series to a second axis](#/axis/every-kind-on-a-second-axis) and 21 more |
2960
2962
  | `grid` | `boolean` | only on the first axis of its orientation | Grid lines.<br>Shown in: [One vehicle through the day (StateBand)](#/stateband/one-vehicle), [Give each one a lane (StateBand)](#/stateband/one-lane-each), [See where the vans stand idle (Scenarios)](#/scenarios/see-where-the-vans-stand-idle) |
2961
2963
  | `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/every-kind-on-a-second-axis), [Mark bands, limits and cells (Chart)](#/chart/bands-limits-and-cells), [Compare progress with a plan (Line)](#/line/burn-down) and 7 more |
2962
2964
  | `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/categories) |
@@ -2970,9 +2972,9 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
2970
2972
  | `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/aligned-ticks) |
2971
2973
  | `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/axes), [Align a second axis to the grid](#/axis/aligned-ticks), [Bind every kind of series to a second axis](#/axis/every-kind-on-a-second-axis) and 7 more |
2972
2974
  | `id` | `string` | `"y"` | Axis id, through which series bind themselves.<br>Shown in: [Add axes for other magnitudes](#/axis/axes), [Align a second axis to the grid](#/axis/aligned-ticks), [Bind every kind of series to a second axis](#/axis/every-kind-on-a-second-axis) and 7 more |
2973
- | `label` | `string` | — | Axis title.<br>Shown in: [Fix the domain and name the ticks](#/axis/configuration), [Add axes for other magnitudes](#/axis/axes), [Leave out the hours nobody works](#/axis/working-time) and 62 more |
2975
+ | `label` | `string` | — | Axis title.<br>Shown in: [Fix the domain and name the ticks](#/axis/configuration), [Add axes for other magnitudes](#/axis/axes), [Leave out the hours nobody works](#/axis/working-time) and 66 more |
2974
2976
  | `tickCount` | `number` | — | Target value; the 1-2-5 algorithm may deviate.<br>Shown in: [Fix the domain and name the ticks](#/axis/configuration), [Leave out the hours nobody works](#/axis/working-time), [Mix series kinds (Chart)](#/chart/mixed) and 8 more |
2975
- | `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/configuration), [Leave out the hours nobody works](#/axis/working-time), [Bind every kind of series to a second axis](#/axis/every-kind-on-a-second-axis) and 19 more |
2977
+ | `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/configuration), [Leave out the hours nobody works](#/axis/working-time), [Bind every kind of series to a second axis](#/axis/every-kind-on-a-second-axis) and 21 more |
2976
2978
  | `grid` | `boolean` | only on the first axis of its orientation | Grid lines.<br>Shown in: [One vehicle through the day (StateBand)](#/stateband/one-vehicle), [Give each one a lane (StateBand)](#/stateband/one-lane-each), [See where the vans stand idle (Scenarios)](#/scenarios/see-where-the-vans-stand-idle) |
2977
2979
  | `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/every-kind-on-a-second-axis), [Mark bands, limits and cells (Chart)](#/chart/bands-limits-and-cells), [Compare progress with a plan (Line)](#/line/burn-down) and 7 more |
2978
2980
  | `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/categories) |
@@ -3030,6 +3032,7 @@ schedule's shape.
3030
3032
  #### Known limits
3031
3033
 
3032
3034
  - No logarithmic and no band scale: compute the logarithm in `value`; a category is a place on the linear scale ([ADR-0032](https://romanhaendler.github.io/umriss-ui/what-umriss-ui-is-not/), [ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)).
3035
+ - 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)).
3033
3036
 
3034
3037
  What umriss deliberately does not build, and why: [ADR-0032](https://romanhaendler.github.io/umriss-ui/what-umriss-ui-is-not/).
3035
3038
 
@@ -3563,7 +3566,7 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
3563
3566
 
3564
3567
  | Prop | Type | Default | Description |
3565
3568
  |---|---|---|---|
3566
- | `value` *required* | [`AxisField`](#type-AxisField)`<T> \| ((d: T, index: number) => `[`AxisValue`](#type-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/gestures-and-keys), [Limit the zoom](#/zoom-and-pan/zoom-limits), [Fit the y axis to what is visible](#/zoom-and-pan/visible-domain) and 70 more |
3569
+ | `value` *required* | [`AxisField`](#type-AxisField)`<T> \| ((d: T, index: number) => `[`AxisValue`](#type-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/gestures-and-keys), [Limit the zoom](#/zoom-and-pan/zoom-limits), [Fit the y axis to what is visible](#/zoom-and-pan/visible-domain) and 74 more |
3567
3570
  | `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/gestures-and-keys), [Limit the zoom](#/zoom-and-pan/zoom-limits), [Fit the y axis to what is visible](#/zoom-and-pan/visible-domain) and 10 more |
3568
3571
  | `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/axes), [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) |
3569
3572
  | `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/gestures-and-keys), [Limit the zoom](#/zoom-and-pan/zoom-limits), [Fit the y axis to what is visible](#/zoom-and-pan/visible-domain) and 36 more |
@@ -3571,15 +3574,15 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
3571
3574
  | `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`, `setDomain`). The plain wheel always scrolls the page.<br>Shown in: [Gestures and keys](#/zoom-and-pan/gestures-and-keys), [Limit the zoom](#/zoom-and-pan/zoom-limits), [Fit the y axis to what is visible](#/zoom-and-pan/visible-domain) and 5 more |
3572
3575
  | `zoomLimits` | [`ZoomLimits`](#type-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-limits) |
3573
3576
  | `id` | `string` | `"x"` | Axis id, through which series bind themselves.<br>Shown in: [Add axes for other magnitudes (Axis)](#/axis/axes), [Align a second axis to the grid (Axis)](#/axis/aligned-ticks), [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) and 7 more |
3574
- | `label` | `string` | — | Axis title.<br>Shown in: [Gestures and keys](#/zoom-and-pan/gestures-and-keys), [Limit the zoom](#/zoom-and-pan/zoom-limits), [Fit the y axis to what is visible](#/zoom-and-pan/visible-domain) and 62 more |
3577
+ | `label` | `string` | — | Axis title.<br>Shown in: [Gestures and keys](#/zoom-and-pan/gestures-and-keys), [Limit the zoom](#/zoom-and-pan/zoom-limits), [Fit the y axis to what is visible](#/zoom-and-pan/visible-domain) and 66 more |
3575
3578
  | `tickCount` | `number` | — | Target value; the 1-2-5 algorithm may deviate.<br>Shown in: [Keep charts in step](#/zoom-and-pan/cursor-sync), [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Leave out the hours nobody works (Axis)](#/axis/working-time) and 8 more |
3576
- | `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/configuration), [Leave out the hours nobody works (Axis)](#/axis/working-time), [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) and 19 more |
3579
+ | `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/configuration), [Leave out the hours nobody works (Axis)](#/axis/working-time), [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) and 21 more |
3577
3580
  | `grid` | `boolean` | only on the first axis of its orientation | Grid lines.<br>Shown in: [One vehicle through the day (StateBand)](#/stateband/one-vehicle), [Give each one a lane (StateBand)](#/stateband/one-lane-each), [See where the vans stand idle (Scenarios)](#/scenarios/see-where-the-vans-stand-idle) |
3578
3581
  | `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/every-kind-on-a-second-axis), [Mark bands, limits and cells (Chart)](#/chart/bands-limits-and-cells), [Compare progress with a plan (Line)](#/line/burn-down) and 7 more |
3579
3582
  | `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/categories) |
3580
3583
  | `categoryLabel` | `(category: string) => string` | the category itself | What is written for a category - under its tick, in the tooltip, the readout and the data table - where the rows hold a key and not a name. Two categories may share a label and keep two places. Compared by its source text, as `tickFormat` is.<br>Shown in: [Fix the order and keep an empty place (Axis)](#/axis/categories), [Tell series apart without colour (Chart)](#/chart/told-apart-without-colour), [Legend above or below (Legend)](#/legend/above-or-below) and 1 more |
3581
3584
 
3582
- ##### `ChartOptions`
3585
+ ##### `ChartOptions<Z = unknown>`
3583
3586
 
3584
3587
  | Prop | Type | Default | Description |
3585
3588
  |---|---|---|---|
@@ -3590,6 +3593,7 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
3590
3593
  | Prop | Type | Default | Description |
3591
3594
  |---|---|---|---|
3592
3595
  | `onViewChange` | `(view: `[`ChartView`](#/view/type-ChartView)`) => void` | — | Every change of the view, once, always the whole view - the one to keep, or to hand another chart as its `initialView`. A pan or zoom is reported at most once per frame. Not called for the view the chart starts with.<br>Shown in: [Keep charts in step](#/zoom-and-pan/cursor-sync), [Read it by keyboard and screen reader (Chart)](#/chart/keyboard-and-screen-reader), [Keep and restore a view (View)](#/view/keep-and-restore) |
3596
+ | `onSelect` | `(hit: `[`TooltipHit`](#type-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/choose-a-hit) |
3593
3597
 
3594
3598
  ##### Types on this page
3595
3599
 
@@ -3640,6 +3644,56 @@ schedule's shape.
3640
3644
  | `min` *required* | `number` | — | <br>Shown in: [Limit the zoom](#/zoom-and-pan/zoom-limits) |
3641
3645
  | `max` *required* | `number` | — | <br>Shown in: [Limit the zoom](#/zoom-and-pan/zoom-limits) |
3642
3646
 
3647
+ ###### `TooltipHit<T = unknown>`
3648
+
3649
+ What the pointer hit: the x value under it and one point per series there.
3650
+ A custom tooltip's `render` receives it.
3651
+
3652
+ | Prop | Type | Default | Description |
3653
+ |---|---|---|---|
3654
+ | `xValue` *required* | `number` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
3655
+ | `points` *required* | `readonly `[`TooltipPoint`](#type-TooltipPoint)`<T>[]` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
3656
+ | `xPx` *required* | `number` | — | Pixel position of the crosshair (snapped to the data point). |
3657
+ | `yPx` *required* | `number` | — | Pixel position of the primary hit. |
3658
+
3659
+ ###### `TooltipPoint<T = unknown>`
3660
+
3661
+ One hit per series in the tooltip.
3662
+
3663
+ | Prop | Type | Default | Description |
3664
+ |---|---|---|---|
3665
+ | `seriesName` *required* | `string` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
3666
+ | `color` *required* | `string` | — | |
3667
+ | `xValue` *required* | `number` | — | The point's x value on its own x axis - with several x axes it can differ from the hit's. |
3668
+ | `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/configuration), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
3669
+ | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself (Tooltip)](#/tooltip/own-content) |
3670
+ | `index` *required* | `number` | — | |
3671
+ | `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. |
3672
+ | `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. |
3673
+ | `box` | [`BoxNumbers`](#type-BoxNumbers)` & Partial<`[`BoxExtras`](#type-BoxExtras)`` > & { /** As the caller gave them; only where the series has `outliers`. */ outliers?: readonly number[]; } `` | — | Only for a box: its further numbers; yValue is its median. Fields of their own for the reason `level` has one ([ADR-0011](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0011-named-channels-never-overloaded.md)). |
3674
+
3675
+ ###### `BoxNumbers<V = number>`
3676
+
3677
+ A box's further numbers beside its median, by name.
3678
+
3679
+ | Prop | Type | Default | Description |
3680
+ |---|---|---|---|
3681
+ | `lowerQuartile` *required* | `V` | — | |
3682
+ | `upperQuartile` *required* | `V` | — | |
3683
+ | `lowerWhisker` *required* | `V` | — | |
3684
+ | `upperWhisker` *required* | `V` | — | |
3685
+
3686
+ ###### `BoxExtras<V = number>`
3687
+
3688
+ The optional numbers of a box (box-plot 04), by name.
3689
+
3690
+ | Prop | Type | Default | Description |
3691
+ |---|---|---|---|
3692
+ | `mean` *required* | `V` | — | |
3693
+ | `notchLower` *required* | `V` | — | |
3694
+ | `notchUpper` *required* | `V` | — | |
3695
+ | `count` *required* | `V` | — | |
3696
+
3643
3697
  #### Known limits
3644
3698
 
3645
3699
  - No zoom along y, no rectangle zoom and no navigator: the x span is the one zoom there is.
@@ -3832,7 +3886,7 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
3832
3886
 
3833
3887
  #### API
3834
3888
 
3835
- ##### `ChartOptions`
3889
+ ##### `ChartOptions<Z = unknown>`
3836
3890
 
3837
3891
  | Prop | Type | Default | Description |
3838
3892
  |---|---|---|---|
@@ -3843,6 +3897,7 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
3843
3897
  | Prop | Type | Default | Description |
3844
3898
  |---|---|---|---|
3845
3899
  | `onViewChange` | `(view: `[`ChartView`](#type-ChartView)`) => void` | — | Every change of the view, once, always the whole view - the one to keep, or to hand another chart as its `initialView`. A pan or zoom is reported at most once per frame. Not called for the view the chart starts with.<br>Shown in: [Keep and restore a view](#/view/keep-and-restore), [Keep charts in step (Zoom and pan)](#/zoom-and-pan/cursor-sync), [Read it by keyboard and screen reader (Chart)](#/chart/keyboard-and-screen-reader) |
3900
+ | `onSelect` | `(hit: `[`TooltipHit`](#type-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/choose-a-hit) |
3846
3901
 
3847
3902
  ##### `ChartView`
3848
3903
 
@@ -3853,6 +3908,18 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
3853
3908
 
3854
3909
  ##### Types on this page
3855
3910
 
3911
+ ###### `TooltipHit<T = unknown>`
3912
+
3913
+ What the pointer hit: the x value under it and one point per series there.
3914
+ A custom tooltip's `render` receives it.
3915
+
3916
+ | Prop | Type | Default | Description |
3917
+ |---|---|---|---|
3918
+ | `xValue` *required* | `number` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
3919
+ | `points` *required* | `readonly `[`TooltipPoint`](#type-TooltipPoint)`<T>[]` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
3920
+ | `xPx` *required* | `number` | — | Pixel position of the crosshair (snapped to the data point). |
3921
+ | `yPx` *required* | `number` | — | Pixel position of the primary hit. |
3922
+
3856
3923
  ###### `AxisSpans`
3857
3924
 
3858
3925
  The span each zoomable x axis shows, by axis `id`, in the axis' units.
@@ -3861,6 +3928,44 @@ The span each zoomable x axis shows, by axis `id`, in the axis' units.
3861
3928
  type AxisSpans = Readonly<Record<string, readonly [number, number]>>;
3862
3929
  ```
3863
3930
 
3931
+ ###### `TooltipPoint<T = unknown>`
3932
+
3933
+ One hit per series in the tooltip.
3934
+
3935
+ | Prop | Type | Default | Description |
3936
+ |---|---|---|---|
3937
+ | `seriesName` *required* | `string` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
3938
+ | `color` *required* | `string` | — | |
3939
+ | `xValue` *required* | `number` | — | The point's x value on its own x axis - with several x axes it can differ from the hit's. |
3940
+ | `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/configuration), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
3941
+ | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself (Tooltip)](#/tooltip/own-content) |
3942
+ | `index` *required* | `number` | — | |
3943
+ | `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. |
3944
+ | `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. |
3945
+ | `box` | [`BoxNumbers`](#type-BoxNumbers)` & Partial<`[`BoxExtras`](#type-BoxExtras)`` > & { /** As the caller gave them; only where the series has `outliers`. */ outliers?: readonly number[]; } `` | — | Only for a box: its further numbers; yValue is its median. Fields of their own for the reason `level` has one ([ADR-0011](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0011-named-channels-never-overloaded.md)). |
3946
+
3947
+ ###### `BoxNumbers<V = number>`
3948
+
3949
+ A box's further numbers beside its median, by name.
3950
+
3951
+ | Prop | Type | Default | Description |
3952
+ |---|---|---|---|
3953
+ | `lowerQuartile` *required* | `V` | — | |
3954
+ | `upperQuartile` *required* | `V` | — | |
3955
+ | `lowerWhisker` *required* | `V` | — | |
3956
+ | `upperWhisker` *required* | `V` | — | |
3957
+
3958
+ ###### `BoxExtras<V = number>`
3959
+
3960
+ The optional numbers of a box (box-plot 04), by name.
3961
+
3962
+ | Prop | Type | Default | Description |
3963
+ |---|---|---|---|
3964
+ | `mean` *required* | `V` | — | |
3965
+ | `notchLower` *required* | `V` | — | |
3966
+ | `notchUpper` *required* | `V` | — | |
3967
+ | `count` *required* | `V` | — | |
3968
+
3864
3969
  #### Known limits
3865
3970
 
3866
3971
  - 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.
@@ -4333,11 +4438,11 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
4333
4438
 
4334
4439
  | Prop | Type | Default | Description |
4335
4440
  |---|---|---|---|
4336
- | `value` *required* | [`Value`](#type-Value)`<T>` | — | Y value - a number field of the row or a function of it; null/undefined/NaN/±Infinity means a gap.<br>Shown in: [One series](#/line/one-series), [Several series and a gap](#/line/multi-series), [Hold a value until it changes](#/line/step) and 45 more |
4441
+ | `value` *required* | [`Value`](#type-Value)`<T>` | — | Y value - a number field of the row or a function of it; null/undefined/NaN/±Infinity means a gap.<br>Shown in: [One series](#/line/one-series), [Several series and a gap](#/line/multi-series), [Hold a value until it changes](#/line/step) and 47 more |
4337
4442
  | `xAxisId` | `string` | `"x"` | Binding to an x axis.<br>Shown in: [Add axes for other magnitudes (Axis)](#/axis/axes), [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) |
4338
4443
  | `yAxisId` | `string` | `"y"` | Binding to a y axis.<br>Shown in: [Hold a value until it changes](#/line/step), [Add axes for other magnitudes (Axis)](#/axis/axes), [Align a second axis to the grid (Axis)](#/axis/aligned-ticks) and 4 more |
4339
4444
  | `data` | `readonly T[]` | — | Series-own data; overrides the container data.<br>Shown in: [Several series and a gap](#/line/multi-series), [Hold a value until it changes](#/line/step), [Tell series apart without colour (Chart)](#/chart/told-apart-without-colour) and 6 more |
4340
- | `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/one-series), [Several series and a gap](#/line/multi-series), [Hold a value until it changes](#/line/step) and 45 more |
4445
+ | `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/one-series), [Several series and a gap](#/line/multi-series), [Hold a value until it changes](#/line/step) and 47 more |
4341
4446
  | `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/value-format), [Show the values as a table (Tooltip)](#/tooltip/data-table), [Table a long series (Tooltip)](#/tooltip/a-week-as-a-table) and 1 more |
4342
4447
  | `color` | `string` | the palette's `--uc-series-N` | Any CSS colour value.<br>Shown in: [Compare progress with a plan](#/line/burn-down), [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Watch latency against its objective (Scenarios)](#/scenarios/watch-latency-against-its-objective) and 1 more |
4343
4448
  | `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)](#/scenarios/compare-budget-with-forecast) |
@@ -4846,12 +4951,12 @@ type ValueFunction<T> = (d: T, index: number) => number | null | undefined;
4846
4951
  Bars along the x axis (a bar or column chart): one per category, grouped side by side, stacked, or as shares of a whole.
4847
4952
 
4848
4953
  ```ts
4849
- import { useChart } from "@umriss-ui/charts";
4954
+ import { useChart, bin } from "@umriss-ui/charts";
4850
4955
  ```
4851
4956
 
4852
4957
  Demo page: https://romanhaendler.github.io/umriss-ui/charts/bar/
4853
4958
 
4854
- The x axis reads the category from the row - a cost centre, a weekday - and gives each a place, a tick and its name ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)); a number or an instant places a bar as well. A bar's foot is 0, so one series carries both signs.
4959
+ The x axis reads the category from the row - a cost centre, a weekday - and gives each a place, a tick and its name ([ADR-0052](https://github.com/romanhaendler/umriss-ui/blob/main/docs/adr/0052-a-chart-reads-its-rows-as-they-come.md)); a number or an instant places a bar as well. A bar's foot is 0, so one series carries both signs. `bin()` counts values into the rows of a histogram: each bin's `from`, `to`, middle `x` and `count`, a value on an edge in the bin that edge starts, the last bin closed, values that are not numbers skipped; a `domain` is taken as given and what lies outside it is left out.
4855
4960
 
4856
4961
  #### Examples
4857
4962
 
@@ -5041,6 +5146,34 @@ export default function Percent() {
5041
5146
  }
5042
5147
  ```
5043
5148
 
5149
+ ##### Count readings into a histogram
5150
+
5151
+ `bin()` counts values into bins of one width on a tidy grid and returns each bin's bounds and count; a `Bar` on the numeric x axis draws them at `x`.
5152
+
5153
+ ```tsx
5154
+ import { useMemo } from "react";
5155
+ import { Tooltip, bin, useChart } from "@umriss-ui/charts";
5156
+
5157
+ /* How long 40 support tickets took to answer, in minutes, as the helpdesk exports them. */
5158
+ const MINUTES_TO_ANSWER = [
5159
+ 4, 7, 9, 11, 12, 12, 14, 15, 15, 17, 18, 19, 21, 22, 22, 23, 24, 26, 27, 28,
5160
+ 29, 31, 33, 34, 36, 38, 41, 43, 47, 52, 55, 58, 63, 71, 78, 84, 96, 112, 135, 171,
5161
+ ];
5162
+
5163
+ export default function Histogram() {
5164
+ const bins = useMemo(() => bin(MINUTES_TO_ANSWER, { width: 20 }), []);
5165
+ const { Chart, XAxis, YAxis, Bar } = useChart(bins);
5166
+ return (
5167
+ <Chart height={260} ariaLabel="Minutes to answer 40 support tickets, counted in bins of 20 minutes">
5168
+ <XAxis value="x" label="minutes to answer" />
5169
+ <YAxis label="tickets" />
5170
+ <Bar value="count" name="Tickets" barWidth={0.94} />
5171
+ <Tooltip mode="x" />
5172
+ </Chart>
5173
+ );
5174
+ }
5175
+ ```
5176
+
5044
5177
  #### When to use something else
5045
5178
 
5046
5179
  - Causes ranked by how much they contribute → Pareto
@@ -5055,17 +5188,17 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
5055
5188
 
5056
5189
  | Prop | Type | Default | Description |
5057
5190
  |---|---|---|---|
5058
- | `value` *required* | [`Value`](#type-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/per-category), [Group bars side by side](#/bar/grouped), [Show bars above and below zero](#/bar/deviation) and 11 more |
5191
+ | `value` *required* | [`Value`](#type-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/per-category), [Group bars side by side](#/bar/grouped), [Show bars above and below zero](#/bar/deviation) and 13 more |
5059
5192
  | `xAxisId` | `string` | `"x"` | Binding to an x axis.<br>Shown in: [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) |
5060
5193
  | `yAxisId` | `string` | `"y"` | Binding to a y axis.<br>Shown in: [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) |
5061
5194
  | `data` | `readonly T[]` | — | Series-own data; overrides the container data.<br>Shown in: [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) |
5062
- | `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/per-category), [Group bars side by side](#/bar/grouped), [Show bars above and below zero](#/bar/deviation) and 11 more |
5195
+ | `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/per-category), [Group bars side by side](#/bar/grouped), [Show bars above and below zero](#/bar/deviation) and 13 more |
5063
5196
  | `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/percent), [Compare the budget with the forecast (Scenarios)](#/scenarios/compare-budget-with-forecast) |
5064
5197
  | `color` | `string` | the palette's `--uc-series-N` | Any CSS colour value.<br>Shown in: [Group bars side by side](#/bar/grouped), [See where the vans stand idle (Scenarios)](#/scenarios/see-where-the-vans-stand-idle), [Compare the budget with the forecast (Scenarios)](#/scenarios/compare-budget-with-forecast) |
5065
5198
  | `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)](#/scenarios/compare-budget-with-forecast) |
5066
- | `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/stacked), [Show shares of a whole](#/bar/percent), [Compare the budget with the forecast (Scenarios)](#/scenarios/compare-budget-with-forecast) |
5199
+ | `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/stacked), [Show shares of a whole](#/bar/percent), [Read a stack's total in the table (Tooltip)](#/tooltip/total-of-a-stack) and 1 more |
5067
5200
  | `normalize` | `boolean` | — | On any member of a stack, every x of the stack sums to 100 %: each value becomes its share, and the y axis reads in percent unless it has a `tickFormat`. The total stays the readings' own sum, written in the series' `format`.<br>Shown in: [Show shares of a whole](#/bar/percent) |
5068
- | `barWidth` | `number` | `0.8` | Width as a fraction of the step; just under 1, so that neighbours do not touch. Several bar series share this fraction.<br>Shown in: [Group bars side by side](#/bar/grouped), [Sort causes and collect the tail (Pareto)](#/pareto/pareto), [See where the vans stand idle (Scenarios)](#/scenarios/see-where-the-vans-stand-idle) and 2 more |
5201
+ | `barWidth` | `number` | `0.8` | Width as a fraction of the step; just under 1, so that neighbours do not touch. Several bar series share this fraction.<br>Shown in: [Group bars side by side](#/bar/grouped), [Count readings into a histogram](#/bar/histogram), [Sort causes and collect the tail (Pareto)](#/pareto/pareto) and 3 more |
5069
5202
 
5070
5203
  ##### Types on this page
5071
5204
 
@@ -5104,7 +5237,7 @@ type ValueFunction<T> = (d: T, index: number) => number | null | undefined;
5104
5237
 
5105
5238
  #### Known limits
5106
5239
 
5107
- - No horizontal bars: bars grow along the y axis; for a ranking by name use `Matrix` or a `Table` ([ADR-0032](https://romanhaendler.github.io/umriss-ui/what-umriss-ui-is-not/)).
5240
+ - 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.
5108
5241
 
5109
5242
  What umriss deliberately does not build, and why: [ADR-0032](https://romanhaendler.github.io/umriss-ui/what-umriss-ui-is-not/).
5110
5243
 
@@ -7840,11 +7973,11 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
7840
7973
 
7841
7974
  | Prop | Type | Default | Description |
7842
7975
  |---|---|---|---|
7843
- | `value` *required* | `number \| Date` | — | Where the line lies, in domain units of its axis; along a time axis an instant, in milliseconds or as a `Date`.<br>Shown in: [Draw a limit](#/limitline/objective), [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state) and 10 more |
7976
+ | `value` *required* | `number \| Date` | — | Where the line lies, in domain units of its axis; along a time axis an instant, in milliseconds or as a `Date`.<br>Shown in: [Draw a limit](#/limitline/objective), [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state) and 11 more |
7844
7977
  | `axisId` | `string` | the first axis of the orientation | Axis on which the value lies.<br>Shown in: [Sort causes and collect the tail (Pareto)](#/pareto/pareto) |
7845
7978
  | `orientation` | `"x" \| "y"` | `"y"` | Whether the value lies on the x or on the y axis - a limit is nearly always a value and not a moment.<br>Shown in: [Mark moments and windows along time](#/limitline/along-time) |
7846
- | `severity` | [`Severity`](#type-Severity)<br>`"warning" \| "alarm"` | `"alarm"` | Two severities, not five: five urgencies are five colours, and five colours on a dense screen are noise.<br>Shown in: [Draw a limit](#/limitline/objective), [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state) and 9 more |
7847
- | `label` | `string` | — | What the value means. A line at 90 does not say what 90 is.<br>Shown in: [Draw a limit](#/limitline/objective), [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state) and 10 more |
7979
+ | `severity` | [`Severity`](#type-Severity)<br>`"warning" \| "alarm"` | `"alarm"` | Two severities, not five: five urgencies are five colours, and five colours on a dense screen are noise.<br>Shown in: [Draw a limit](#/limitline/objective), [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state) and 10 more |
7980
+ | `label` | `string` | — | What the value means. A line at 90 does not say what 90 is.<br>Shown in: [Draw a limit](#/limitline/objective), [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state) and 11 more |
7848
7981
  | `color` | `string` | — | Any CSS colour value, in place of the one the severity yields. A colour handed in instead of a severity puts the rule in the application and the presentation in the library, and the two drift apart as soon as a bound moves - so this is for the case that has no severity, not for taste. The line or band takes it; its label keeps the axis's text colour, which stays legible in both themes, and a short stroke before the text in the line's pattern takes this colour.<br>Shown in: [Mark moments and windows along time](#/limitline/along-time) |
7849
7982
  | `inExtent` | `boolean` | `true` | Whether the value widens the axis extent, so that the limit is certainly visible.<br>Shown in: [Sort causes and collect the tail (Pareto)](#/pareto/pareto), [Watch latency against its objective (Scenarios)](#/scenarios/watch-latency-against-its-objective), [Hold the tile length in control (Scenarios)](#/scenarios/hold-the-tile-length-in-control) |
7850
7983
 
@@ -7858,12 +7991,12 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
7858
7991
 
7859
7992
  | Prop | Type | Default | Description |
7860
7993
  |---|---|---|---|
7861
- | `from` *required* | `number \| Date` | — | Lower edge of the band, in domain units of its axis; along a time axis an instant, in milliseconds or as a `Date`.<br>Shown in: [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state), [Watch a kiln zone against its tolerance](#/limitline/kiln-zone) and 4 more |
7862
- | `to` *required* | `number \| Date` | — | Upper edge, in domain units of its axis. A band drawn backwards (`from` above `to`) is drawn between the two all the same - the pair is a range, not a direction.<br>Shown in: [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state), [Watch a kiln zone against its tolerance](#/limitline/kiln-zone) and 4 more |
7994
+ | `from` *required* | `number \| Date` | — | Lower edge of the band, in domain units of its axis; along a time axis an instant, in milliseconds or as a `Date`.<br>Shown in: [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state), [Watch a kiln zone against its tolerance](#/limitline/kiln-zone) and 5 more |
7995
+ | `to` *required* | `number \| Date` | — | Upper edge, in domain units of its axis. A band drawn backwards (`from` above `to`) is drawn between the two all the same - the pair is a range, not a direction.<br>Shown in: [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state), [Watch a kiln zone against its tolerance](#/limitline/kiln-zone) and 5 more |
7863
7996
  | `axisId` | `string` | the first axis of the orientation | Axis on which the value lies.<br>Shown in: [Sort causes and collect the tail (Pareto)](#/pareto/pareto) |
7864
7997
  | `orientation` | `"x" \| "y"` | `"y"` | Whether the value lies on the x or on the y axis - a limit is nearly always a value and not a moment.<br>Shown in: [Mark moments and windows along time](#/limitline/along-time) |
7865
- | `severity` | [`Severity`](#type-Severity)<br>`"warning" \| "alarm"` | `"warning"` | Two severities, not five: five urgencies are five colours, and five colours on a dense screen are noise.<br>Shown in: [Draw a limit](#/limitline/objective), [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state) and 9 more |
7866
- | `label` | `string` | — | What the value means. A line at 90 does not say what 90 is.<br>Shown in: [Draw a limit](#/limitline/objective), [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state) and 10 more |
7998
+ | `severity` | [`Severity`](#type-Severity)<br>`"warning" \| "alarm"` | `"warning"` | Two severities, not five: five urgencies are five colours, and five colours on a dense screen are noise.<br>Shown in: [Draw a limit](#/limitline/objective), [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state) and 10 more |
7999
+ | `label` | `string` | — | What the value means. A line at 90 does not say what 90 is.<br>Shown in: [Draw a limit](#/limitline/objective), [Lay a band under the series](#/limitline/band), [Read limits above the states](#/limitline/limits-and-state) and 11 more |
7867
8000
  | `color` | `string` | — | Any CSS colour value, in place of the one the severity yields. A colour handed in instead of a severity puts the rule in the application and the presentation in the library, and the two drift apart as soon as a bound moves - so this is for the case that has no severity, not for taste. The line or band takes it; its label keeps the axis's text colour, which stays legible in both themes, and a short stroke before the text in the line's pattern takes this colour.<br>Shown in: [Mark moments and windows along time](#/limitline/along-time) |
7868
8001
  | `inExtent` | `boolean` | `true` | Whether the value widens the axis extent, so that the limit is certainly visible.<br>Shown in: [Sort causes and collect the tail (Pareto)](#/pareto/pareto), [Watch latency against its objective (Scenarios)](#/scenarios/watch-latency-against-its-objective), [Hold the tile length in control (Scenarios)](#/scenarios/hold-the-tile-length-in-control) |
7869
8002
 
@@ -8749,6 +8882,8 @@ Demo page: https://romanhaendler.github.io/umriss-ui/charts/tooltip/
8749
8882
 
8750
8883
  The crosshair snaps to a reading, never between two. The data table's key stands at the end of the legend.
8751
8884
 
8885
+ `useChart(rows, { onSelect })` hears a click or tap on a hit and Enter on the Active point, with the hit a custom `render` receives - once, never at the end of a drag, and for a double click only on its first click. The chart keeps and draws no selection; it needs a `Tooltip` for its hits and does not send the hit over `syncId`.
8886
+
8752
8887
  #### Examples
8753
8888
 
8754
8889
  ##### Report every series or the nearest
@@ -9316,6 +9451,115 @@ export default function WeekAsTable() {
9316
9451
  }
9317
9452
  ```
9318
9453
 
9454
+ ##### Read the limits at the hit
9455
+
9456
+ `limits` adds a row for every labelled limit on the hit's y axis, its label and value in the axis' format; a key reads the same out, and the chart's summary names them without it.
9457
+
9458
+ ```tsx
9459
+ import { LimitBand, LimitLine, Tooltip, useChart } from "@umriss-ui/charts";
9460
+
9461
+ /* Checkout's 95th percentile latency every ten minutes of a morning, in ms, as the monitor exports it. */
9462
+ const LATENCY = [
9463
+ { time: "09:00", p95: 212 }, { time: "09:10", p95: 224 }, { time: "09:20", p95: 231 },
9464
+ { time: "09:30", p95: 246 }, { time: "09:40", p95: 318 }, { time: "09:50", p95: 402 },
9465
+ { time: "10:00", p95: 387 }, { time: "10:10", p95: 291 }, { time: "10:20", p95: 238 },
9466
+ { time: "10:30", p95: 229 },
9467
+ ].map((row, x) => ({ ...row, x }));
9468
+
9469
+ export default function Limits() {
9470
+ const { Chart, XAxis, YAxis, Line } = useChart(LATENCY);
9471
+ return (
9472
+ <Chart height={260} ariaLabel="Checkout's latency against its objective and its warning band">
9473
+ <XAxis value="x" tickFormat={(v) => LATENCY[Math.round(v)]?.time ?? ""} />
9474
+ <YAxis label="ms" tickFormat={(v) => `${v} ms`} />
9475
+ <LimitBand from={250} to={300} severity="warning" label="Warning band" />
9476
+ <LimitLine value={300} severity="alarm" label="Objective" />
9477
+ <Line value="p95" name="p95" />
9478
+ <Tooltip mode="x" limits />
9479
+ </Chart>
9480
+ );
9481
+ }
9482
+ ```
9483
+
9484
+ ##### Choose a hit
9485
+
9486
+ `onSelect` hears a click on a hit, a tap, or Enter on the Active point, with the same hit a custom tooltip receives. The chart keeps no choice and draws none: it is your state, here shown below the chart.
9487
+
9488
+ ```tsx
9489
+ import { useState } from "react";
9490
+ import { Tooltip, useChart, type TooltipHit } from "@umriss-ui/charts";
9491
+
9492
+ /* Orders taken per hour of a shop's day, written out here so the example runs on its own. */
9493
+ const ORDERS = [
9494
+ { hour: 8, web: 14, phone: 6 }, { hour: 9, web: 22, phone: 9 }, { hour: 10, web: 31, phone: 11 },
9495
+ { hour: 11, web: 38, phone: 8 }, { hour: 12, web: 52, phone: 14 }, { hour: 13, web: 47, phone: 10 },
9496
+ { hour: 14, web: 35, phone: 7 }, { hour: 15, web: 29, phone: 9 },
9497
+ ];
9498
+
9499
+ type Order = (typeof ORDERS)[number];
9500
+
9501
+ export default function ChooseAHit() {
9502
+ const [chosen, setChosen] = useState<TooltipHit<Order> | null>(null);
9503
+ const { Chart, XAxis, YAxis, Line } = useChart(ORDERS, { onSelect: setChosen });
9504
+ return (
9505
+ <>
9506
+ <Chart height={260} ariaLabel="Orders per hour, by channel">
9507
+ <XAxis value="hour" tickFormat={(v) => `${v}:00`} />
9508
+ <YAxis label="orders" />
9509
+ <Line value="web" name="Web" />
9510
+ <Line value="phone" name="Phone" />
9511
+ <Tooltip mode="x" />
9512
+ </Chart>
9513
+ <dl className="metrics">
9514
+ <div>
9515
+ <dt>Chosen</dt>
9516
+ <dd>{chosen === null ? "nothing yet - click a point, or press Enter on one" : `${chosen.xValue}:00`}</dd>
9517
+ </div>
9518
+ {chosen?.points.map((p) => (
9519
+ <div key={p.seriesName}>
9520
+ <dt>{p.seriesName}</dt>
9521
+ <dd>{p.yValue} orders</dd>
9522
+ </div>
9523
+ ))}
9524
+ </dl>
9525
+ </>
9526
+ );
9527
+ }
9528
+ ```
9529
+
9530
+ ##### Read a stack's total in the table
9531
+
9532
+ 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.
9533
+
9534
+ ```tsx
9535
+ import { DataTable, Legend, Tooltip, useChart } from "@umriss-ui/charts";
9536
+
9537
+ /* Support tickets opened per working day, by channel - one week, 2 to 6 March. */
9538
+ const TICKETS = [
9539
+ { day: "Mon 2", mail: 34, chat: 21, phone: 9 },
9540
+ { day: "Tue 3", mail: 29, chat: 25, phone: 12 },
9541
+ { day: "Wed 4", mail: 31, chat: 18, phone: 7 },
9542
+ { day: "Thu 5", mail: 27, chat: 30, phone: 11 },
9543
+ { day: "Fri 6", mail: 22, chat: 16, phone: 6 },
9544
+ ];
9545
+
9546
+ export default function TotalOfAStack() {
9547
+ const { Chart, XAxis, YAxis, Bar } = useChart(TICKETS);
9548
+ return (
9549
+ <Chart height={260} ariaLabel="Tickets opened per working day, by channel">
9550
+ <XAxis value="day" />
9551
+ <YAxis label="Tickets" />
9552
+ <Bar value="mail" name="Mail" stack="tickets" />
9553
+ <Bar value="chat" name="Chat" stack="tickets" />
9554
+ <Bar value="phone" name="Phone" stack="tickets" />
9555
+ <Legend placement="top" />
9556
+ <Tooltip mode="x" />
9557
+ <DataTable />
9558
+ </Chart>
9559
+ );
9560
+ }
9561
+ ```
9562
+
9319
9563
  #### Keyboard
9320
9564
 
9321
9565
  The keys of [Chart](#/chart/keyboard-chart) apply here.
@@ -9326,9 +9570,23 @@ The keys of [Chart](#/chart/keyboard-chart) apply here.
9326
9570
 
9327
9571
  | Prop | Type | Default | Description |
9328
9572
  |---|---|---|---|
9329
- | `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/x-or-nearest), [Write the tooltip yourself](#/tooltip/own-content), [Write each value in its unit](#/tooltip/value-format) and 57 more |
9573
+ | `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/x-or-nearest), [Write the tooltip yourself](#/tooltip/own-content), [Write each value in its unit](#/tooltip/value-format) and 61 more |
9574
+ | `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/limits) |
9330
9575
  | `render` | `(hit: `[`TooltipHit`](#type-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/own-content), [Fix the domain and name the ticks (Axis)](#/axis/configuration) |
9331
9576
 
9577
+ ##### `ChartOptions<Z = unknown>`
9578
+
9579
+ | Prop | Type | Default | Description |
9580
+ |---|---|---|---|
9581
+ | `initialView` | [`ChartView`](#/view/type-ChartView) | — | The view to start from, and to go to whenever one differing in content from the last is handed in - the same view again changes nothing, and what it leaves out is reset; one the chart reported itself, handed back late, is its own state coming back and not gone to - two charts in step do not jump back mid-pan. The chart remembers none: where a view is kept is the application's decision.<br>Shown in: [Fit the y axis to what is visible (Zoom and pan)](#/zoom-and-pan/visible-domain), [Keep charts in step (Zoom and pan)](#/zoom-and-pan/cursor-sync), [Read it by keyboard and screen reader (Chart)](#/chart/keyboard-and-screen-reader) and 2 more |
9582
+
9583
+ ###### Events
9584
+
9585
+ | Prop | Type | Default | Description |
9586
+ |---|---|---|---|
9587
+ | `onViewChange` | `(view: `[`ChartView`](#/view/type-ChartView)`) => void` | — | Every change of the view, once, always the whole view - the one to keep, or to hand another chart as its `initialView`. A pan or zoom is reported at most once per frame. Not called for the view the chart starts with.<br>Shown in: [Keep charts in step (Zoom and pan)](#/zoom-and-pan/cursor-sync), [Read it by keyboard and screen reader (Chart)](#/chart/keyboard-and-screen-reader), [Keep and restore a view (View)](#/view/keep-and-restore) |
9588
+ | `onSelect` | `(hit: `[`TooltipHit`](#type-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/choose-a-hit) |
9589
+
9332
9590
  ##### Types on this page
9333
9591
 
9334
9592
  ###### `TooltipHit<T = unknown>`
@@ -9338,8 +9596,8 @@ A custom tooltip's `render` receives it.
9338
9596
 
9339
9597
  | Prop | Type | Default | Description |
9340
9598
  |---|---|---|---|
9341
- | `xValue` *required* | `number` | — | <br>Shown in: [Write the tooltip yourself](#/tooltip/own-content), [Fix the domain and name the ticks (Axis)](#/axis/configuration) |
9342
- | `points` *required* | `readonly `[`TooltipPoint`](#type-TooltipPoint)`<T>[]` | — | <br>Shown in: [Write the tooltip yourself](#/tooltip/own-content), [Fix the domain and name the ticks (Axis)](#/axis/configuration) |
9599
+ | `xValue` *required* | `number` | — | <br>Shown in: [Write the tooltip yourself](#/tooltip/own-content), [Choose a hit](#/tooltip/choose-a-hit), [Fix the domain and name the ticks (Axis)](#/axis/configuration) |
9600
+ | `points` *required* | `readonly `[`TooltipPoint`](#type-TooltipPoint)`<T>[]` | — | <br>Shown in: [Write the tooltip yourself](#/tooltip/own-content), [Choose a hit](#/tooltip/choose-a-hit), [Fix the domain and name the ticks (Axis)](#/axis/configuration) |
9343
9601
  | `xPx` *required* | `number` | — | Pixel position of the crosshair (snapped to the data point). |
9344
9602
  | `yPx` *required* | `number` | — | Pixel position of the primary hit. |
9345
9603
 
@@ -9349,10 +9607,10 @@ One hit per series in the tooltip.
9349
9607
 
9350
9608
  | Prop | Type | Default | Description |
9351
9609
  |---|---|---|---|
9352
- | `seriesName` *required* | `string` | — | <br>Shown in: [Write the tooltip yourself](#/tooltip/own-content), [Fix the domain and name the ticks (Axis)](#/axis/configuration) |
9610
+ | `seriesName` *required* | `string` | — | <br>Shown in: [Write the tooltip yourself](#/tooltip/own-content), [Choose a hit](#/tooltip/choose-a-hit), [Fix the domain and name the ticks (Axis)](#/axis/configuration) |
9353
9611
  | `color` *required* | `string` | — | |
9354
9612
  | `xValue` *required* | `number` | — | The point's x value on its own x axis - with several x axes it can differ from the hit's. |
9355
- | `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/configuration) |
9613
+ | `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/choose-a-hit), [Fix the domain and name the ticks (Axis)](#/axis/configuration) |
9356
9614
  | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself](#/tooltip/own-content) |
9357
9615
  | `index` *required* | `number` | — | |
9358
9616
  | `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. |
@@ -9647,7 +9905,7 @@ Every entry of a named series is a button whose `aria-pressed` says whether its
9647
9905
 
9648
9906
  | Prop | Type | Default | Description |
9649
9907
  |---|---|---|---|
9650
- | `placement` | `"top" \| "bottom"` | `"top"` | Which side of the plot area the legend stands on.<br>Shown in: [Legend above or below](#/legend/above-or-below), [Mix series kinds (Chart)](#/chart/mixed), [Mark every kind (Chart)](#/chart/marks-on-every-kind) and 27 more |
9908
+ | `placement` | `"top" \| "bottom"` | `"top"` | Which side of the plot area the legend stands on.<br>Shown in: [Legend above or below](#/legend/above-or-below), [Mix series kinds (Chart)](#/chart/mixed), [Mark every kind (Chart)](#/chart/marks-on-every-kind) and 28 more |
9651
9909
 
9652
9910
  ### Benchmark
9653
9911
 
@@ -10109,13 +10367,13 @@ Used on [Scenarios](#/), [Chart](#/chart), [Axis](#/axis), [View](#/view), [Line
10109
10367
  [LimitLine](#/limitline) – A limit drawn as a band between two values of an axis - a tolerance or a
10110
10368
  zone. Otherwise a `LimitLine`.
10111
10369
 
10112
- Used on [Scenarios](#/), [Chart](#/chart) and [LimitLine](#/limitline).
10370
+ Used on [Scenarios](#/), [Chart](#/chart), [LimitLine](#/limitline) and [Tooltip](#/tooltip).
10113
10371
 
10114
10372
  ##### `LimitLine`
10115
10373
 
10116
10374
  [LimitLine](#/limitline) – The limits a course is read against (a threshold, a reference line): a line above the series, a band beneath them. Reach for it for objectives, budgets and alert levels.
10117
10375
 
10118
- Used on [Scenarios](#/), [Chart](#/chart), [Scatter](#/scatter), [BoxPlot](#/boxplot), [LimitLine](#/limitline), [ControlChart](#/controlchart) and [Pareto](#/pareto).
10376
+ Used on [Scenarios](#/), [Chart](#/chart), [Scatter](#/scatter), [BoxPlot](#/boxplot), [LimitLine](#/limitline), [ControlChart](#/controlchart), [Pareto](#/pareto) and [Tooltip](#/tooltip).
10119
10377
 
10120
10378
  ##### `Tooltip`
10121
10379
 
@@ -10137,7 +10395,7 @@ identity over the renders -, the `view`, its `domains` and `hidden`, and
10137
10395
  the setters that change them.
10138
10396
 
10139
10397
  ```ts
10140
- function useChart<Z>(rows: readonly Z[], options?: ChartOptions): ChartParts<Z>;
10398
+ function useChart<Z>(rows: readonly Z[], options?: ChartOptions<Z>): ChartParts<Z>;
10141
10399
  ```
10142
10400
 
10143
10401
  Used on [Scenarios](#/), [Installation](#/installation), [Chart](#/chart), [Axis](#/axis), [Zoom and pan](#/zoom-and-pan), [View](#/view), [Line](#/line), [Area](#/area), [Bar](#/bar), [Scatter](#/scatter), [BoxPlot](#/boxplot), [StateBand](#/stateband), [Matrix](#/matrix), [LimitLine](#/limitline), [ControlChart](#/controlchart), [Pareto](#/pareto), [Tooltip](#/tooltip), [Legend](#/legend) and [Benchmark](#/benchmark).
@@ -10168,6 +10426,23 @@ duplicate.
10168
10426
  function assess(value: number | null | undefined, set?: LimitSet): Assessment;
10169
10427
  ```
10170
10428
 
10429
+ ##### `bin`
10430
+
10431
+ Counts values into bins of equal width.
10432
+
10433
+ A value on an edge belongs to the bin that edge starts - `[from, to)` - but
10434
+ the last bin is closed, so the greatest value of a domain is counted and not
10435
+ lost. Values that are not finite (`NaN`, ±Infinity, `null`, `undefined`)
10436
+ are skipped, as a gap is on a chart. Without values there are no bins; with
10437
+ values there is at least one, even when they are all equal.
10438
+
10439
+ ```ts
10440
+ function bin(values: readonly (number | null | undefined)[], options?: BinOptions): Bin[];
10441
+ function bin<T>(rows: readonly T[], value: (row: T) => number | null | undefined, options?: BinOptions): Bin[];
10442
+ ```
10443
+
10444
+ Used on [Bar](#/bar).
10445
+
10171
10446
  ##### `breaks`
10172
10447
 
10173
10448
  The breaks in working time that lie within the extent [from, to] - what the
@@ -10754,6 +11029,27 @@ A `Bar` as the chart holds it once registered.
10754
11029
  | `stack` | `string` | — | The stack this series stands in: series with the same id, on the same x and y axis, stack in registration order (K1). From `Stackable`. |
10755
11030
  | `normalize` | `boolean` | — | On any member, the whole stack sums to 100 at every x (K5). From `Stackable`. |
10756
11031
 
11032
+ ##### `Bin`
11033
+
11034
+ A bin, the way it is drawn.
11035
+
11036
+ | Prop | Type | Default | Description |
11037
+ |---|---|---|---|
11038
+ | `from` *required* | `number` | — | Lower edge; the bin holds it. |
11039
+ | `to` *required* | `number` | — | Upper edge; the bin holds it only if it is the last one. |
11040
+ | `x` *required* | `number` | — | The middle, `(from + to) / 2` - the bar's x. |
11041
+ | `count` *required* | `number` | — | How many values fall in it. |
11042
+
11043
+ ##### `BinOptions`
11044
+
11045
+ The settings of `bin`.
11046
+
11047
+ | Prop | Type | Default | Description |
11048
+ |---|---|---|---|
11049
+ | `width` | `number` | — | The width of a bin. Wins over `count`. Without a `domain` the edges are multiples of it.<br>Shown in: [Count readings into a histogram (Bar)](#/bar/histogram) |
11050
+ | `count` | `number` | `10` | About how many bins: the width becomes the next 1-2-5 step, so the number comes out near it and not exactly - with a `domain`, exactly. |
11051
+ | `domain` | `readonly [number, number]` | — | The span the bins cover, the first edge on its start and the last on its end. Values outside it are left out, not clamped into the end bins. |
11052
+
10757
11053
  ##### `BoxChannels`
10758
11054
 
10759
11055
  The named channels of a box: one number per point, NaN where the median
@@ -10847,12 +11143,12 @@ is typed by that `data` instead.
10847
11143
 
10848
11144
  | Prop | Type | Default | Description |
10849
11145
  |---|---|---|---|
10850
- | `Chart` *required* | `(props: `[`ChartProps`](#/chart/type-ChartProps)`) => 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/first-chart), [Size a chart by its container (Installation)](#/installation/in-its-container), [Mix series kinds (Chart)](#/chart/mixed) and 70 more |
10851
- | `XAxis` *required* | `<T = Z>(props: `[`XAxisProps`](#/axis/type-XAxisProps)`<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/first-chart), [Size a chart by its container (Installation)](#/installation/in-its-container), [Mix series kinds (Chart)](#/chart/mixed) and 70 more |
10852
- | `YAxis` *required* | `(props: `[`YAxisProps`](#/axis/type-YAxisProps)`) => null` | — | <br>Shown in: [Draw a first chart (Installation)](#/installation/first-chart), [Size a chart by its container (Installation)](#/installation/in-its-container), [Mix series kinds (Chart)](#/chart/mixed) and 70 more |
10853
- | `Line` *required* | `<T = Z>(props: `[`LineProps`](#/line/type-LineProps)`<T>) => null` | — | <br>Shown in: [Draw a first chart (Installation)](#/installation/first-chart), [Size a chart by its container (Installation)](#/installation/in-its-container), [Mix series kinds (Chart)](#/chart/mixed) and 45 more |
11146
+ | `Chart` *required* | `(props: `[`ChartProps`](#/chart/type-ChartProps)`) => 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/first-chart), [Size a chart by its container (Installation)](#/installation/in-its-container), [Mix series kinds (Chart)](#/chart/mixed) and 74 more |
11147
+ | `XAxis` *required* | `<T = Z>(props: `[`XAxisProps`](#/axis/type-XAxisProps)`<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/first-chart), [Size a chart by its container (Installation)](#/installation/in-its-container), [Mix series kinds (Chart)](#/chart/mixed) and 74 more |
11148
+ | `YAxis` *required* | `(props: `[`YAxisProps`](#/axis/type-YAxisProps)`) => null` | — | <br>Shown in: [Draw a first chart (Installation)](#/installation/first-chart), [Size a chart by its container (Installation)](#/installation/in-its-container), [Mix series kinds (Chart)](#/chart/mixed) and 74 more |
11149
+ | `Line` *required* | `<T = Z>(props: `[`LineProps`](#/line/type-LineProps)`<T>) => null` | — | <br>Shown in: [Draw a first chart (Installation)](#/installation/first-chart), [Size a chart by its container (Installation)](#/installation/in-its-container), [Mix series kinds (Chart)](#/chart/mixed) and 47 more |
10854
11150
  | `Area` *required* | `<T = Z>(props: `[`AreaProps`](#/area/type-AreaProps)`<T>) => null` | — | <br>Shown in: [Mix series kinds (Chart)](#/chart/mixed), [Mark every kind (Chart)](#/chart/marks-on-every-kind), [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) and 5 more |
10855
- | `Bar` *required* | `<T = Z>(props: `[`BarProps`](#/bar/type-BarProps)`<T>) => null` | — | <br>Shown in: [Mix series kinds (Chart)](#/chart/mixed), [Mark every kind (Chart)](#/chart/marks-on-every-kind), [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) and 11 more |
11151
+ | `Bar` *required* | `<T = Z>(props: `[`BarProps`](#/bar/type-BarProps)`<T>) => null` | — | <br>Shown in: [Mix series kinds (Chart)](#/chart/mixed), [Mark every kind (Chart)](#/chart/marks-on-every-kind), [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) and 13 more |
10856
11152
  | `Scatter` *required* | `<T = Z>(props: `[`ScatterProps`](#/scatter/type-ScatterProps)`<T>) => null` | — | <br>Shown in: [Mix series kinds (Chart)](#/chart/mixed), [Mark every kind (Chart)](#/chart/marks-on-every-kind), [Bind every kind of series to a second axis (Axis)](#/axis/every-kind-on-a-second-axis) and 2 more |
10857
11153
  | `StateBand` *required* | `<T = Z>(props: `[`StateBandProps`](#/stateband/type-StateBandProps)`<T> & `[`OneLane`](#type-OneLane)`) => null` | — | <br>Shown in: [Mark bands, limits and cells (Chart)](#/chart/bands-limits-and-cells), [One vehicle through the day (StateBand)](#/stateband/one-vehicle), [Lay a state under a course (StateBand)](#/stateband/under-a-course) and 3 more |
10858
11154
  | `Matrix` *required* | `<T = Z>(props: `[`MatrixProps`](#/matrix/type-MatrixProps)`<T>) => null` | — | <br>Shown in: [Mark bands, limits and cells (Chart)](#/chart/bands-limits-and-cells), [Colour cells by their limits (Matrix)](#/matrix/by-limits), [Colour cells along a gradient (Matrix)](#/matrix/gradient) |
@@ -10892,10 +11188,12 @@ Everything the charts put out as text.
10892
11188
  | `seriesList` *required* | `(names: readonly string[]) => string` | — | The summary's visible series, by name. |
10893
11189
  | `visibleRange` *required* | `(from: string, to: string) => string` | — | The summary's visible stretch of the x axis, both ends formatted. |
10894
11190
  | `seriesExtent` *required* | `(name: string, min: string, max: string) => string` | — | The summary's lowest and highest value of one series in that stretch. |
11191
+ | `limitList` *required* | `(limits: readonly { name: string; value: string }[]) => string` | — | The summary's labelled limits, each with its value in its axis' format (a band's two edges as one value). |
10895
11192
  | `readout` *required* | `(x: string, rows: readonly { name: string; value: string; x: string }[]) => string` | — | What is read after a key: the position, then each series with its value (and its own x where it stands on another x axis), the emphasised one first. |
10896
11193
  | `stackTotal` *required* | `string` | — | The last row of a stack in the tooltip and the readout: the sum of its members at the point (charts-stacking K4). |
10897
11194
  | `percent` *required* | `(value: string) => string` | — | A share in a normalised stack, its number already formatted (K5). |
10898
11195
  | `walkHelp` *required* | `string` | — | The keys that walk the chart. |
11196
+ | `selectHelp` *required* | `string` | — | The key that reports the Active point - only where `onSelect` is given. |
10899
11197
  | `zoomHelp` *required* | `string` | — | The keys that zoom and pan it - only where the chart can be zoomed. |
10900
11198
  | `legendHelp` *required* | `string` | — | The keys of the legend's entries - only where it has one to press. |
10901
11199
  | `allShown` *required* | `string` | — | Read out where hiding would leave nothing to see, and every series is shown instead. |
@@ -11488,6 +11786,7 @@ A `Tooltip` as the chart holds it once registered.
11488
11786
  | Prop | Type | Default | Description |
11489
11787
  |---|---|---|---|
11490
11788
  | `mode` *required* | `"x" \| "nearest"` | — | |
11789
+ | `limits` | `boolean` | — | |
11491
11790
  | `render` | `(hit: `[`TooltipHit`](#type-TooltipHit)`<T>) => ReactNode` | — | |
11492
11791
 
11493
11792
  ##### `TooltipHit<T = unknown>`
@@ -11497,8 +11796,8 @@ A custom tooltip's `render` receives it.
11497
11796
 
11498
11797
  | Prop | Type | Default | Description |
11499
11798
  |---|---|---|---|
11500
- | `xValue` *required* | `number` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content) |
11501
- | `points` *required* | `readonly `[`TooltipPoint`](#type-TooltipPoint)`<T>[]` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content) |
11799
+ | `xValue` *required* | `number` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
11800
+ | `points` *required* | `readonly `[`TooltipPoint`](#type-TooltipPoint)`<T>[]` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
11502
11801
  | `xPx` *required* | `number` | — | Pixel position of the crosshair (snapped to the data point). |
11503
11802
  | `yPx` *required* | `number` | — | Pixel position of the primary hit. |
11504
11803
 
@@ -11510,10 +11809,10 @@ One hit per series in the tooltip.
11510
11809
 
11511
11810
  | Prop | Type | Default | Description |
11512
11811
  |---|---|---|---|
11513
- | `seriesName` *required* | `string` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content) |
11812
+ | `seriesName` *required* | `string` | — | <br>Shown in: [Fix the domain and name the ticks (Axis)](#/axis/configuration), [Write the tooltip yourself (Tooltip)](#/tooltip/own-content), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
11514
11813
  | `color` *required* | `string` | — | |
11515
11814
  | `xValue` *required* | `number` | — | The point's x value on its own x axis - with several x axes it can differ from the hit's. |
11516
- | `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/configuration) |
11815
+ | `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/configuration), [Choose a hit (Tooltip)](#/tooltip/choose-a-hit) |
11517
11816
  | `datum` *required* | `T` | — | <br>Shown in: [Write the tooltip yourself (Tooltip)](#/tooltip/own-content) |
11518
11817
  | `index` *required* | `number` | — | |
11519
11818
  | `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. |