@stealthscale/component-charts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1862 -0
- package/dist/cyclonedx/bom.json +1 -0
- package/dist/index.d.ts +4332 -0
- package/dist/index.js +14711 -0
- package/dist/recipe-ZKqGhtpf.js +1054 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/theme.d.ts +11 -0
- package/dist/theme.js +22 -0
- package/package.json +84 -0
package/README.md
ADDED
|
@@ -0,0 +1,1862 @@
|
|
|
1
|
+
# @stealthscale/component-charts
|
|
2
|
+
|
|
3
|
+
`@stealthscale/component-charts` renders data as charts over recharts. The presets render line, area
|
|
4
|
+
and bar charts from declared series, bars and lines on two value axes, a forecast band, Pareto,
|
|
5
|
+
burndown, stream and waterfall charts, scatter and bubble charts with a trend line or four named
|
|
6
|
+
quadrants, histograms, distributions compared on shared bins, box plots and violins of groups,
|
|
7
|
+
candlesticks of prices, a pie or a donut from slices, a radar of dimensions, measures as rings
|
|
8
|
+
against a full turn, a rose of a cycle, one value on a dial against its zones, the stages of a
|
|
9
|
+
funnel with their rates, a hierarchy of parts as nested tiles or as rings, flows between stages as
|
|
10
|
+
bands, flows between peers as ribbons around a circle, a value for every pair of two sets of
|
|
11
|
+
categories as a grid of colored cells, moments on one time window across lanes, and a run of values
|
|
12
|
+
the size of a word. The presets are built on `Chart`, which renders a figure around recharts' own
|
|
13
|
+
chart with a measured plot, a tooltip, a legend that shows and hides series, a caption and an empty
|
|
14
|
+
state. A line, area or bar chart also marks moments, periods and points on its categories, and plots
|
|
15
|
+
an earlier period behind the current one. The heatmap renders an HTML grid in the figure in place of
|
|
16
|
+
a recharts chart. A caller who needs another chart writes it inside `Chart`. Each series takes its
|
|
17
|
+
color from the theme. The preset under `./theme` registers the recipes with an application's
|
|
18
|
+
compiler.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pnpm add @stealthscale/component-charts recharts react-is
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The package depends on `recharts` 3.10.1, pinned exactly, because the recipe restyles the classes
|
|
27
|
+
recharts puts on its SVG. It depends on `@stealthscale/component-data` for `ColorSwatch` and on
|
|
28
|
+
`@stealthscale/component-collections` for the funnel's table and the heatmap's scroll area. It peers
|
|
29
|
+
on `react`, `react-is`, which recharts asks for, `@stealthscale/component-primitives`, whose scroll
|
|
30
|
+
area the table renders in, `@stealthscale/theme`, `@stealthscale/hooks` and
|
|
31
|
+
`@stealthscale/provider-locale`.
|
|
32
|
+
|
|
33
|
+
## Presets
|
|
34
|
+
|
|
35
|
+
A preset takes the rows, the field the category axis reads and the series, and renders the whole
|
|
36
|
+
chart:
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { BarChart } from "@stealthscale/component-charts";
|
|
40
|
+
|
|
41
|
+
<BarChart
|
|
42
|
+
caption="Growth sign-ups passed Starter in September, 158 to 152."
|
|
43
|
+
categoryKey="month"
|
|
44
|
+
data={months}
|
|
45
|
+
label="Sign-ups per plan per month"
|
|
46
|
+
labelOptions={{ month: "short", timeZone: "UTC" }}
|
|
47
|
+
series={[
|
|
48
|
+
{ key: "starter", label: "Starter" },
|
|
49
|
+
{ key: "growth", label: "Growth" },
|
|
50
|
+
]}
|
|
51
|
+
/>;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| Preset | Renders | Own props |
|
|
55
|
+
| -------------------- | ---------------------------------------------------------- | ------------------------------------------ |
|
|
56
|
+
| `LineChart` | A line per series | `curve` |
|
|
57
|
+
| `StepLineChart` | A line per series, flat from each point to the next | |
|
|
58
|
+
| `AreaChart` | An area per series, each fading to the axis | `curve` |
|
|
59
|
+
| `StackedAreaChart` | The series stacked into one total | `curve`, `percent` |
|
|
60
|
+
| `BarChart` | A bar per series, side by side in each category | `zones`, `targetLabel`, a series' `target` |
|
|
61
|
+
| `StackedBarChart` | Each category's total as one bar, split into the series | |
|
|
62
|
+
| `PercentStackedBar` | Each category as one bar of 100%, split into shares | |
|
|
63
|
+
| `HorizontalBarChart` | A bar per series on its side, the categories down the edge | `zones`, `targetLabel`, a series' `target` |
|
|
64
|
+
|
|
65
|
+
`curve` is `monotone` unless stated, or `linear` for straight lines between the points. With
|
|
66
|
+
`percent` every point of a `StackedAreaChart` sums to 100%.
|
|
67
|
+
|
|
68
|
+
| Prop | Takes | Default |
|
|
69
|
+
| ------------------------------------------------------- | -------------------------------------------------------------------------- | ---------------------------------- |
|
|
70
|
+
| `data` | The rows, in order | Required |
|
|
71
|
+
| `categoryKey` | The field of each row the category axis reads | Required |
|
|
72
|
+
| `series` | `{ key, label, color, dashed, previousOf }` per series, in legend order | Required |
|
|
73
|
+
| `annotations` | `{ key, at, label, color, until, value }` per mark on the categories | None |
|
|
74
|
+
| `label` | The chart's accessible name, recharts' `title` | Required |
|
|
75
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
76
|
+
| `labelOptions` | `Intl.DateTimeFormatOptions` for the category ticks and the tooltip's head | The category as it is |
|
|
77
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the value ticks and the tooltip's values | A number in the locale |
|
|
78
|
+
| `valueDomain` | recharts' domain of the value axis, such as `[0.99, 1]` | From zero |
|
|
79
|
+
| `grid` | Whether lines cross the plot at the value ticks | `true` |
|
|
80
|
+
| `legend` | Whether the legend renders | Two or more series, and rows |
|
|
81
|
+
| `legendLabel` | The legend's accessible name | `Series` |
|
|
82
|
+
| `defaultIndex` | The row the tooltip shows on the first render | None |
|
|
83
|
+
| `animate` | Whether the marks animate in, which they never do under reduced motion | `false` |
|
|
84
|
+
| `empty` | The message in the plot's place while the chart has no rows | `No data` |
|
|
85
|
+
| `ratio` | The plot's aspect ratio | `video` |
|
|
86
|
+
| `hiddenKeys`, `defaultHiddenKeys`, `onHiddenKeysChange` | The hidden series, controlled or from a first value | None hidden |
|
|
87
|
+
| `locale` | The locale the values are written in | The provider's, else the runtime's |
|
|
88
|
+
| `children` | recharts elements rendered after the marks, such as a `ReferenceLine` | None |
|
|
89
|
+
|
|
90
|
+
Any other prop goes to the `figure`.
|
|
91
|
+
|
|
92
|
+
### Marks
|
|
93
|
+
|
|
94
|
+
- A line and an area's edge are 2px wide and have no dots. The point the tooltip is at takes a dot
|
|
95
|
+
of radius 5 inside an edge in the panel's color.
|
|
96
|
+
- A series with `dashed` renders its line in 6px dashes 4px apart, for a target or a projection. A
|
|
97
|
+
series with `previousOf` renders dashed the same way.
|
|
98
|
+
- An unstacked area fills with a gradient from 0.3 opacity at its top to transparent at the axis, so
|
|
99
|
+
overlapping areas show through each other. A stacked area fills flat at 0.85 opacity.
|
|
100
|
+
- An unstacked bar rounds its end by 4px. A stacked bar keeps square ends, and a hairline edge in
|
|
101
|
+
the panel's color parts the segments of a stack.
|
|
102
|
+
- In a stack summed to 100% the value ticks read in percent, and the tooltip writes each series' own
|
|
103
|
+
value, not its share.
|
|
104
|
+
- The value axis labels every tick, and the grid renders a line at every label.
|
|
105
|
+
|
|
106
|
+
### A reference line
|
|
107
|
+
|
|
108
|
+
A child renders inside the chart after the marks. `Chart.colorOf` returns the chart color of a
|
|
109
|
+
palette for its stroke, and the recipe sets its label in the chart's muted ink:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import { ReferenceLine } from "recharts";
|
|
113
|
+
|
|
114
|
+
import { Chart, LineChart } from "@stealthscale/component-charts";
|
|
115
|
+
|
|
116
|
+
<LineChart categoryKey="day" data={days} label="Cloud spend per day" series={[{ key: "spend" }]}>
|
|
117
|
+
<ReferenceLine
|
|
118
|
+
label={{ position: "insideTopRight", value: "Budget" }}
|
|
119
|
+
stroke={Chart.colorOf("error")}
|
|
120
|
+
strokeDasharray="6 4"
|
|
121
|
+
y={1200}
|
|
122
|
+
/>
|
|
123
|
+
</LineChart>;
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The recipe's muted ink applies over any `fill` a caller passes to a `Label`.
|
|
127
|
+
|
|
128
|
+
### Annotations
|
|
129
|
+
|
|
130
|
+
`annotations` marks moments, periods and points on the category axis. The fields of each annotation
|
|
131
|
+
decide its mark:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { LineChart } from "@stealthscale/component-charts";
|
|
135
|
+
|
|
136
|
+
<LineChart
|
|
137
|
+
annotations={[
|
|
138
|
+
{ at: "2026-09-17", key: "4.12", label: "Release 4.12" },
|
|
139
|
+
{ at: "2026-09-20", color: "info", key: "freeze", label: "Change freeze", until: "2026-09-22" },
|
|
140
|
+
{ at: "2026-09-18", color: "error", key: "outage", label: "Processor outage", value: 312 },
|
|
141
|
+
]}
|
|
142
|
+
categoryKey="day"
|
|
143
|
+
data={days}
|
|
144
|
+
label="p95 latency per day"
|
|
145
|
+
series={[{ key: "p95", label: "p95 latency" }]}
|
|
146
|
+
/>;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
| Fields | Mark |
|
|
150
|
+
| ---------------- | ------------------------------------------------------------------------------- |
|
|
151
|
+
| `at` | A moment: a rule across the plot in 4px dashes 3px apart |
|
|
152
|
+
| `at` and `until` | A period: a wash from `at` to `until` at 0.12 opacity, without an edge |
|
|
153
|
+
| `at` and `value` | A point: a ring of radius 5 around `value` at `at`, 2px wide and without a fill |
|
|
154
|
+
|
|
155
|
+
- `at` and `until` are categories of the chart, in the values the category axis reads. A period's
|
|
156
|
+
ends may come in either order.
|
|
157
|
+
- A mark takes its annotation's `color`, the neutral palette's chart color unless stated.
|
|
158
|
+
- recharts renders a wash under the series and a rule and a ring over them. A ring widens the value
|
|
159
|
+
axis to its value. On a chart of bars on their side the marks run along the category axis, down
|
|
160
|
+
the plot.
|
|
161
|
+
- The words start 4px inside the top of a rule or a wash, or centre 4px above a ring, in the label
|
|
162
|
+
ink inside a halo in the panel's color. Words that leave the plot's sides, or meet the words of an
|
|
163
|
+
annotation earlier in the list, are hidden.
|
|
164
|
+
- The tooltip lists the annotations at its category after the series' rows, each with a swatch in
|
|
165
|
+
its color. The arrows move the tooltip through every category, so the live region reads each
|
|
166
|
+
annotation, hidden words included.
|
|
167
|
+
|
|
168
|
+
### Two periods
|
|
169
|
+
|
|
170
|
+
`alignPeriods(current, previous, options)` returns a row per row of the current period, with the
|
|
171
|
+
fields of the previous period's row at the same position. A series with `previousOf` is the earlier
|
|
172
|
+
period of the series it names:
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
import { alignPeriods, LineChart } from "@stealthscale/component-charts";
|
|
176
|
+
|
|
177
|
+
const days = alignPeriods(thisWeek, lastWeek, { categoryKey: "day", keys: ["revenue"] });
|
|
178
|
+
|
|
179
|
+
<LineChart
|
|
180
|
+
categoryKey="day"
|
|
181
|
+
data={days}
|
|
182
|
+
label="Revenue per day"
|
|
183
|
+
series={[
|
|
184
|
+
{ key: "revenue", label: "This week" },
|
|
185
|
+
{ key: "revenuePrevious", label: "Last week", previousOf: "revenue" },
|
|
186
|
+
]}
|
|
187
|
+
/>;
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- The periods match by position, the first row against the first row. A previous period that is
|
|
191
|
+
shorter leaves the last rows without its values.
|
|
192
|
+
- Each copied field takes the suffix `Previous` unless `suffix` states another, and the previous
|
|
193
|
+
period's own category is copied too, as `dayPrevious`.
|
|
194
|
+
- A series with `previousOf` renders dashed, in the neutral palette's chart color unless it states a
|
|
195
|
+
`color`.
|
|
196
|
+
- The tooltip writes the change from the earlier period after the current value, as a signed
|
|
197
|
+
percentage to one decimal after the locale's list separator: "€26,300, +8.2%". A row without a
|
|
198
|
+
finite earlier value, or with an earlier value of zero, writes the value alone.
|
|
199
|
+
|
|
200
|
+
### Zooming a long series
|
|
201
|
+
|
|
202
|
+
A preset plots the rows it receives, so a range picked in another control zooms it. The forms
|
|
203
|
+
`Slider` picks the range with two thumbs over the rows' indices. Each thumb takes its own `label`,
|
|
204
|
+
and `getAriaValueText` writes its date for a screen reader. The arrow keys, Page Up, Page Down, Home
|
|
205
|
+
and End move the focused thumb:
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
import { useState } from "react";
|
|
209
|
+
|
|
210
|
+
import { AreaChart } from "@stealthscale/component-charts";
|
|
211
|
+
import { Slider } from "@stealthscale/component-forms";
|
|
212
|
+
|
|
213
|
+
const [range, setRange] = useState([days.length - 91, days.length - 1]);
|
|
214
|
+
const [from = 0, to = 0] = range;
|
|
215
|
+
|
|
216
|
+
<>
|
|
217
|
+
<AreaChart
|
|
218
|
+
categoryKey="day"
|
|
219
|
+
data={days.slice(from, to + 1)}
|
|
220
|
+
label="Visitors per day"
|
|
221
|
+
series={[{ key: "visitors", label: "Visitors" }]}
|
|
222
|
+
/>
|
|
223
|
+
<Slider.Root
|
|
224
|
+
getAriaValueText={({ value }) => dateOf(value)}
|
|
225
|
+
max={days.length - 1}
|
|
226
|
+
minStepsBetweenThumbs={6}
|
|
227
|
+
onValueChange={({ value }) => {
|
|
228
|
+
setRange(value);
|
|
229
|
+
}}
|
|
230
|
+
value={range}
|
|
231
|
+
>
|
|
232
|
+
<Slider.Label>Days shown</Slider.Label>
|
|
233
|
+
<Slider.Control>
|
|
234
|
+
<Slider.Track>
|
|
235
|
+
<Slider.Range />
|
|
236
|
+
</Slider.Track>
|
|
237
|
+
<Slider.Thumb index={0} label="From" />
|
|
238
|
+
<Slider.Thumb index={1} label="To" />
|
|
239
|
+
</Slider.Control>
|
|
240
|
+
</Slider.Root>
|
|
241
|
+
</>;
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Targets and zones
|
|
245
|
+
|
|
246
|
+
A bar chart's series with `target` reads each row's target from that field and marks it with a tick
|
|
247
|
+
across the row's bar. `zones` fill each bar's row with the zones of the value axis. With both, each
|
|
248
|
+
bar is a bullet graph, and the rows compare their measures and their targets on one scale.
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { HorizontalBarChart } from "@stealthscale/component-charts";
|
|
252
|
+
|
|
253
|
+
<HorizontalBarChart
|
|
254
|
+
caption="Two of six teams passed their plan, and Borealis is behind at 64%."
|
|
255
|
+
categoryKey="team"
|
|
256
|
+
data={teams}
|
|
257
|
+
label="Plan attainment per team"
|
|
258
|
+
series={[{ key: "done", label: "Attainment", target: "plan" }]}
|
|
259
|
+
targetLabel="Plan"
|
|
260
|
+
valueDomain={[0, 1.2]}
|
|
261
|
+
valueOptions={{ style: "percent" }}
|
|
262
|
+
zones={[
|
|
263
|
+
{ color: "error", label: "Behind", upTo: 0.7 },
|
|
264
|
+
{ color: "warning", label: "Close", upTo: 0.9 },
|
|
265
|
+
{ color: "success", label: "On plan", upTo: 1.2 },
|
|
266
|
+
]}
|
|
267
|
+
/>;
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
- The tick is 2px wide and 76% of the bar's thickness, in the ink inside a halo in the panel's
|
|
271
|
+
color. A target past either end of the value axis is placed at that end. A row whose target is not
|
|
272
|
+
a finite number renders no tick.
|
|
273
|
+
- `zones` take the gauge's `GaugeZone`: where each zone ends, its palette and its name. They resolve
|
|
274
|
+
against the value axis' ends, which `valueDomain` states, and every row shares them.
|
|
275
|
+
- Each zone fills the whole row, tinted at 40% of its palette over the panel, and the part of the
|
|
276
|
+
axis no zone covers takes the track's fill. With zones the measure is 42% of the row's thickness,
|
|
277
|
+
centred.
|
|
278
|
+
- The tooltip writes the value, then the target after `targetLabel`, "Target" unless stated, then
|
|
279
|
+
the name of the zone the value is in, where the name is text.
|
|
280
|
+
- A key under the plot labels the tick with `targetLabel` and lists each zone with a `label`.
|
|
281
|
+
- The zones and the tick keep their places while the bars animate in.
|
|
282
|
+
|
|
283
|
+
### Keys
|
|
284
|
+
|
|
285
|
+
recharts' keyboard layer inverts the arrows in a sideways layout. ArrowLeft moves the tooltip down a
|
|
286
|
+
`HorizontalBarChart`, and ArrowRight moves it up.
|
|
287
|
+
|
|
288
|
+
## Mixed marks
|
|
289
|
+
|
|
290
|
+
`ComboChart`, `RangeChart`, `ParetoChart` and `BurndownChart` render marks of different kinds on one
|
|
291
|
+
plot. They take the preset props, and each adds its own:
|
|
292
|
+
|
|
293
|
+
```tsx
|
|
294
|
+
import { ComboChart } from "@stealthscale/component-charts";
|
|
295
|
+
|
|
296
|
+
<ComboChart
|
|
297
|
+
caption="Revenue rose to €254K in September at a 62% gross margin."
|
|
298
|
+
categoryKey="month"
|
|
299
|
+
data={months}
|
|
300
|
+
endDomain={[0, 1]}
|
|
301
|
+
endOptions={{ style: "percent" }}
|
|
302
|
+
label="Revenue and gross margin per month"
|
|
303
|
+
series={[
|
|
304
|
+
{ key: "revenue", label: "Revenue", mark: "bar" },
|
|
305
|
+
{ axis: "end", key: "margin", label: "Gross margin", mark: "line" },
|
|
306
|
+
]}
|
|
307
|
+
/>;
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
| Preset | Renders | Own props |
|
|
311
|
+
| --------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
312
|
+
| `ComboChart` | Each series as the mark it states: `bar`, `line` or `area` | `curve`, `endDomain`, `endOptions` |
|
|
313
|
+
| `RangeChart` | A band between two fields of each row, and lines plotted over it | `band`, `curve` |
|
|
314
|
+
| `ParetoChart` | Bars sorted largest first, and their running share on an end axis | `valueKey`, `valueLabel`, `cumulativeLabel`, `threshold`, `thresholdLabel` |
|
|
315
|
+
| `BurndownChart` | The work left per period, the ideal line and the projection | `points`, `periods`, `total`, `projection`, `remainingLabel`, `idealLabel`, `projectedLabel` |
|
|
316
|
+
|
|
317
|
+
- A combo series takes `mark` and `axis`. `axis: "end"` puts a series on a second value axis at the
|
|
318
|
+
plot's end edge, which renders while a series reads it. `endOptions` writes its ticks and its
|
|
319
|
+
series' tooltip values, and `endDomain` sets its range, from zero unless stated.
|
|
320
|
+
- recharts renders areas behind bars and bars behind lines, whatever the series' order. A combo
|
|
321
|
+
chart's cursor is a line, because only recharts' `BarChart` renders the band cursor.
|
|
322
|
+
- A range chart's `band` takes `{ key, label, color, low, high }`. It fills the space between its
|
|
323
|
+
two fields at 0.2 opacity with no edge, and the tooltip writes it as a range, such as
|
|
324
|
+
"15,500–19,500". The low field must be below the high field in every row.
|
|
325
|
+
- A Pareto chart sorts the rows largest first. `paretoRows` returns the sorted rows with each row's
|
|
326
|
+
`share` and running `cumulative` share, and `paretoCutoff(rows, threshold)` counts the rows it
|
|
327
|
+
takes to add up to the threshold, so a caption can state the count. The dashed threshold line is
|
|
328
|
+
at 80% unless `threshold` states otherwise, and `0` renders none.
|
|
329
|
+
- A burndown chart takes `{ at, remaining }` per period, with `remaining` missing for a period to
|
|
330
|
+
come. The ideal line runs straight from `total`, else the first reading, to zero over `periods`,
|
|
331
|
+
else over the points. The projection continues the least-squares trend of the readings from the
|
|
332
|
+
last reading and never goes below zero. A trend that does not fall has no projection.
|
|
333
|
+
- `burndownFinish` returns the period the projection falls to zero, as a fractional index, and
|
|
334
|
+
`burndownRows` returns the rows the chart plots. The ideal line and the projection take fractional
|
|
335
|
+
values. The burndown writes its values to one decimal unless `valueOptions` states otherwise.
|
|
336
|
+
|
|
337
|
+
## Stream and waterfall
|
|
338
|
+
|
|
339
|
+
`StreamGraph` stacks many series about a moving baseline. Each band's rise and fall reads across a
|
|
340
|
+
long span:
|
|
341
|
+
|
|
342
|
+
- `baseline` is `wiggle` unless stated, or `silhouette`, which centres the stack on a straight line.
|
|
343
|
+
The graph renders no value axis and no grid, and the stack fills the plot's height.
|
|
344
|
+
- The bands stack inside out. The series are sorted by onset, the centre of each series' mass, and
|
|
345
|
+
dealt onto the lighter of two piles, so a series that arrives late is outside and moves no band
|
|
346
|
+
beneath it. `insideOut={false}` keeps the series' order, the first at the bottom.
|
|
347
|
+
- The stack's order changes neither a series' color nor its place in the legend and the tooltip.
|
|
348
|
+
`insideOutOrder` and `streamOnset` return the order and the onsets.
|
|
349
|
+
- The legend renders while the series fit the theme's eight series colors.
|
|
350
|
+
|
|
351
|
+
`WaterfallChart` renders the steps that take a running total from one number to another:
|
|
352
|
+
|
|
353
|
+
```tsx
|
|
354
|
+
import { WaterfallChart } from "@stealthscale/component-charts";
|
|
355
|
+
|
|
356
|
+
<WaterfallChart
|
|
357
|
+
caption="MRR grew from €120K in August to €135.9K in September."
|
|
358
|
+
label="MRR bridge from August to September"
|
|
359
|
+
steps={[
|
|
360
|
+
{ key: "august", label: "August", total: true, value: 120_000 },
|
|
361
|
+
{ key: "new", label: "New", value: 18_400 },
|
|
362
|
+
{ key: "churn", label: "Churn", value: -7600 },
|
|
363
|
+
{ key: "september", label: "September", total: true },
|
|
364
|
+
]}
|
|
365
|
+
valueOptions={{ currency: "EUR", notation: "compact", style: "currency" }}
|
|
366
|
+
/>;
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
- A change floats from the running total before it to the total after it, in the success palette's
|
|
370
|
+
chart color when it rises and the error palette's when it falls. A total rises from zero in the
|
|
371
|
+
neutral palette's chart color.
|
|
372
|
+
- A total with a `value` sets the running total, for an opening balance. A total without one shows
|
|
373
|
+
the running total.
|
|
374
|
+
- A dashed connector joins each bar to the next at the running total. The chart writes each bar's
|
|
375
|
+
signed change or total above the bar and keeps 20px free above the tallest bar for it.
|
|
376
|
+
- The values show while the widest one fits a step, at 8px a character, and hide together in a
|
|
377
|
+
narrower plot.
|
|
378
|
+
- The tooltip writes the same value as the bar, and its swatch takes the bar's color.
|
|
379
|
+
- The value axis rounds its range to whole ticks and runs through zero where the running total goes
|
|
380
|
+
below it. `waterfallBars` returns the bars the chart renders, with each step's `start`, `end` and
|
|
381
|
+
`change`.
|
|
382
|
+
|
|
383
|
+
| Prop | Takes | Default |
|
|
384
|
+
| ------------------------------------------------------------------------- | ------------------------------------------------------------------- | -------- |
|
|
385
|
+
| `steps` | `{ key, label, value, total }` per step, in the order they happened | Required |
|
|
386
|
+
| `label` | The chart's accessible name, recharts' `title` | Required |
|
|
387
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
388
|
+
| `valueLabel` | The name of the values in the tooltip | `Amount` |
|
|
389
|
+
| `valueLabels` | Whether each bar's value is written above it | `true` |
|
|
390
|
+
| `connectors` | Whether dashed connectors join the bars | `true` |
|
|
391
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the ticks, the tooltip and the bars | A number |
|
|
392
|
+
| `grid`, `defaultIndex`, `animate`, `empty`, `locale`, `ratio`, `children` | As the cartesian presets take them | |
|
|
393
|
+
|
|
394
|
+
## Points
|
|
395
|
+
|
|
396
|
+
`ScatterPlot` renders a point per thing at two measures, and `BubbleChart` also sizes each point by
|
|
397
|
+
a third:
|
|
398
|
+
|
|
399
|
+
```tsx
|
|
400
|
+
import { ScatterPlot } from "@stealthscale/component-charts";
|
|
401
|
+
|
|
402
|
+
<ScatterPlot
|
|
403
|
+
caption="Mid-market deals close within 50 days, while enterprise deals take 88 to 178 days."
|
|
404
|
+
label="Days to close by deal size"
|
|
405
|
+
series={[
|
|
406
|
+
{ key: "mid", label: "Mid-market", points: midMarket },
|
|
407
|
+
{ key: "enterprise", label: "Enterprise", points: enterprise },
|
|
408
|
+
]}
|
|
409
|
+
xKey="size"
|
|
410
|
+
xLabel="Deal size"
|
|
411
|
+
yKey="days"
|
|
412
|
+
yLabel="Days to close"
|
|
413
|
+
/>;
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
- Each series takes its own `points`, because a scatter's series do not share a category. `xKey` and
|
|
417
|
+
`yKey` are the fields of a point.
|
|
418
|
+
- `xLabel` and `yLabel` are required titles, because neither axis of a scatter explains itself. The
|
|
419
|
+
x title renders under the ticks and the y title along the start edge. `xOptions`, `yOptions`,
|
|
420
|
+
`xDomain` and `yDomain` format and range each axis.
|
|
421
|
+
- A point is 60 square pixels unless `size` states otherwise. `BubbleChart` takes `sizeKey` and
|
|
422
|
+
`sizeLabel`, and recharts scales each point's area over `sizeRange`, 40 to 1600 square pixels
|
|
423
|
+
unless stated. Both axes are padded by the largest point's radius, so no point crosses the plot's
|
|
424
|
+
edge.
|
|
425
|
+
- The tooltip's heading is the label of the point's series. Each row writes a value in its axis'
|
|
426
|
+
format beside the axis' title. `defaultIndex` opens it at a point of the first series.
|
|
427
|
+
- recharts' keyboard layer walks the first series' points only, measured in Firefox and Chromium, so
|
|
428
|
+
the series a reader walks goes first.
|
|
429
|
+
- `RegressionOverlay` is a recharts child that renders the least-squares line through `data` as a
|
|
430
|
+
dashed segment over the x range the points cover. The fit leaves out a point whose x or y is not a
|
|
431
|
+
finite number. `linearRegression(points, xKey, yKey)` returns the line's `slope`, `intercept` and
|
|
432
|
+
`r2`. A caption states r², and a page leaves the line out where r² is low.
|
|
433
|
+
- With `series`, the line takes the series' color, hides while the legend hides the series, and
|
|
434
|
+
fades with the series' points. Without it, the line takes the neutral palette's chart color and
|
|
435
|
+
ignores the legend, for a fit across series.
|
|
436
|
+
- recharts' own fitted line, `lineType="fitting"` on a `Scatter`, fits a point without a value at
|
|
437
|
+
pixel (0, 0), the top-left corner of the chart's `svg`, and returns no r².
|
|
438
|
+
|
|
439
|
+
### Quadrants
|
|
440
|
+
|
|
441
|
+
A quadrant chart divides the plot into four named quadrants and writes each point's name beside it:
|
|
442
|
+
|
|
443
|
+
```tsx
|
|
444
|
+
import { ScatterPlot } from "@stealthscale/component-charts";
|
|
445
|
+
|
|
446
|
+
<ScatterPlot
|
|
447
|
+
caption="Alder and Cedar lead on both vision and execution."
|
|
448
|
+
label="Vendors by vision and execution"
|
|
449
|
+
labelKey="name"
|
|
450
|
+
quadrants={{
|
|
451
|
+
names: {
|
|
452
|
+
bottomEnd: "Visionaries",
|
|
453
|
+
bottomStart: "Niche players",
|
|
454
|
+
topEnd: "Leaders",
|
|
455
|
+
topStart: "Challengers",
|
|
456
|
+
},
|
|
457
|
+
}}
|
|
458
|
+
series={[{ key: "vendors", label: "Vendors", points: vendors }]}
|
|
459
|
+
xEnds={["Low", "High"]}
|
|
460
|
+
xKey="vision"
|
|
461
|
+
xLabel="Vision"
|
|
462
|
+
yEnds={["Low", "High"]}
|
|
463
|
+
yKey="execution"
|
|
464
|
+
yLabel="Execution"
|
|
465
|
+
/>;
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
- `quadrants` takes the name of each quadrant, keyed `topStart`, `topEnd`, `bottomStart` and
|
|
469
|
+
`bottomEnd` in the reading direction, and `x` and `y`, the values the plot divides at. Unless they
|
|
470
|
+
are stated, the plot divides at the middle of each axis' span: the axis' domain where it states
|
|
471
|
+
two numbers, 0 to 1 for an axis with named ends, else the points' own values.
|
|
472
|
+
- Two dashed lines in the emphasized border ink divide the plot, and each name is written in its
|
|
473
|
+
corner in the muted ink. A point on a line belongs to the upper or the end side, and
|
|
474
|
+
`quadrantOf(x, y, division)` returns a point's quadrant.
|
|
475
|
+
- `labelKey` names a field of each point whose words are written beside the point and head its
|
|
476
|
+
tooltip. The words run toward the middle past 72% of the plot's width. Words that leave the plot,
|
|
477
|
+
or meet the words of a point earlier in the series, are hidden, and the tooltip still names the
|
|
478
|
+
point.
|
|
479
|
+
- With `quadrants`, the tooltip's heading names the point's quadrant after the point, joined by the
|
|
480
|
+
locale's list separator: "Alder, Leaders".
|
|
481
|
+
- `xEnds` and `yEnds` name an axis' two ends, such as "Rare" and "Certain", in place of its ticks.
|
|
482
|
+
The axis spans `xDomain` or `yDomain`, 0 to 1 unless it states two numbers, and the grid renders
|
|
483
|
+
the plot's two edges alone along it.
|
|
484
|
+
- `spreadPoints(points, options)` moves points closer than `distance` apart, 0.06 of each axis' span
|
|
485
|
+
unless stated, along a golden-angle spiral around each point's place. Each point keeps its
|
|
486
|
+
quadrant, and every call places the points the same way. The function moves the plotted values, so
|
|
487
|
+
the tooltip writes the moved values: it suits scores read by quadrant, never measurements.
|
|
488
|
+
|
|
489
|
+
## Distributions
|
|
490
|
+
|
|
491
|
+
`HistogramChart` renders the shape of one distribution: its values counted into bins that touch
|
|
492
|
+
along a numeric axis.
|
|
493
|
+
|
|
494
|
+
```tsx
|
|
495
|
+
import { HistogramChart } from "@stealthscale/component-charts";
|
|
496
|
+
|
|
497
|
+
<HistogramChart
|
|
498
|
+
caption="107 of 140 requests finish under 60ms, and a slow tail runs to 460ms."
|
|
499
|
+
countLabel="Requests"
|
|
500
|
+
label="Response times of the checkout API"
|
|
501
|
+
valueOptions={{ style: "unit", unit: "millisecond", unitDisplay: "narrow" }}
|
|
502
|
+
values={latencies}
|
|
503
|
+
/>;
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
- The chart takes `values`, one number per thing measured, not rows. A value that is not a finite
|
|
507
|
+
number is left out.
|
|
508
|
+
- `binCount(values)` returns Freedman–Diaconis' count, or Sturges' where the middle half of the
|
|
509
|
+
values ties, at most 30. `binValues(values, { bins, domain })` counts the values into bins whose
|
|
510
|
+
width snaps to the nearest of 1, 2 or 5 times a power of ten, as d3's and Vega's bins do, and one
|
|
511
|
+
step coarser where the bins would pass 30.
|
|
512
|
+
- `bins` aims for a number of bins. `domain` bins a range from its lower edge and leaves out the
|
|
513
|
+
values outside the bins. When every value is equal, the chart counts them in one bin a tenth of
|
|
514
|
+
the value wide.
|
|
515
|
+
- A bin runs from its lower edge up to its upper edge and leaves the upper edge to the next bin. The
|
|
516
|
+
last bin includes its upper edge, so the largest value is counted.
|
|
517
|
+
- The bars touch, parted by the recipe's hairline in the panel's color. Each bar runs from its lower
|
|
518
|
+
edge to its upper edge at the axis' own pixels, where recharts rounds a bar's width to a whole
|
|
519
|
+
pixel.
|
|
520
|
+
- The axis labels every edge, else every second or every fifth, so at most eight even steps show.
|
|
521
|
+
The tooltip heads each bar with its range, such as "20–40ms", and writes its count.
|
|
522
|
+
|
|
523
|
+
| Prop | Takes | Default |
|
|
524
|
+
| ------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------ |
|
|
525
|
+
| `values` | The values to count, one per thing measured | Required |
|
|
526
|
+
| `label` | The chart's accessible name, recharts' `title` | Required |
|
|
527
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
528
|
+
| `bins` | The number of bins to aim for, at most 30 | Freedman–Diaconis' count |
|
|
529
|
+
| `domain` | The range to bin, from its lower edge | The values' range |
|
|
530
|
+
| `countLabel` | The name of the counts in the tooltip | `Count` |
|
|
531
|
+
| `color` | A hue or a palette for the bars | `series.1` |
|
|
532
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the edges and the tooltip's heading | A number in the locale |
|
|
533
|
+
| `grid`, `defaultIndex`, `animate`, `empty`, `locale`, `ratio`, `children` | As the cartesian presets take them | |
|
|
534
|
+
|
|
535
|
+
`DistributionChart` compares two or more distributions on one set of bins:
|
|
536
|
+
|
|
537
|
+
```tsx
|
|
538
|
+
import { DistributionChart } from "@stealthscale/component-charts";
|
|
539
|
+
|
|
540
|
+
<DistributionChart
|
|
541
|
+
caption="Three in four Free sessions end within 10 minutes, against one in ten Pro sessions."
|
|
542
|
+
label="Session lengths by plan"
|
|
543
|
+
normalize
|
|
544
|
+
series={[
|
|
545
|
+
{ key: "free", label: "Free", values: freeSessions },
|
|
546
|
+
{ key: "pro", label: "Pro", values: proSessions },
|
|
547
|
+
]}
|
|
548
|
+
valueOptions={{ style: "unit", unit: "minute", unitDisplay: "short" }}
|
|
549
|
+
/>;
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
- The chart chooses the bins from every series' values together, so the series' bars start at the
|
|
553
|
+
same edges. Histograms binned apart put their bars at different edges and widths.
|
|
554
|
+
- `normalize` counts each bin as a share of its own series' total, so series of different sizes
|
|
555
|
+
compare. The value axis and the tooltip then write percents.
|
|
556
|
+
- The bars of a bin are side by side in the series' order, each a share of the bin's width. A series
|
|
557
|
+
the legend hides gives its share to the series shown.
|
|
558
|
+
- A series takes `{ key, label, color, values }`. The chart takes the histogram's `bins`,
|
|
559
|
+
`valueOptions`, `grid`, `defaultIndex`, `animate`, `empty`, `locale`, `ratio` and `children`, and
|
|
560
|
+
the cartesian presets' legend props.
|
|
561
|
+
|
|
562
|
+
`BoxPlot` compares the distributions of groups: each group's median, middle half, whiskers and
|
|
563
|
+
outliers.
|
|
564
|
+
|
|
565
|
+
```tsx
|
|
566
|
+
import { BoxPlot } from "@stealthscale/component-charts";
|
|
567
|
+
|
|
568
|
+
<BoxPlot
|
|
569
|
+
caption="Singapore's median of 165ms is more than twice Frankfurt's 61ms."
|
|
570
|
+
countLabel="Requests"
|
|
571
|
+
groups={[
|
|
572
|
+
{ key: "frankfurt", label: "Frankfurt", values: frankfurt },
|
|
573
|
+
{ key: "singapore", label: "Singapore", values: singapore },
|
|
574
|
+
]}
|
|
575
|
+
label="Response times of the checkout API by region"
|
|
576
|
+
valueOptions={{ style: "unit", unit: "millisecond", unitDisplay: "narrow" }}
|
|
577
|
+
/>;
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
- A group takes `{ key, label, values }`, or a `summary` in place of its values, for values
|
|
581
|
+
summarised elsewhere. `boxStats(values, whisker)` returns the summary the chart renders.
|
|
582
|
+
`quantile(sorted, share)` reads its quartiles by type 7, the rule of SQL's `PERCENTILE_CONT`, so a
|
|
583
|
+
query that summarises the values matches the chart.
|
|
584
|
+
- The box runs from the first quartile to the third, and the median crosses it in the ink. Each
|
|
585
|
+
whisker extends to the furthest value within `whisker` interquartile ranges of the box, and ends
|
|
586
|
+
at the box's edge when no value is within that distance.
|
|
587
|
+
- Every value past a whisker renders as a hollow point in the box's color, and outliers of equal
|
|
588
|
+
value share one point. `outliers={false}` leaves the points out.
|
|
589
|
+
- The value axis is rounded around the values, because a box plot compares where values fall.
|
|
590
|
+
`valueDomain` sets another range, such as one scale for every week.
|
|
591
|
+
- A box is at most 72px wide. The box under the pointer or the keyboard fills at 0.6 of its color
|
|
592
|
+
inside an edge 2px wide, and the rest fill at 0.35.
|
|
593
|
+
- The key under the plot labels the box, the median, the whiskers and the outliers beside a glyph of
|
|
594
|
+
each in the chart's inks, because a box plot is read by convention. `legend={false}` leaves it
|
|
595
|
+
out.
|
|
596
|
+
- The tooltip heads a group with its name and writes its median, middle half, whiskers, outliers and
|
|
597
|
+
count. `animate` grows each box from the axis with the bar's entrance.
|
|
598
|
+
|
|
599
|
+
| Prop | Takes | Default |
|
|
600
|
+
| ------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------- |
|
|
601
|
+
| `groups` | `{ key, label, values, summary }` per group, in the axis' order | Required |
|
|
602
|
+
| `label` | The chart's accessible name, recharts' `title` | Required |
|
|
603
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
604
|
+
| `whisker` | The whiskers' reach in interquartile ranges | `1.5` |
|
|
605
|
+
| `outliers` | Whether each outlier renders as a point | `true` |
|
|
606
|
+
| `legend` | Whether the key renders | `true`, while a group has values |
|
|
607
|
+
| `color` | A hue or a palette for the boxes | `series.1` |
|
|
608
|
+
| `valueDomain` | recharts' domain of the value axis | Rounded around the values |
|
|
609
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the ticks and the tooltip's values | A number in the locale |
|
|
610
|
+
| `medianLabel`, `quartilesLabel`, `whiskersLabel`, `outliersLabel`, `countLabel` | The names in the tooltip and the key | `Median`, `Middle half`, `Whiskers`, `Outliers`, `Count` |
|
|
611
|
+
| `grid`, `defaultIndex`, `animate`, `empty`, `locale`, `ratio`, `children` | As the cartesian presets take them | |
|
|
612
|
+
|
|
613
|
+
`ViolinPlot` renders the density of each group's values, mirrored about the middle of its band, with
|
|
614
|
+
its quartiles inside.
|
|
615
|
+
|
|
616
|
+
```tsx
|
|
617
|
+
import { ViolinPlot } from "@stealthscale/component-charts";
|
|
618
|
+
|
|
619
|
+
<ViolinPlot
|
|
620
|
+
caption="Search requests peak at 22ms and at 167ms."
|
|
621
|
+
countLabel="Requests"
|
|
622
|
+
groups={[
|
|
623
|
+
{ key: "search", label: "Search", values: search },
|
|
624
|
+
{ key: "checkout", label: "Checkout", values: checkout },
|
|
625
|
+
]}
|
|
626
|
+
label="Response times of the shop's API by endpoint"
|
|
627
|
+
valueOptions={{ style: "unit", unit: "millisecond", unitDisplay: "narrow" }}
|
|
628
|
+
/>;
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
- A group takes `{ key, label, values }`. The chart takes the values themselves, because no summary
|
|
632
|
+
gives a density back.
|
|
633
|
+
- `kernelDensity(values, { bandwidth, resolution })` returns a Gaussian kernel density sampled at
|
|
634
|
+
`resolution` even steps, 64 unless stated, from the smallest value to the largest. The outline
|
|
635
|
+
stops at those values, because a kernel puts mass past the data, such as below zero for a
|
|
636
|
+
duration.
|
|
637
|
+
- Each group's kernel is as wide as `silvermanBandwidth(values)` returns, unless `bandwidth` states
|
|
638
|
+
one width for every group. Silverman's rule takes 0.9 times the smaller of the standard deviation
|
|
639
|
+
and the interquartile range over 1.34, times the count to the power of −1/5.
|
|
640
|
+
- A kernel wider than the gap between two peaks merges them, and a narrow one shows sampling noise
|
|
641
|
+
as peaks.
|
|
642
|
+
- Each violin is as wide as its bar, 110px at most, at its own densest value. Its width compares
|
|
643
|
+
within the group only.
|
|
644
|
+
- Inside each violin, a line runs between the whiskers `boxStats` returns, a narrow bar in the ink
|
|
645
|
+
spans the middle half, and a hollow point marks the median. `quartiles={false}` leaves the three
|
|
646
|
+
out.
|
|
647
|
+
- The violin under the pointer or the keyboard fills at 0.5 of its color inside an edge 2px wide,
|
|
648
|
+
and the rest fill at 0.28.
|
|
649
|
+
- `densityPeaks(density)` returns the values a density peaks at, each at least 5% of the tallest
|
|
650
|
+
peak's height.
|
|
651
|
+
- A violin's tooltip lists its median, middle half, range, peaks and count. Each peak takes the
|
|
652
|
+
decimals of the density's sampling step, because a peak is one of the sampled values.
|
|
653
|
+
- The key labels the density. While `quartiles` is on, it also labels the middle half and the
|
|
654
|
+
median.
|
|
655
|
+
|
|
656
|
+
| Prop | Takes | Default |
|
|
657
|
+
| ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
658
|
+
| `groups` | `{ key, label, values }` per group, in the axis' order | Required |
|
|
659
|
+
| `label` | The chart's accessible name, recharts' `title` | Required |
|
|
660
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
661
|
+
| `bandwidth` | The kernel's standard deviation for every group, in the values' units | Silverman's, per group |
|
|
662
|
+
| `resolution` | The number of points each density is sampled at | `64` |
|
|
663
|
+
| `quartiles` | Whether the line, the bar and the point render inside each violin | `true` |
|
|
664
|
+
| `legend` | Whether the key renders | `true`, while a group has values |
|
|
665
|
+
| `color`, `valueDomain`, `valueOptions` | As the box plot takes them | |
|
|
666
|
+
| `medianLabel`, `quartilesLabel`, `rangeLabel`, `peaksLabel`, `countLabel`, `densityLabel` | The names in the tooltip and the key | `Median`, `Middle half`, `Range`, `Peaks`, `Count`, `Density` |
|
|
667
|
+
| `grid`, `defaultIndex`, `animate`, `empty`, `locale`, `ratio`, `children` | As the cartesian presets take them | |
|
|
668
|
+
|
|
669
|
+
`CandlestickChart` renders each period's open, high, low and close as a body inside a wick:
|
|
670
|
+
|
|
671
|
+
```tsx
|
|
672
|
+
import { CandlestickChart } from "@stealthscale/component-charts";
|
|
673
|
+
|
|
674
|
+
<CandlestickChart
|
|
675
|
+
caption="ACME rose 12.7% over September, from $156.22 to $176.03."
|
|
676
|
+
categoryKey="day"
|
|
677
|
+
data={days}
|
|
678
|
+
label="Daily prices of ACME"
|
|
679
|
+
labelOptions={{ day: "numeric", month: "short", timeZone: "UTC" }}
|
|
680
|
+
valueOptions={{ currency: "USD", style: "currency" }}
|
|
681
|
+
/>;
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
- Each row takes `open`, `high`, `low` and `close`. A row without four finite prices is left out.
|
|
685
|
+
- The body runs from the open to the close, and the wick from the low to the high.
|
|
686
|
+
- `candleDirection(candle)` compares the close with the candle's own open. A rising candle takes the
|
|
687
|
+
success palette's chart color, a falling one the error palette's, and a flat one the neutral
|
|
688
|
+
palette's.
|
|
689
|
+
- `candleChange(candle)` returns the change and its share of the open. The share is `undefined` for
|
|
690
|
+
an open of 0.
|
|
691
|
+
- A doji, a period that closed at its open, has a body 1.5px high. A candle is at most 28px wide,
|
|
692
|
+
and the candle under the pointer or the keyboard takes a wick and an edge 2px wide.
|
|
693
|
+
- The category axis is ordinal, so a weekend takes no room. `labelOptions` formats the periods on
|
|
694
|
+
the ticks and in the tooltip's heading.
|
|
695
|
+
- The value axis is rounded around the prices and does not start at zero, because a price is read
|
|
696
|
+
for its movement. Its ticks step by 1, 2, 2.5 or 5 times a power of ten, recharts' `snap125` rule.
|
|
697
|
+
- A candle's tooltip lists its open, high, low and close, then the change with its sign and its
|
|
698
|
+
percentage of the open to two decimals.
|
|
699
|
+
- The key labels the rising color and the falling color with the data package's `ColorSwatch`. It
|
|
700
|
+
labels the flat color while the chart has a flat candle, and then the body and the wick.
|
|
701
|
+
|
|
702
|
+
| Prop | Takes | Default |
|
|
703
|
+
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------- |
|
|
704
|
+
| `data` | The rows, each with `open`, `high`, `low` and `close` | Required |
|
|
705
|
+
| `categoryKey` | The field of each row the category axis reads | Required |
|
|
706
|
+
| `label` | The chart's accessible name, recharts' `title` | Required |
|
|
707
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
708
|
+
| `labelOptions` | `Intl.DateTimeFormatOptions` for the category ticks and the tooltip's heading | The category as it is |
|
|
709
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the ticks, the prices and the change | A number in the locale |
|
|
710
|
+
| `valueDomain` | recharts' domain of the value axis | Rounded around the prices |
|
|
711
|
+
| `legend` | Whether the key renders | `true`, while a row has prices |
|
|
712
|
+
| `openLabel`, `highLabel`, `lowLabel`, `closeLabel`, `changeLabel` | The names in the tooltip | `Open`, `High`, `Low`, `Close`, `Change` |
|
|
713
|
+
| `upLabel`, `downLabel`, `flatLabel`, `bodyLabel`, `wickLabel` | The names in the key | `Up`, `Down`, `Flat`, `Open to close`, `Low to high` |
|
|
714
|
+
| `grid`, `defaultIndex`, `animate`, `empty`, `locale`, `ratio`, `children` | As the cartesian presets take them | |
|
|
715
|
+
|
|
716
|
+
## Pie and donut
|
|
717
|
+
|
|
718
|
+
`PieChart` and `DonutChart` render the parts of one whole from slices:
|
|
719
|
+
|
|
720
|
+
```tsx
|
|
721
|
+
import { DonutChart } from "@stealthscale/component-charts";
|
|
722
|
+
|
|
723
|
+
<DonutChart
|
|
724
|
+
caption="Video takes 60% of the 195.5 GB in use."
|
|
725
|
+
center="195.5 GB"
|
|
726
|
+
centerLabel="in use"
|
|
727
|
+
label="Storage per kind of file"
|
|
728
|
+
slices={[
|
|
729
|
+
{ key: "video", label: "Video", value: 118 },
|
|
730
|
+
{ key: "images", label: "Images", value: 42 },
|
|
731
|
+
{ key: "archives", label: "Archives", value: 26 },
|
|
732
|
+
]}
|
|
733
|
+
/>;
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
- The chart renders the slices largest first, from 12 o'clock and clockwise, because a reader
|
|
737
|
+
compares each slice with its neighbours.
|
|
738
|
+
- The chart writes each slice's share of the slices shown on the slice, in the chart's locale, in
|
|
739
|
+
the ink inside a halo in the panel's color, which reads on a slice of any color. The pointer
|
|
740
|
+
passes through a share to its slice. `shares={false}` leaves the shares out.
|
|
741
|
+
- `maxSlices` gathers the slices past it into one slice named by `otherLabel`, keyed `other`, in the
|
|
742
|
+
neutral palette's chart color.
|
|
743
|
+
- A slice the legend hides leaves the pie, and the shares are of the slices shown. The pointer or
|
|
744
|
+
focus on a legend button fades every other slice.
|
|
745
|
+
- `DonutChart` cuts a hole of 58% of the chart's radius and renders `center` and `centerLabel` in
|
|
746
|
+
it. The chart does not sum the slices, because the figure in the hole is as often the largest
|
|
747
|
+
share or a change as the total.
|
|
748
|
+
|
|
749
|
+
| Prop | Takes | Default |
|
|
750
|
+
| -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | -------------------------- |
|
|
751
|
+
| `slices` | `{ key, label, value, color }` per slice, in any order | Required |
|
|
752
|
+
| `label` | The chart's accessible name, recharts' `title` | Required |
|
|
753
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
754
|
+
| `shares` | Whether each slice's share is written on it | `true` |
|
|
755
|
+
| `maxSlices` | The largest number of slices the pie renders | Every slice |
|
|
756
|
+
| `otherLabel` | The name of the slice the tail gathers into | `Other` |
|
|
757
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the tooltip's values | A number in the locale |
|
|
758
|
+
| `legend` | Whether the legend renders | `true`, while slices exist |
|
|
759
|
+
| `ratio` | The plot's aspect ratio | `square` |
|
|
760
|
+
| `legendLabel`, `defaultIndex`, `animate`, `empty`, `hiddenKeys`, `defaultHiddenKeys`, `onHiddenKeysChange`, `locale`, `children` | As the cartesian presets take them | |
|
|
761
|
+
| `center`, `centerLabel` | `DonutChart` only: the figure in the hole and its words | None |
|
|
762
|
+
|
|
763
|
+
## Radial charts
|
|
764
|
+
|
|
765
|
+
`RadarChart` renders each series' values across three or more dimensions as a polygon on one spoke
|
|
766
|
+
per dimension:
|
|
767
|
+
|
|
768
|
+
```tsx
|
|
769
|
+
import { RadarChart } from "@stealthscale/component-charts";
|
|
770
|
+
|
|
771
|
+
<RadarChart
|
|
772
|
+
caption="Cost is the only score that fell this quarter, from 6 to 4."
|
|
773
|
+
categoryKey="dimension"
|
|
774
|
+
data={scores}
|
|
775
|
+
label="Checkout service scores out of 10"
|
|
776
|
+
series={[
|
|
777
|
+
{ key: "current", label: "This quarter" },
|
|
778
|
+
{ key: "previous", label: "Last quarter" },
|
|
779
|
+
]}
|
|
780
|
+
valueDomain={[0, 10]}
|
|
781
|
+
/>;
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
- Each row is a spoke named by `categoryKey`, and each series is a polygon. The chart takes the
|
|
785
|
+
cartesian presets' props.
|
|
786
|
+
- The spokes share one radius axis, so the dimensions must share a scale. `valueDomain` states it,
|
|
787
|
+
such as `[0, 10]` for scores out of ten. recharts rounds the axis from zero around the values
|
|
788
|
+
unless it is stated.
|
|
789
|
+
- The web's rings follow six radius ticks, which step a top of 1, 5, 10 or 100 evenly. `scale`
|
|
790
|
+
writes the ticks up the spoke at 12 o'clock, each hanging from its ring inside a halo in the
|
|
791
|
+
panel's color.
|
|
792
|
+
- A polygon fills at 0.25 of its color inside an edge 2px wide. `filled: false` on a series leaves
|
|
793
|
+
the outline, for three series or more.
|
|
794
|
+
- The web's radius is 72% of the largest circle the plot fits, which leaves the spokes' names room
|
|
795
|
+
beside it. The plot's ratio is `landscape` unless stated.
|
|
796
|
+
- The polygon's shape follows the order of the rows, so two orders of the same scores render two
|
|
797
|
+
shapes. Compare radars only with their spokes in one order.
|
|
798
|
+
- The tooltip's heading is the spoke's name, with a row per series. The arrows walk the spokes, and
|
|
799
|
+
the cursor is a line along the spoke under the pointer.
|
|
800
|
+
|
|
801
|
+
| Prop | Takes | Default |
|
|
802
|
+
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------- |
|
|
803
|
+
| `series` | `{ key, label, color, filled }` per series, in the legend's order | Required |
|
|
804
|
+
| `scale` | Whether the radius ticks are written up the top spoke | `false` |
|
|
805
|
+
| `ratio` | The plot's aspect ratio | `landscape` |
|
|
806
|
+
| `data`, `categoryKey`, `label`, `caption`, `labelOptions`, `valueOptions`, `valueDomain`, `grid`, `legend`, `legendLabel`, `defaultIndex`, `animate`, `empty`, the hidden keys, `locale`, `children` | As the cartesian presets take them | |
|
|
807
|
+
|
|
808
|
+
`RadialBarChart` renders each measure as a ring read against the value of a full turn, the first
|
|
809
|
+
ring outermost:
|
|
810
|
+
|
|
811
|
+
```tsx
|
|
812
|
+
import { RadialBarChart } from "@stealthscale/component-charts";
|
|
813
|
+
|
|
814
|
+
<RadialBarChart
|
|
815
|
+
bars={[
|
|
816
|
+
{ key: "storage", label: "Storage", value: 0.82 },
|
|
817
|
+
{ key: "seats", label: "Seats", value: 0.61 },
|
|
818
|
+
{ key: "api", label: "API calls", value: 0.34 },
|
|
819
|
+
]}
|
|
820
|
+
caption="Storage is at 82% of the plan's quota, and nothing else is past two thirds."
|
|
821
|
+
label="Plan usage"
|
|
822
|
+
max={1}
|
|
823
|
+
valueOptions={{ style: "percent" }}
|
|
824
|
+
/>;
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
- A ring shows one measure against its own track, such as the use of a quota. A bar chart compares
|
|
828
|
+
measures with each other.
|
|
829
|
+
- `max` is the value of a full turn, such as a quota, a target or a capacity. Without it the largest
|
|
830
|
+
value shown closes its track, and every other ring is measured against that value.
|
|
831
|
+
- A ring past `max` closes its track, and the other rings keep their share of `max`. A ring below
|
|
832
|
+
zero renders no arc. The legend and the tooltip write each ring's own value.
|
|
833
|
+
- A ring's length is its angle times its radius, so the same value renders a longer arc further out.
|
|
834
|
+
The legend writes each ring's value beside its name, because an arc is not read for a quantity.
|
|
835
|
+
`values={false}` leaves the names alone.
|
|
836
|
+
- Each ring's track is the rest of its full turn in the neutral palette's `subtle` fill.
|
|
837
|
+
`track={false}` leaves the tracks out. Under forced colors a track fills with `Canvas` inside a
|
|
838
|
+
`GrayText` edge.
|
|
839
|
+
- The rings turn clockwise from 12 o'clock. `startAngle` and `endAngle` take recharts' degrees,
|
|
840
|
+
where 90 is 12 o'clock and clockwise is negative, so `225` and `-45` turn three quarters with the
|
|
841
|
+
gap at the bottom.
|
|
842
|
+
- The hole in the middle is 30% of the largest circle the plot fits. The plot's ratio is `square`
|
|
843
|
+
unless stated.
|
|
844
|
+
- The tooltip writes the ring's name and value without a heading, and no cursor renders. The arrows
|
|
845
|
+
walk the rings from the outermost.
|
|
846
|
+
- A ring the legend hides leaves the chart. With `max` the other rings keep their turn.
|
|
847
|
+
|
|
848
|
+
| Prop | Takes | Default |
|
|
849
|
+
| ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------ |
|
|
850
|
+
| `bars` | `{ key, label, value, color }` per ring, the outermost first | Required |
|
|
851
|
+
| `label` | The chart's accessible name, recharts' `title` | Required |
|
|
852
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
853
|
+
| `max` | The value of a full turn | The largest value shown |
|
|
854
|
+
| `track` | Whether the rest of each ring's turn renders behind it | `true` |
|
|
855
|
+
| `values` | Whether the legend writes each ring's value beside its name | `true` |
|
|
856
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the legend's and the tooltip's values | A number in the locale |
|
|
857
|
+
| `startAngle`, `endAngle` | The angles the rings start and end at, in recharts' degrees | `90`, `-270` |
|
|
858
|
+
| `legend` | Whether the legend renders | `true`, while bars exist |
|
|
859
|
+
| `ratio` | The plot's aspect ratio | `square` |
|
|
860
|
+
| `legendLabel`, `defaultIndex`, `animate`, `empty`, the hidden keys, `locale`, `children` | As the cartesian presets take them | |
|
|
861
|
+
|
|
862
|
+
`PolarAreaChart` renders a rose: each category of a cycle as a wedge of the same angle, whose area
|
|
863
|
+
follows its value:
|
|
864
|
+
|
|
865
|
+
```tsx
|
|
866
|
+
import { PolarAreaChart } from "@stealthscale/component-charts";
|
|
867
|
+
|
|
868
|
+
<PolarAreaChart
|
|
869
|
+
caption="The load builds from 06:00, peaks at noon and falls by 21:00."
|
|
870
|
+
label="Requests a minute by hour"
|
|
871
|
+
slices={[
|
|
872
|
+
{ key: "00", label: "00:00", value: 120 },
|
|
873
|
+
{ key: "06", label: "06:00", value: 210 },
|
|
874
|
+
{ key: "12", label: "12:00", value: 1180 },
|
|
875
|
+
{ key: "18", label: "18:00", value: 640 },
|
|
876
|
+
]}
|
|
877
|
+
/>;
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
- The wedges run clockwise from 12 o'clock in the slices' order, so twelve months read like a clock
|
|
881
|
+
face. A rose suits categories that wrap, such as hours, months or compass points. A bar chart
|
|
882
|
+
compares values on their own.
|
|
883
|
+
- A wedge's radius is √(value / `max`) of the full radius, so its area follows its value. A wedge
|
|
884
|
+
worth four times another is twice as long and covers four times the area, where a radius in
|
|
885
|
+
proportion to the value would cover sixteen times. `areaRadius(value, max)` returns the share of
|
|
886
|
+
the full radius.
|
|
887
|
+
- `max` is the value of a full radius, such as a capacity. Without it the wedge of the largest value
|
|
888
|
+
shown extends to the full radius. A wedge past `max` extends to the full radius too, and a value
|
|
889
|
+
below zero renders no wedge.
|
|
890
|
+
- Every wedge takes `color`, the theme's first series color unless stated, or the palette its slice
|
|
891
|
+
states. The fill rises from 0.45 of the color at zero to the whole color at `max`, so the shade
|
|
892
|
+
repeats the radius.
|
|
893
|
+
- The chart writes the names on one circle around the rose, each centred on its wedge. Names of five
|
|
894
|
+
characters, such as 00:00, clear a room 20rem wide by 10px. `names={false}` leaves them out, and
|
|
895
|
+
the wedges fill the whole circle.
|
|
896
|
+
- The legend writes each value beside its name, because a radius is not read for a quantity.
|
|
897
|
+
`values={false}` leaves the names alone.
|
|
898
|
+
- A wedge the legend hides keeps its place with no radius, so no category moves into another's
|
|
899
|
+
place. The other wedges are measured against the largest value shown.
|
|
900
|
+
- The tooltip writes the wedge's name and value without a heading. The arrows walk the wedges
|
|
901
|
+
clockwise from 12 o'clock.
|
|
902
|
+
|
|
903
|
+
| Prop | Takes | Default |
|
|
904
|
+
| ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------- |
|
|
905
|
+
| `slices` | `{ key, label, value, color }` per category, in the cycle's order | Required |
|
|
906
|
+
| `label` | The chart's accessible name, recharts' `title` | Required |
|
|
907
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
908
|
+
| `max` | The value of a full radius | The largest value shown |
|
|
909
|
+
| `color` | A hue, a palette or a series color for every wedge | `series.1` |
|
|
910
|
+
| `names` | Whether the names render around the rose | `true` |
|
|
911
|
+
| `values` | Whether the legend writes each value beside its name | `true` |
|
|
912
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the legend's and the tooltip's values | A number in the locale |
|
|
913
|
+
| `legend` | Whether the legend renders | `true`, while slices exist |
|
|
914
|
+
| `ratio` | The plot's aspect ratio | `square` |
|
|
915
|
+
| `legendLabel`, `defaultIndex`, `animate`, `empty`, the hidden keys, `locale`, `children` | As the cartesian presets take them | |
|
|
916
|
+
|
|
917
|
+
`GaugeChart` renders one value on a dial from a range's minimum to its maximum, with the zones that
|
|
918
|
+
name what the value means:
|
|
919
|
+
|
|
920
|
+
```tsx
|
|
921
|
+
import { GaugeChart } from "@stealthscale/component-charts";
|
|
922
|
+
|
|
923
|
+
<GaugeChart
|
|
924
|
+
caption="p99 latency is 740ms, inside the 900ms error budget."
|
|
925
|
+
label="Checkout p99 latency"
|
|
926
|
+
max={1200}
|
|
927
|
+
value={740}
|
|
928
|
+
valueOptions={{ style: "unit", unit: "millisecond", unitDisplay: "narrow" }}
|
|
929
|
+
zones={[
|
|
930
|
+
{ color: "success", label: "Healthy", upTo: 600 },
|
|
931
|
+
{ color: "warning", label: "Watch", upTo: 900 },
|
|
932
|
+
{ color: "error", label: "Critical", upTo: 1200 },
|
|
933
|
+
]}
|
|
934
|
+
/>;
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
- A gauge shows where one value is in a range, such as a latency against its error budget. A bar
|
|
938
|
+
chart compares values with each other.
|
|
939
|
+
- The dial turns clockwise from `startAngle` to `endAngle`, `225` to `-45` in recharts' degrees
|
|
940
|
+
unless stated: three quarters of a turn with the gap at the bottom.
|
|
941
|
+
- The reading fills the dial from `min` in its zone's color, and the rest of the range renders as
|
|
942
|
+
the track in the neutral palette's `subtle` fill. Under forced colors the track fills with
|
|
943
|
+
`Canvas` inside a `GrayText` edge.
|
|
944
|
+
- The chart writes the value in the middle of the dial and the zone's name under it, because an arc
|
|
945
|
+
is not read for a quantity. `centerLabel` replaces the zone's name, such as with a unit.
|
|
946
|
+
- A zone takes `{ upTo, label, color }` and starts where the zone before it ends. The zones form a
|
|
947
|
+
thin ring outside the reading, each at 40% of its color over the panel, a degree apart. A zone
|
|
948
|
+
without a color takes the neutral palette.
|
|
949
|
+
- The part of the range past the last zone renders as the track in the zones' ring. A reading there,
|
|
950
|
+
or in a zone without a color, takes `color`, the theme's first series color unless stated.
|
|
951
|
+
- `gaugeBands(zones, min, max)` returns the bands the ring renders: sorted, clamped to the range,
|
|
952
|
+
and with the range's uncovered part marked `uncovered`. A zone that ends past `max`, `Infinity`
|
|
953
|
+
included, ends at `max`. `gaugeBandAt(bands, value)` returns the band a value is in, the lower one
|
|
954
|
+
on a boundary.
|
|
955
|
+
- A value past the range fills the dial to its end or leaves it empty, and the figure and the
|
|
956
|
+
meter's text write the value itself. A value that is not a finite number renders the empty state.
|
|
957
|
+
- The range's two ends are written under the dial's ends. `limits={false}` leaves them out, for a
|
|
958
|
+
gauge in a small tile.
|
|
959
|
+
- The plot is a `meter` named by `label`. Its text is the value and the zone's name, such as "740ms,
|
|
960
|
+
Watch", and includes a zone's `label` only when the label is a string. The chart has no keyboard
|
|
961
|
+
layer and no tooltip, because the meter reports its one value.
|
|
962
|
+
|
|
963
|
+
| Prop | Takes | Default |
|
|
964
|
+
| ------------------------ | -------------------------------------------------------------------------------- | ---------------------- |
|
|
965
|
+
| `value` | The value the gauge shows | Required |
|
|
966
|
+
| `max` | The value the dial ends at | Required |
|
|
967
|
+
| `label` | The meter's accessible name | Required |
|
|
968
|
+
| `min` | The value the dial starts at | `0` |
|
|
969
|
+
| `zones` | `{ upTo, label, color }` per zone, in any order | None |
|
|
970
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
971
|
+
| `centerLabel` | The words under the figure | The zone's name |
|
|
972
|
+
| `color` | A hue, a palette or a series color for a reading no zone colors | `series.1` |
|
|
973
|
+
| `limits` | Whether the range's ends are written under the dial | `true` |
|
|
974
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the figure, the range's ends and the meter's text | A number in the locale |
|
|
975
|
+
| `startAngle`, `endAngle` | The angles the dial starts and ends at, in recharts' degrees | `225`, `-45` |
|
|
976
|
+
| `animate` | Whether the dial fills in, which it never does under reduced motion | `false` |
|
|
977
|
+
| `empty` | The message in the plot's place while the value is not a finite number | `No data` |
|
|
978
|
+
| `ratio` | The plot's aspect ratio | `square` |
|
|
979
|
+
| `locale`, `children` | As the cartesian presets take them | |
|
|
980
|
+
|
|
981
|
+
## Funnel
|
|
982
|
+
|
|
983
|
+
`FunnelChart` renders the stages one set passes through, each a trapezoid as wide as its count,
|
|
984
|
+
above a table of each stage's count and both of its rates:
|
|
985
|
+
|
|
986
|
+
```tsx
|
|
987
|
+
import { FunnelChart } from "@stealthscale/component-charts";
|
|
988
|
+
|
|
989
|
+
<FunnelChart
|
|
990
|
+
caption="The biggest loss is before “Added to basket”: 18,500 people."
|
|
991
|
+
label="Checkout funnel for September"
|
|
992
|
+
stages={[
|
|
993
|
+
{ key: "visited", label: "Visited the shop", value: 48_200 },
|
|
994
|
+
{ key: "viewed", label: "Viewed a product", value: 31_400 },
|
|
995
|
+
{ key: "basket", label: "Added to basket", value: 12_900 },
|
|
996
|
+
{ key: "paid", label: "Paid", value: 5960 },
|
|
997
|
+
]}
|
|
998
|
+
/>;
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
- A funnel shows which step of a process loses people. Its stages are nested sets, each a subset of
|
|
1002
|
+
the one before it. A bar chart compares values that are not nested.
|
|
1003
|
+
- The stages run from the top in their order. Each stage narrows to the next, and the last is a
|
|
1004
|
+
rectangle. A stage's width is its count's share of the first stage's count.
|
|
1005
|
+
- Each stage's count is written on it in the ink inside a halo in the panel's color, and the pointer
|
|
1006
|
+
passes through the count to the stage. `values={false}` leaves the counts out.
|
|
1007
|
+
- The table under the plot writes each stage's count, its share of the stage before and its share of
|
|
1008
|
+
the first stage. The first stage's share of the stage before is empty. `steps={false}` leaves the
|
|
1009
|
+
table out.
|
|
1010
|
+
- The table is the collections package's `Table.Simple` at the small size, and each stage's name
|
|
1011
|
+
follows a swatch of its fill. In a room too narrow for it the table scrolls sideways, and its
|
|
1012
|
+
scroll area is then a region in the tab order named by `stepsLabel`.
|
|
1013
|
+
- A rate is written to two significant digits, such as 0.16%, 12% or 150%, because a whole percent
|
|
1014
|
+
writes a 0.3% conversion as 0%.
|
|
1015
|
+
- The stages take one palette, `color`, from the whole color at the top down to 35% of it over the
|
|
1016
|
+
panel at the bottom, because the stages are ordered.
|
|
1017
|
+
- `funnelSteps(stages)` returns each stage with its count, its two rates and the count lost before
|
|
1018
|
+
it. `biggestDrop(steps)` returns the step that loses the most people, by count rather than by
|
|
1019
|
+
rate, and `funnelWidenings(stages)` returns the stages whose count is above the stage before. A
|
|
1020
|
+
caption states the findings they return.
|
|
1021
|
+
- A stage whose count is above the stage before renders as it is, and its share of the stage before
|
|
1022
|
+
is above 100%. It means the counts come from sets that are not nested, such as two queries
|
|
1023
|
+
filtered apart.
|
|
1024
|
+
- A value that is not a finite number or is below zero counts as 0, and a stage after a stage of 0
|
|
1025
|
+
converts at 0.
|
|
1026
|
+
- The tooltip heads a stage with its name and writes its count and both rates. The arrows walk the
|
|
1027
|
+
stages from the top.
|
|
1028
|
+
- The chart has no legend, because hiding a stage would change both rates of the stage after it.
|
|
1029
|
+
- A pyramid, curved or stepped sections and names beside the stages are not offered. The table
|
|
1030
|
+
writes the names.
|
|
1031
|
+
|
|
1032
|
+
| Prop | Takes | Default |
|
|
1033
|
+
| -------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------- |
|
|
1034
|
+
| `stages` | `{ key, label, value }` per stage, from the top | Required |
|
|
1035
|
+
| `label` | The chart's accessible name, recharts' `title` | Required |
|
|
1036
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
1037
|
+
| `color` | A hue, a palette or a series color for the stages | `series.1` |
|
|
1038
|
+
| `values` | Whether each stage's count is written on it | `true` |
|
|
1039
|
+
| `steps` | Whether the table of the stages' counts and rates renders under the plot | `true` |
|
|
1040
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the counts on the stages, the table and the tooltip | A number in the locale |
|
|
1041
|
+
| `stageLabel`, `valueLabel`, `stepLabel`, `overallLabel` | The headings of the table's columns and the tooltip's rows | `Stage`, `Count`, `Of previous`, `Of first` |
|
|
1042
|
+
| `stepsLabel` | The accessible name of the table's scroll area | `Conversion by stage` |
|
|
1043
|
+
| `ratio` | The plot's aspect ratio | `video` |
|
|
1044
|
+
| `defaultIndex`, `animate`, `empty`, `locale`, `children` | As the cartesian presets take them | |
|
|
1045
|
+
|
|
1046
|
+
## Treemap
|
|
1047
|
+
|
|
1048
|
+
`TreemapChart` renders a hierarchy of parts as nested tiles whose areas follow their sizes, such as
|
|
1049
|
+
spend by team and then by service:
|
|
1050
|
+
|
|
1051
|
+
```tsx
|
|
1052
|
+
import { TreemapChart } from "@stealthscale/component-charts";
|
|
1053
|
+
|
|
1054
|
+
<TreemapChart
|
|
1055
|
+
caption="Kubernetes and Postgres are 42% of the bill."
|
|
1056
|
+
label="Cloud spend for September by team and service"
|
|
1057
|
+
nodes={[
|
|
1058
|
+
{
|
|
1059
|
+
children: [
|
|
1060
|
+
{ key: "postgres", label: "Postgres", value: 18_400 },
|
|
1061
|
+
{ key: "kafka", label: "Kafka", value: 9700 },
|
|
1062
|
+
],
|
|
1063
|
+
key: "data",
|
|
1064
|
+
label: "Data",
|
|
1065
|
+
},
|
|
1066
|
+
{
|
|
1067
|
+
children: [
|
|
1068
|
+
{ key: "kubernetes", label: "Kubernetes", value: 21_800 },
|
|
1069
|
+
{ key: "logs", label: "Log ingest", value: 7600 },
|
|
1070
|
+
],
|
|
1071
|
+
key: "platform",
|
|
1072
|
+
label: "Platform",
|
|
1073
|
+
},
|
|
1074
|
+
]}
|
|
1075
|
+
valueOptions={{ currency: "EUR", maximumFractionDigits: 0, style: "currency" }}
|
|
1076
|
+
/>;
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
- A treemap shows where the mass of many parts is, in two levels. An area is read less precisely
|
|
1080
|
+
than a length, so a bar chart compares values, and a pie or a donut shows two or three parts.
|
|
1081
|
+
- A parent's size is its children's sum, and its own `value` is ignored. A value that is not a
|
|
1082
|
+
finite number or is below zero counts as 0 and has no tile, because an area cannot be negative.
|
|
1083
|
+
- Each level runs largest first from the top left. The tiles are 2px apart, and the panel shows
|
|
1084
|
+
between them.
|
|
1085
|
+
- Each top-level node is a family of tiles in its `color`, else in the series color at its place.
|
|
1086
|
+
Its parts mix that color over the panel, from the whole color for the largest part down to 45% for
|
|
1087
|
+
the smallest.
|
|
1088
|
+
- Each tile writes its name, and under the name its value and its share of the whole, where the tile
|
|
1089
|
+
is tall enough. The tile measures each line after layout and hides a line wider than itself,
|
|
1090
|
+
because a clipped word reads as another word. `values={false}` leaves the value and the share out.
|
|
1091
|
+
- The legend lists each top-level node with its total. A node the legend hides leaves the layout,
|
|
1092
|
+
and each share is of the nodes shown.
|
|
1093
|
+
- The tooltip heads a tile with its name and writes its value and its share of the total to two
|
|
1094
|
+
significant digits.
|
|
1095
|
+
- The chart's `svg` is one tab stop named by `label`. The left and right arrows walk the tiles depth
|
|
1096
|
+
first, a family before its parts, Home and End go to the first tile and the last, and each step
|
|
1097
|
+
opens the tooltip at the tile. `defaultIndex` is a place in that walk.
|
|
1098
|
+
- `hierarchyLeaves(nodes)` returns every leaf depth first with its top-level group, its value as
|
|
1099
|
+
given and its share of the total of every tile. A ranking sorts the leaves, and a leaf with a
|
|
1100
|
+
value below zero is one the chart leaves out. A caption states the findings they return.
|
|
1101
|
+
- recharts' drill-down, `type="nest"`, is not offered, because its breadcrumbs take no key.
|
|
1102
|
+
|
|
1103
|
+
| Prop | Takes | Default |
|
|
1104
|
+
| ------------------------------------------------------- | ------------------------------------------------------------------------------------- | ---------------------- |
|
|
1105
|
+
| `nodes` | `HierarchyNode`s, `{ key, label, value, color, children }`, top level first | Required |
|
|
1106
|
+
| `label` | The accessible name of the chart's keyboard layer | Required |
|
|
1107
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
1108
|
+
| `values` | Whether each tile writes its value and share, and the legend each family's total | `true` |
|
|
1109
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the values on the tiles, in the legend and the tooltip | A number in the locale |
|
|
1110
|
+
| `valueLabel`, `shareLabel` | The names of the tooltip's rows | `Value`, `Of total` |
|
|
1111
|
+
| `legend`, `legendLabel` | Whether the legend renders, and the name of its group of buttons | `true`, `Series` |
|
|
1112
|
+
| `defaultIndex` | The place in the walk of the tile the tooltip opens at first | None |
|
|
1113
|
+
| `hiddenKeys`, `defaultHiddenKeys`, `onHiddenKeysChange` | The top-level nodes the legend hides | |
|
|
1114
|
+
| `ratio` | The plot's aspect ratio | `video` |
|
|
1115
|
+
| `animate`, `empty`, `locale`, `children` | As the cartesian presets take them | |
|
|
1116
|
+
|
|
1117
|
+
## Sunburst
|
|
1118
|
+
|
|
1119
|
+
`SunburstChart` renders a hierarchy of parts as rings around a middle, one ring per level, such as
|
|
1120
|
+
spend by team, then by service, then by resource:
|
|
1121
|
+
|
|
1122
|
+
```tsx
|
|
1123
|
+
import { SunburstChart } from "@stealthscale/component-charts";
|
|
1124
|
+
|
|
1125
|
+
<SunburstChart
|
|
1126
|
+
caption="The platform team is 65% of the bill."
|
|
1127
|
+
center="€73,500"
|
|
1128
|
+
centerLabel="September"
|
|
1129
|
+
label="Cloud spend for September by team, service and resource"
|
|
1130
|
+
nodes={[
|
|
1131
|
+
{
|
|
1132
|
+
children: [
|
|
1133
|
+
{
|
|
1134
|
+
children: [
|
|
1135
|
+
{ key: "vms", label: "Virtual machines", value: 26_000 },
|
|
1136
|
+
{ key: "gpus", label: "GPUs", value: 12_400 },
|
|
1137
|
+
],
|
|
1138
|
+
key: "compute",
|
|
1139
|
+
label: "Compute",
|
|
1140
|
+
},
|
|
1141
|
+
{ key: "network", label: "Network", value: 9300 },
|
|
1142
|
+
],
|
|
1143
|
+
key: "platform",
|
|
1144
|
+
label: "Platform",
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
children: [
|
|
1148
|
+
{ key: "web", label: "Web app", value: 14_200 },
|
|
1149
|
+
{ key: "mobile", label: "Mobile app", value: 11_600 },
|
|
1150
|
+
],
|
|
1151
|
+
key: "product",
|
|
1152
|
+
label: "Product",
|
|
1153
|
+
},
|
|
1154
|
+
]}
|
|
1155
|
+
valueOptions={{ currency: "EUR", maximumFractionDigits: 0, style: "currency" }}
|
|
1156
|
+
/>;
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
- A sunburst shows how a whole divides level by level and which parent each part belongs to. An arc
|
|
1160
|
+
is read less precisely than a length or an area, so a bar chart compares values and a treemap
|
|
1161
|
+
compares the sizes of many parts.
|
|
1162
|
+
- The rings run from the middle out, one per level. Each ring renders its nodes in their parents'
|
|
1163
|
+
order, largest first, from 12 o'clock and clockwise, so a part's arc is inside its parent's arc. A
|
|
1164
|
+
leaf that ends above the outer rings leaves a gap of its size in each ring beyond it, and a gap
|
|
1165
|
+
takes no pointer.
|
|
1166
|
+
- The rings split the room from a hole of 36% of the radius to 96% of it evenly. The hole renders
|
|
1167
|
+
`center` and `centerLabel`.
|
|
1168
|
+
- Sizes and families follow the treemap's rules. A parent's size is its children's sum, a value
|
|
1169
|
+
below zero or not finite has no arc, and each top-level node is a family in its `color`, else in
|
|
1170
|
+
the series color at its place.
|
|
1171
|
+
- Each ring is paler than the ring inside it. A part mixes its family's color over the panel by its
|
|
1172
|
+
place among its siblings, from the whole color down to 45%, times 0.82 for each level below the
|
|
1173
|
+
top: 82% to 37% in the second ring, 67% to 30% in the third.
|
|
1174
|
+
- No arc is labelled. The tooltip heads an arc with its name and writes its value and its share of
|
|
1175
|
+
the total to two significant digits. The legend lists each top-level node with its total, and a
|
|
1176
|
+
node it hides leaves the rings.
|
|
1177
|
+
- The chart's `svg` is one tab stop named by `label`. The left and right arrows walk the arcs depth
|
|
1178
|
+
first, a family before its parts, Home and End go to the first arc and the last, and each step
|
|
1179
|
+
opens the tooltip at the arc. `defaultIndex` is a place in that walk.
|
|
1180
|
+
- The chart renders a recharts `Pie` per ring inside a `PieChart`, so `children` are recharts
|
|
1181
|
+
elements of a pie chart.
|
|
1182
|
+
|
|
1183
|
+
| Prop | Takes | Default |
|
|
1184
|
+
| ------------------------------------------------------- | --------------------------------------------------------------------------- | ---------------------- |
|
|
1185
|
+
| `nodes` | `HierarchyNode`s, `{ key, label, value, color, children }`, top level first | Required |
|
|
1186
|
+
| `label` | The accessible name of the chart's keyboard layer | Required |
|
|
1187
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
1188
|
+
| `center`, `centerLabel` | The figure in the hole and its words | None |
|
|
1189
|
+
| `values` | Whether the legend writes each family's total | `true` |
|
|
1190
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the values in the legend and the tooltip | A number in the locale |
|
|
1191
|
+
| `valueLabel`, `shareLabel` | The names of the tooltip's rows | `Value`, `Of total` |
|
|
1192
|
+
| `legend`, `legendLabel` | Whether the legend renders, and the name of its group of buttons | `true`, `Series` |
|
|
1193
|
+
| `defaultIndex` | The place in the walk of the arc the tooltip opens at first | None |
|
|
1194
|
+
| `hiddenKeys`, `defaultHiddenKeys`, `onHiddenKeysChange` | The top-level nodes the legend hides | |
|
|
1195
|
+
| `ratio` | The plot's aspect ratio | `square` |
|
|
1196
|
+
| `animate`, `empty`, `locale`, `children` | As the cartesian presets take them | |
|
|
1197
|
+
|
|
1198
|
+
## Sankey
|
|
1199
|
+
|
|
1200
|
+
`SankeyChart` renders flows between the stages of a process as bands whose widths follow their
|
|
1201
|
+
values, such as a month's visitors by channel, then by what they did:
|
|
1202
|
+
|
|
1203
|
+
```tsx
|
|
1204
|
+
import { SankeyChart } from "@stealthscale/component-charts";
|
|
1205
|
+
|
|
1206
|
+
<SankeyChart
|
|
1207
|
+
caption="27% of visitors signed up."
|
|
1208
|
+
flows={[
|
|
1209
|
+
{ from: "organic", to: "signup", value: 3100 },
|
|
1210
|
+
{ from: "organic", to: "left", value: 9300 },
|
|
1211
|
+
{ from: "paid", to: "signup", value: 2000 },
|
|
1212
|
+
{ from: "paid", to: "left", value: 4800 },
|
|
1213
|
+
]}
|
|
1214
|
+
label="Visitors for September by channel and outcome"
|
|
1215
|
+
nodes={[
|
|
1216
|
+
{ key: "organic", label: "Organic search" },
|
|
1217
|
+
{ key: "paid", label: "Paid search" },
|
|
1218
|
+
{ key: "signup", label: "Signed up" },
|
|
1219
|
+
{ key: "left", label: "Left" },
|
|
1220
|
+
]}
|
|
1221
|
+
/>;
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
- A sankey shows where a quantity goes as it splits and merges between stages. A funnel is the
|
|
1225
|
+
better chart for one path.
|
|
1226
|
+
- `nodes` are `{ key, label, color }` and `flows` are `{ from, to, value }` by key, where recharts
|
|
1227
|
+
takes indexes. A flow is rendered between two known nodes with a finite value above zero, and
|
|
1228
|
+
flows between the same two nodes add up to one band. The chart leaves out a node without a
|
|
1229
|
+
rendered flow.
|
|
1230
|
+
- `sankeyCycles(flows)` returns the flows that close a loop against the flows before them. A sankey
|
|
1231
|
+
shows flow in one direction and recharts throws on a loop, so the chart leaves those flows out,
|
|
1232
|
+
and a caption states them.
|
|
1233
|
+
- `flowBalance(nodes, flows)` returns each node's inflow and outflow. A node that sends less than it
|
|
1234
|
+
receives loses flow the chart does not show, and a caption states the difference.
|
|
1235
|
+
- Each node is a bar in its `color`, else in the series color at its place, as tall as the larger of
|
|
1236
|
+
its inflow and outflow. Each flow is a band in its source's color at the backdrop opacity.
|
|
1237
|
+
recharts places each node in a column by the longest path to it, and a node that sends nothing in
|
|
1238
|
+
the last column.
|
|
1239
|
+
- Each node writes its name after its bar, or before it in the last column, and its value under the
|
|
1240
|
+
name where the bar is two lines tall (`values`). A line that would meet a line of a node earlier
|
|
1241
|
+
in `nodes`, or leave the plot, is hidden. The tooltip and the walk name every node.
|
|
1242
|
+
- The tooltip writes a node's inflow and outflow, and a flow's value and its share of what its
|
|
1243
|
+
source sends to two significant digits. The tooltip at a flow lifts it, and at a node lifts every
|
|
1244
|
+
flow of the node, while the other marks fade.
|
|
1245
|
+
- The chart's `svg` is one tab stop named by `label`. The left and right arrows walk each node in
|
|
1246
|
+
the order of `nodes`, then the flows it sends. Home and End go to the first mark and the last, and
|
|
1247
|
+
each step opens the tooltip at the mark. `defaultIndex` is a place in that walk.
|
|
1248
|
+
|
|
1249
|
+
| Prop | Takes | Default |
|
|
1250
|
+
| ----------------------------- | ----------------------------------------------------------------------------- | ---------------------- |
|
|
1251
|
+
| `nodes` | `SankeyNode`s, `{ key, label, color }`, in the walk's order | Required |
|
|
1252
|
+
| `flows` | `SankeyFlow`s, `{ from, to, value }` by key | Required |
|
|
1253
|
+
| `label` | The accessible name of the chart's keyboard layer | Required |
|
|
1254
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
1255
|
+
| `values` | Whether each node writes its value under its name | `true` |
|
|
1256
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the values beside the nodes and in the tooltip | A number in the locale |
|
|
1257
|
+
| `inflowLabel`, `outflowLabel` | The names of a node's rows in the tooltip | `In`, `Out` |
|
|
1258
|
+
| `valueLabel`, `shareLabel` | The names of a flow's rows in the tooltip | `Value`, `Of source` |
|
|
1259
|
+
| `defaultIndex` | The place in the walk of the mark the tooltip opens at first | None |
|
|
1260
|
+
| `ratio` | The plot's aspect ratio | `video` |
|
|
1261
|
+
| `empty`, `locale`, `children` | As the cartesian presets take them | |
|
|
1262
|
+
|
|
1263
|
+
## Chord diagram
|
|
1264
|
+
|
|
1265
|
+
`ChordDiagram` renders flows between peers around a circle, such as calls between services. Each
|
|
1266
|
+
node is an arc as long as what it sends, and each pair of nodes is one ribbon whose two ends are as
|
|
1267
|
+
wide as the two directions:
|
|
1268
|
+
|
|
1269
|
+
```tsx
|
|
1270
|
+
import { ChordDiagram } from "@stealthscale/component-charts";
|
|
1271
|
+
|
|
1272
|
+
<ChordDiagram
|
|
1273
|
+
caption="api sends the most calls."
|
|
1274
|
+
flows={[
|
|
1275
|
+
{ from: "api", to: "auth", value: 2600 },
|
|
1276
|
+
{ from: "auth", to: "api", value: 1900 },
|
|
1277
|
+
{ from: "api", to: "billing", value: 1400 },
|
|
1278
|
+
{ from: "billing", to: "api", value: 700 },
|
|
1279
|
+
]}
|
|
1280
|
+
label="Calls between services in the last hour"
|
|
1281
|
+
nodes={[
|
|
1282
|
+
{ key: "api", label: "api" },
|
|
1283
|
+
{ key: "auth", label: "auth" },
|
|
1284
|
+
{ key: "billing", label: "billing" },
|
|
1285
|
+
]}
|
|
1286
|
+
/>;
|
|
1287
|
+
```
|
|
1288
|
+
|
|
1289
|
+
- A chord diagram shows the flows a sankey leaves out: flows both ways between two nodes, flows
|
|
1290
|
+
around a loop, and a node's flow to itself. A sankey is the better chart for flow through stages.
|
|
1291
|
+
- `nodes` and `flows` are the sankey's `SankeyNode` and `SankeyFlow`, so the same arrays render as
|
|
1292
|
+
either chart. A flow counts between two known nodes with a finite value above zero, and flows
|
|
1293
|
+
between the same two nodes add up. Only the nodes a counted flow touches have arcs.
|
|
1294
|
+
- `chordLayout(nodes, flows, pad)` returns the arcs and the ribbons in radians from 12 o'clock,
|
|
1295
|
+
clockwise, as d3-chord's. An arc is as long as what its node sends, so a node that only receives
|
|
1296
|
+
has an arc of no length and its ribbons end in points. The arcs are 0.06 radians apart unless
|
|
1297
|
+
`pad` states another space, and the spaces take at most half the circle.
|
|
1298
|
+
- Each arc is in its node's `color`, else in the series color at its place. Each ribbon is wider at
|
|
1299
|
+
the node that sends more and takes that node's color, so its color is the pair's net direction. A
|
|
1300
|
+
ribbon rests at the backdrop opacity.
|
|
1301
|
+
- Each node writes its name outside the ring at its arc's middle. A name that would meet the name of
|
|
1302
|
+
a node earlier in `nodes`, or leave the plot's sides, is hidden. The readout and the walk name
|
|
1303
|
+
every node.
|
|
1304
|
+
- The readout in the middle of the ring is the kit's tooltip. It writes what an arc's node sends and
|
|
1305
|
+
receives, and both directions of a ribbon, including a direction of 0. The readout at a ribbon
|
|
1306
|
+
lifts it, and at an arc every ribbon of its node, while the other marks fade.
|
|
1307
|
+
- The chart's `svg` is one tab stop named by `label`. The left and right arrows walk each arc in the
|
|
1308
|
+
order of `nodes`, then the ribbons that start on it. Home and End go to the first mark and the
|
|
1309
|
+
last, and each step moves the readout to the mark. `defaultIndex` is a place in that walk.
|
|
1310
|
+
|
|
1311
|
+
| Prop | Takes | Default |
|
|
1312
|
+
| ----------------------------- | --------------------------------------------------------------------------- | ---------------------- |
|
|
1313
|
+
| `nodes` | `SankeyNode`s, `{ key, label, color }`, in the order their arcs follow | Required |
|
|
1314
|
+
| `flows` | `SankeyFlow`s, `{ from, to, value }` by key, both ways and to a node itself | Required |
|
|
1315
|
+
| `label` | The accessible name of the chart's keyboard layer | Required |
|
|
1316
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
1317
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the amounts in the readout | A number in the locale |
|
|
1318
|
+
| `inflowLabel`, `outflowLabel` | The names of an arc's rows in the readout | `In`, `Out` |
|
|
1319
|
+
| `defaultIndex` | The place in the walk of the mark the readout opens at first | None |
|
|
1320
|
+
| `ratio` | The plot's aspect ratio | `square` |
|
|
1321
|
+
| `empty`, `locale`, `children` | As the cartesian presets take them | |
|
|
1322
|
+
|
|
1323
|
+
## Heatmap
|
|
1324
|
+
|
|
1325
|
+
`Heatmap` renders a value for every pair of a row and a column as a grid of cells colored from their
|
|
1326
|
+
values, such as card authorisations per weekday and hour:
|
|
1327
|
+
|
|
1328
|
+
```tsx
|
|
1329
|
+
import { Heatmap } from "@stealthscale/component-charts";
|
|
1330
|
+
|
|
1331
|
+
<Heatmap
|
|
1332
|
+
caption="Tuesday at 17:00 is the busiest hour, with 405 authorisations."
|
|
1333
|
+
cells={[
|
|
1334
|
+
{ column: "09", row: "mon", value: 128 },
|
|
1335
|
+
{ column: "17", row: "mon", value: 342 },
|
|
1336
|
+
{ column: "09", row: "tue", value: 116 },
|
|
1337
|
+
{ column: "17", row: "tue", value: 405 },
|
|
1338
|
+
]}
|
|
1339
|
+
columns={[
|
|
1340
|
+
{ key: "09", label: "09:00" },
|
|
1341
|
+
{ key: "17", label: "17:00" },
|
|
1342
|
+
]}
|
|
1343
|
+
corner="Weekday"
|
|
1344
|
+
label="Card authorisations per hour, by weekday"
|
|
1345
|
+
rows={[
|
|
1346
|
+
{ key: "mon", label: "Mon" },
|
|
1347
|
+
{ key: "tue", label: "Tue" },
|
|
1348
|
+
]}
|
|
1349
|
+
valueLabel="Authorisations"
|
|
1350
|
+
/>;
|
|
1351
|
+
```
|
|
1352
|
+
|
|
1353
|
+
- A reader finds a dark block in a heatmap faster than the largest number in a table, and reads the
|
|
1354
|
+
exact value in the readout or printed in the cell.
|
|
1355
|
+
- The grid is an HTML `table` in the `grid` role, named by `label`. Each heading is a `th` with its
|
|
1356
|
+
scope, so a screen reader reads a cell's value with its row and its column.
|
|
1357
|
+
- `cells` are `{ row, column, value }`. Every pair of a row and a column is a cell. A pair without a
|
|
1358
|
+
reading, a `null` value and a value that is not a finite number are missing: the cell has no fill,
|
|
1359
|
+
a dashed edge and the words of `missingLabel`. Of two readings for one pair, the later applies.
|
|
1360
|
+
- `rows` and `columns` list the headings in order. An axis without them takes each key in the order
|
|
1361
|
+
the cells first name it. A reading whose row or column is not on a stated axis is left out.
|
|
1362
|
+
- `sparse` renders a pair without a reading as an empty place: no fill, no edge, no words, and the
|
|
1363
|
+
walk passes over it. A `null` value is still a missing reading. A calendar's days outside its
|
|
1364
|
+
window and the periods past a cohort's last count are empty places.
|
|
1365
|
+
- `groups` lists the headings of groups of columns, and a column states its group's key in `group`.
|
|
1366
|
+
A group's heading spans the consecutive columns that name it in a row above the column headings.
|
|
1367
|
+
Its words start at the group's first column, never widen the columns, and are hidden from sight
|
|
1368
|
+
while they are wider than the group.
|
|
1369
|
+
- A heading with `hidden` renders its words for a screen reader alone, such as a calendar's weeks.
|
|
1370
|
+
- A reading's `label` names it: the readout writes it as its heading in place of the row's and the
|
|
1371
|
+
column's words, and a screen reader reads it before the value, after the locale's list separator.
|
|
1372
|
+
A reading's `text` is what the cell prints and reads for its value, such as a head count while the
|
|
1373
|
+
fill is a rate.
|
|
1374
|
+
- `shape` renders each cell as a `block`, which widens to a printed value, or a `square` with a 2px
|
|
1375
|
+
corner, which does not print its value. Beside squares the row headings take no height of their
|
|
1376
|
+
own, so the rows are as far apart as the columns. A square keeps its side while its column's
|
|
1377
|
+
heading fits it.
|
|
1378
|
+
- The cell type is the caller's: `onSelect` receives the reading as it was passed, with any fields
|
|
1379
|
+
the caller added, such as a calendar day's `date`. A pair without a reading calls nothing.
|
|
1380
|
+
- A `sequential` scale mixes `color` over the panel from 12% at the domain's minimum to the whole
|
|
1381
|
+
color at its maximum, so the lowest value keeps a tint. A `diverging` scale mixes
|
|
1382
|
+
`colors.negative` under `midpoint` and `colors.positive` over it, from the panel at the midpoint
|
|
1383
|
+
to the whole color at the further end of the domain, with one reach on both sides. A value past
|
|
1384
|
+
the domain takes the color of its end.
|
|
1385
|
+
- The domain is the values' span unless `domain` pins it. `heatmapDomain(cells)` returns the span of
|
|
1386
|
+
the readings of two or more grids, so grids read against each other take one domain.
|
|
1387
|
+
- `values` prints each value in its cell in black or white, whichever `contrast-color()` finds
|
|
1388
|
+
contrasts more with the cell: at least 4.56:1 on every fill of the ten themes. A browser without
|
|
1389
|
+
`contrast-color()` writes the values in the ink.
|
|
1390
|
+
- The key under the grid writes `valueLabel` and the domain's ends on either side of a bar of the
|
|
1391
|
+
scale's colors, and a diverging scale's midpoint under the bar. The bar is hidden from assistive
|
|
1392
|
+
technology.
|
|
1393
|
+
- The grid is one tab stop. The arrow keys move to the next cell, Home and End to the row's ends,
|
|
1394
|
+
and Control or Command with Home or End to the grid's first and last cell. Enter, Space and a
|
|
1395
|
+
press call `onSelect` with the cell's reading.
|
|
1396
|
+
- The readout over the cell under the pointer, else over the focused cell, writes the reading's
|
|
1397
|
+
`label`, else the row's and the column's headings, and the value. Escape hides it until the
|
|
1398
|
+
pointer or focus moves.
|
|
1399
|
+
- A grid wider than its container scrolls sideways, with the row headings sticky at its start. The
|
|
1400
|
+
cell at `defaultIndex` is scrolled into the grid's view when the heatmap mounts, and the page does
|
|
1401
|
+
not move.
|
|
1402
|
+
|
|
1403
|
+
| Prop | Takes | Default |
|
|
1404
|
+
| ----------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------- |
|
|
1405
|
+
| `cells` | `HeatmapCell`s, `{ row, column, value, label, text }`, in any order | Required |
|
|
1406
|
+
| `label` | The grid's accessible name | Required |
|
|
1407
|
+
| `rows`, `columns` | `HeatmapHeading`s, `{ key, label, group, hidden }`, in order | Each key in the order the cells first name it |
|
|
1408
|
+
| `groups` | `HeatmapHeading`s of the groups of columns | None |
|
|
1409
|
+
| `sparse` | Whether a pair without a reading is an empty place | `false` |
|
|
1410
|
+
| `corner` | The words over the row headings | None |
|
|
1411
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
1412
|
+
| `scale` | `sequential` or `diverging` | `sequential` |
|
|
1413
|
+
| `color` | The color of a sequential scale | `series.1` |
|
|
1414
|
+
| `colors` | `{ negative, positive }`, the colors of a diverging scale | `orange` and `blue` |
|
|
1415
|
+
| `midpoint` | The value a diverging scale takes the panel at | `0` |
|
|
1416
|
+
| `domain` | `{ min, max }`, the values the scale spans | The values' span |
|
|
1417
|
+
| `values` | Whether each block cell prints its value | `false` |
|
|
1418
|
+
| `valueOptions` | `Intl.NumberFormatOptions` for the values | A number in the locale |
|
|
1419
|
+
| `valueLabel` | The name of the values in the key and the readout | `Value` |
|
|
1420
|
+
| `missingLabel` | The words of a cell without a value | `No data` |
|
|
1421
|
+
| `defaultIndex` | The place in `cells` of the reading the readout shows at first | None |
|
|
1422
|
+
| `onSelect` | Called with the reading of the cell Enter, Space or a press selects | None |
|
|
1423
|
+
| `shape` | `block` or `square` | `block` |
|
|
1424
|
+
| `size` | `sm`, `md` or `lg`: blocks 24, 32 or 44px tall and at least as wide, squares 10, 14 or 24px | `md` |
|
|
1425
|
+
| `ratio` | The aspect ratio of the empty state | `wide` |
|
|
1426
|
+
| `empty`, `locale` | As the cartesian presets take them | |
|
|
1427
|
+
|
|
1428
|
+
### Calendar
|
|
1429
|
+
|
|
1430
|
+
`calendarCells(days, options)` lays daily readings out as a heatmap calendar and returns the props
|
|
1431
|
+
to spread into `Heatmap`:
|
|
1432
|
+
|
|
1433
|
+
```tsx
|
|
1434
|
+
import { calendarCells, Heatmap } from "@stealthscale/component-charts";
|
|
1435
|
+
|
|
1436
|
+
<Heatmap
|
|
1437
|
+
{...calendarCells(deploys, { from: "2025-10-01", locale: "en-US", to: "2026-09-30" })}
|
|
1438
|
+
caption="The release week of 9 March was the busiest, with 80 deploys."
|
|
1439
|
+
label="Deploys per day, October 2025 to September 2026"
|
|
1440
|
+
size="sm"
|
|
1441
|
+
valueLabel="Deploys"
|
|
1442
|
+
/>;
|
|
1443
|
+
```
|
|
1444
|
+
|
|
1445
|
+
- `days` are `{ date, value }`, each date a civil date written `YYYY-MM-DD`, never an instant. The
|
|
1446
|
+
layout reads each date as a calendar date through `@internationalized/date`, so no time zone moves
|
|
1447
|
+
a day. A string that is not a date is left out, and of two readings for one date the later
|
|
1448
|
+
applies.
|
|
1449
|
+
- The window runs from `from` to `to`, the days' span unless stated, in whole weeks. A day of the
|
|
1450
|
+
window without a reading is missing, and a place of the first or last week outside the window is
|
|
1451
|
+
an empty place.
|
|
1452
|
+
- The rows are the weekdays from the locale's first day of the week, or from `weekStartsOn`, a
|
|
1453
|
+
`DayOfWeek` from `sun` to `sat`. The columns are the weeks, each with a hidden heading of its
|
|
1454
|
+
dates in the window. The groups are the months, each week in the month of its first day in the
|
|
1455
|
+
window, the first month with its year.
|
|
1456
|
+
- Each cell's `label` is its date in the locale's full words, and `onSelect` receives the cell with
|
|
1457
|
+
its `date`.
|
|
1458
|
+
- The props render square cells in a sparse grid. A calendar that calls `onSelect` takes
|
|
1459
|
+
`size="lg"`, whose 24px squares are the pointer target WCAG 2.5.8 asks for.
|
|
1460
|
+
|
|
1461
|
+
## Cohorts
|
|
1462
|
+
|
|
1463
|
+
A cohort is `{ key, label, size, retained }`: how many arrived together and how many remained at
|
|
1464
|
+
each period since, index 0 being the period they arrived in. The length of `retained` is how far the
|
|
1465
|
+
cohort is observed. A `null` inside it is a count nobody measured, and a period past its end has not
|
|
1466
|
+
happened yet.
|
|
1467
|
+
|
|
1468
|
+
`cohortCells(cohorts, options)` returns the props of a heatmap of retention by cohort:
|
|
1469
|
+
|
|
1470
|
+
```tsx
|
|
1471
|
+
import { cohortCells, Heatmap } from "@stealthscale/component-charts";
|
|
1472
|
+
|
|
1473
|
+
<Heatmap
|
|
1474
|
+
{...cohortCells(signups, {
|
|
1475
|
+
average: "All cohorts",
|
|
1476
|
+
label: (cohort) => `${cohort.label} · ${cohort.size}`,
|
|
1477
|
+
locale: "en-US",
|
|
1478
|
+
periodLabel: (period) => `M${period}`,
|
|
1479
|
+
})}
|
|
1480
|
+
corner="Signed up"
|
|
1481
|
+
label="Share of customers still active, by signup month and month since signup"
|
|
1482
|
+
values
|
|
1483
|
+
/>;
|
|
1484
|
+
```
|
|
1485
|
+
|
|
1486
|
+
- Each cohort is a row and each period a column. A cell's value is the share of the cohort that
|
|
1487
|
+
remained, on a scale from 0 to 1 written as a percentage, and a period past the cohort's last
|
|
1488
|
+
count is an empty place.
|
|
1489
|
+
- `measure: "count"` writes how many remained in each cell, and the fill is the rate either way.
|
|
1490
|
+
- `average` names a last row of the rates weighted by each cohort's size, over the cohorts with a
|
|
1491
|
+
rate at the period. `cohortAverages` returns those rates, and `retentionRate` one cohort's rate.
|
|
1492
|
+
- `onSelect` receives a cell with its `cohort`, `period` and `retained` count.
|
|
1493
|
+
|
|
1494
|
+
`retentionSeries(cohorts, options)` returns the props of a `LineChart` of the same cohorts:
|
|
1495
|
+
|
|
1496
|
+
```tsx
|
|
1497
|
+
import { LineChart, retentionSeries } from "@stealthscale/component-charts";
|
|
1498
|
+
|
|
1499
|
+
<LineChart
|
|
1500
|
+
{...retentionSeries(trials, { periodLabel: (week) => `W${week}` })}
|
|
1501
|
+
label="Share of trials still active, by week since the trial started"
|
|
1502
|
+
/>;
|
|
1503
|
+
```
|
|
1504
|
+
|
|
1505
|
+
- A row per period, a line per cohort, and the average weighted by each cohort's size. The value
|
|
1506
|
+
axis runs from 0 to 1 and writes percentages.
|
|
1507
|
+
- Every cohort's line takes `color`, `series.1` unless stated, each newer cohort mixed further
|
|
1508
|
+
towards the ink, up to 60% for the newest. The average is the ink.
|
|
1509
|
+
- The average renders solid while at least `minCohorts`, 3 unless stated, have a rate at the period,
|
|
1510
|
+
and dashed after, where it is the average of the oldest few. The solid and the dashed part share
|
|
1511
|
+
the last solid period, so the line joins. `averageLabel` and `sparseLabel` name them.
|
|
1512
|
+
- `cohortSeriesKey(key)` returns a cohort's series key, for `defaultHiddenKeys` or a
|
|
1513
|
+
`ReferenceLine`.
|
|
1514
|
+
|
|
1515
|
+
## Timeline
|
|
1516
|
+
|
|
1517
|
+
`TimelineChart` places moments on one time window across lanes, such as deploys and incidents per
|
|
1518
|
+
service:
|
|
1519
|
+
|
|
1520
|
+
```tsx
|
|
1521
|
+
import { TimelineChart } from "@stealthscale/component-charts";
|
|
1522
|
+
|
|
1523
|
+
<TimelineChart
|
|
1524
|
+
caption="The api raised three alerts between 14:03 and 14:09 UTC and rolled back at 16:20."
|
|
1525
|
+
events={moments}
|
|
1526
|
+
label="Deploys and incidents on 28 September"
|
|
1527
|
+
labelOptions={{ hour: "2-digit", hourCycle: "h23", minute: "2-digit", timeZone: "UTC" }}
|
|
1528
|
+
lanes={[
|
|
1529
|
+
{ key: "api", label: "API" },
|
|
1530
|
+
{ key: "web", label: "Web" },
|
|
1531
|
+
]}
|
|
1532
|
+
since="2026-09-28T00:00:00Z"
|
|
1533
|
+
until="2026-09-29T00:00:00Z"
|
|
1534
|
+
/>;
|
|
1535
|
+
```
|
|
1536
|
+
|
|
1537
|
+
- A moment is `{ key, at, label, lane, color }`, where `at` is a `Date`, milliseconds since the
|
|
1538
|
+
epoch or an ISO 8601 string. A moment whose instant cannot be read, or outside the window, is left
|
|
1539
|
+
out.
|
|
1540
|
+
- `lanes` lists the lanes in order with their names, else each lane in the order the moments first
|
|
1541
|
+
name it, and a moment of a lane it does not list is left out. The moments without a lane go into
|
|
1542
|
+
one lane named by `otherLabel`, after the others unless `lanes` lists its key, `OTHER_LANE`. A
|
|
1543
|
+
chart of one lane renders no lane axis.
|
|
1544
|
+
- Every lane shares one window, from the earliest moment to the latest unless `since` and `until`
|
|
1545
|
+
state it. A caller who shows part of the moments states the window, so each marker keeps its
|
|
1546
|
+
place.
|
|
1547
|
+
- Moments of a lane closer than `minGap` of the window share one marker. A moment joins the marker
|
|
1548
|
+
that opened first, measured from where that marker opened, so evenly spaced moments do not chain
|
|
1549
|
+
into one marker across the chart.
|
|
1550
|
+
- A marker is a dot of radius 6 for one moment and a pill 20px tall with the count for two or more,
|
|
1551
|
+
7px wider per digit of the count. It takes the color of its loudest moment: `error`, then
|
|
1552
|
+
`warning`, then its first moment's color, else the theme's first series color. The count is black
|
|
1553
|
+
or white, whichever `contrast-color()` finds against the marker.
|
|
1554
|
+
- The time axis writes `ticks` times evenly spaced across the window, both ends included, in
|
|
1555
|
+
`labelOptions`. recharts moves the time at each end inward until it fits, and leaves out a time
|
|
1556
|
+
between them that would meet another.
|
|
1557
|
+
- The tooltip heads a marker with its lane and its time, or the range from its first moment to its
|
|
1558
|
+
last, and lists each moment with its time: five at most, then the row `moreLabel` writes for the
|
|
1559
|
+
rest.
|
|
1560
|
+
- The plot is a row of `sizes.10` per lane over the time axis, at the chart's `rows` ratio.
|
|
1561
|
+
- The chart's `svg` is one tab stop in the `application` role. The left and right arrows, Home and
|
|
1562
|
+
End walk the markers lane by lane, and Enter and Space select the marker the walk is at.
|
|
1563
|
+
`onSelect` receives the marker's moments on a press, Enter or Space.
|
|
1564
|
+
- `layoutEvents(events, options)` returns the window and the lanes of clusters the chart plots, for
|
|
1565
|
+
a caller who writes a caption from them.
|
|
1566
|
+
- To show part of the moments, a caller filters `events` and states `since` and `until`, so the
|
|
1567
|
+
window does not move.
|
|
1568
|
+
|
|
1569
|
+
| Prop | Takes | Default |
|
|
1570
|
+
| ---------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------- |
|
|
1571
|
+
| `events` | The moments, in any order | Required |
|
|
1572
|
+
| `label` | The chart's accessible name, recharts' `title` | Required |
|
|
1573
|
+
| `caption` | The finding in words, which names the figure | None |
|
|
1574
|
+
| `lanes` | `{ key, label }` per lane, in order | Each lane the moments name |
|
|
1575
|
+
| `otherLabel` | The name of the lane of the moments without a lane | `Other` |
|
|
1576
|
+
| `since`, `until` | The window's ends: a `Date`, milliseconds or an ISO 8601 string | The earliest and the latest moment |
|
|
1577
|
+
| `minGap` | The share of the window under which two moments of a lane share a marker | `0.02` |
|
|
1578
|
+
| `ticks` | The number of times on the time axis, both ends included | `5` |
|
|
1579
|
+
| `labelOptions` | `Intl.DateTimeFormatOptions` for the ticks and the tooltip's times | The day, the month and the time |
|
|
1580
|
+
| `moreLabel` | Writes the row that counts the moments the tooltip does not list | `N more` |
|
|
1581
|
+
| `onSelect` | Called with a marker's moments on a press, Enter or Space | None |
|
|
1582
|
+
| `defaultIndex` | The place in the walk of the marker the tooltip shows on the first render | None |
|
|
1583
|
+
| `animate`, `empty`, `locale`, `children` | As the cartesian presets take them | |
|
|
1584
|
+
|
|
1585
|
+
## Sparkline and sparkbar
|
|
1586
|
+
|
|
1587
|
+
`Sparkline` and `Sparkbar` render a run of values the size of a word, without axes, a tooltip or a
|
|
1588
|
+
legend, for a table row or for the figure the run belongs to:
|
|
1589
|
+
|
|
1590
|
+
```tsx
|
|
1591
|
+
import { Sparkline } from "@stealthscale/component-charts";
|
|
1592
|
+
|
|
1593
|
+
<Sparkline values={[5980, 6420, 6110, 7030, 6890, 7560, 8130]} />;
|
|
1594
|
+
```
|
|
1595
|
+
|
|
1596
|
+
- A sparkline spans the run's own range, so it shows whether the run rises or falls, and the figure
|
|
1597
|
+
goes beside it. `area` fills under the line with the chart's gradient, which measures volume only
|
|
1598
|
+
for a run that starts at zero. `curve` is `monotone` unless stated, or `linear`.
|
|
1599
|
+
- A sparkbar measures each bar from zero. `signed` renders a value under zero in the error palette's
|
|
1600
|
+
chart color, for a change per period.
|
|
1601
|
+
- `baseline` marks a value with a dashed line in the subtle ink, and a sparkline's range widens to
|
|
1602
|
+
include it.
|
|
1603
|
+
- A value that is not a finite number keeps its place as a gap, because a zero there would plot a
|
|
1604
|
+
fall that never happened. A sparkbar renders a zero and a missing value alike, as an empty period.
|
|
1605
|
+
- `color` takes a hue or a palette, else the theme's first series color.
|
|
1606
|
+
- `label` makes the run an image named by it. Without one the run is hidden from assistive
|
|
1607
|
+
technology, for a run beside its figure. Neither takes a tab stop.
|
|
1608
|
+
- The box is a `div`, because recharts renders `div`s inside it. A spark goes in a table cell or a
|
|
1609
|
+
row of a layout, not in a paragraph.
|
|
1610
|
+
|
|
1611
|
+
| Axis | Values | Default |
|
|
1612
|
+
| --------- | ---------------------------------------------------------------------- | ------- |
|
|
1613
|
+
| `size` | `sm` 64 by 16, `md` 96 by 24, `lg` 128 by 32 pixels, times the density | `md` |
|
|
1614
|
+
| `stretch` | `true` fills the container's width at the size's height | `false` |
|
|
1615
|
+
|
|
1616
|
+
## Chart
|
|
1617
|
+
|
|
1618
|
+
`Chart.useChart` takes the rows and the series and returns the chart. `Chart.Root` provides it to
|
|
1619
|
+
the parts. Each mark reads its color, its opacity and whether the legend hides it from the chart.
|
|
1620
|
+
|
|
1621
|
+
```tsx
|
|
1622
|
+
import { Line, LineChart, Tooltip, XAxis, YAxis } from "recharts";
|
|
1623
|
+
|
|
1624
|
+
import { Chart } from "@stealthscale/component-charts";
|
|
1625
|
+
|
|
1626
|
+
const chart = Chart.useChart({
|
|
1627
|
+
data: payouts,
|
|
1628
|
+
series: [
|
|
1629
|
+
{ key: "paid", label: "Paid" },
|
|
1630
|
+
{ key: "refunded", label: "Refunded" },
|
|
1631
|
+
],
|
|
1632
|
+
});
|
|
1633
|
+
const euros = chart.formatNumber({ currency: "EUR", style: "currency" });
|
|
1634
|
+
|
|
1635
|
+
<Chart.Root chart={chart}>
|
|
1636
|
+
<Chart.Plot>
|
|
1637
|
+
<LineChart accessibilityLayer data={chart.data} title="Payouts per day">
|
|
1638
|
+
<XAxis dataKey="day" />
|
|
1639
|
+
<YAxis tickFormatter={euros} />
|
|
1640
|
+
<Tooltip content={<Chart.Tooltip formatValue={euros} />} />
|
|
1641
|
+
{chart.series.map((series) => (
|
|
1642
|
+
<Line
|
|
1643
|
+
dataKey={series.key}
|
|
1644
|
+
hide={series.hidden}
|
|
1645
|
+
key={series.key}
|
|
1646
|
+
opacity={series.opacity}
|
|
1647
|
+
stroke={series.color}
|
|
1648
|
+
/>
|
|
1649
|
+
))}
|
|
1650
|
+
</LineChart>
|
|
1651
|
+
</Chart.Plot>
|
|
1652
|
+
<Chart.Legend />
|
|
1653
|
+
<Chart.Caption>Refunds rose to €1,250 on Thursday.</Chart.Caption>
|
|
1654
|
+
</Chart.Root>;
|
|
1655
|
+
```
|
|
1656
|
+
|
|
1657
|
+
| Part | Element | What it renders |
|
|
1658
|
+
| ----------- | ------------ | ------------------------------------------------------------------------------------------------------------- |
|
|
1659
|
+
| `Root` | `figure` | The figure. It takes the chart and the ratio |
|
|
1660
|
+
| `Plot` | `div` | The box recharts sizes the chart to, while the chart has rows, and `center` and `centerLabel` over its middle |
|
|
1661
|
+
| `Empty` | `div` | Its children in the plot's place and at its ratio, while the chart has no rows |
|
|
1662
|
+
| `Legend` | `fieldset` | A button per series, which shows and hides the series |
|
|
1663
|
+
| `Key` | `ul` | The parts of the marks, for a chart read by convention, such as a box plot |
|
|
1664
|
+
| `KeyItem` | `li` | A part's glyph, the caller's `svg`, beside its name |
|
|
1665
|
+
| `Caption` | `figcaption` | The finding in words, which is the figure's accessible name |
|
|
1666
|
+
| `Tooltip` | `output` | The content of recharts' `Tooltip`: the category and each series' value, in the legend's order |
|
|
1667
|
+
| `Crosshair` | `g` | A dashed guide across the plot at the active row's value of `dataKey`, a child of a cartesian chart |
|
|
1668
|
+
|
|
1669
|
+
`Chart.Tooltip` takes `rowsOf(entries)` for a tooltip about one datum with more than one number,
|
|
1670
|
+
such as a box. The rows it returns render as `{ key, name, value }` without a swatch.
|
|
1671
|
+
`notesOf(label)` returns notes the tooltip lists after the rows, each `{ key, text, color }` with a
|
|
1672
|
+
swatch.
|
|
1673
|
+
|
|
1674
|
+
`Chart.Crosshair` is a recharts child of any cartesian chart, a preset included:
|
|
1675
|
+
`<LineChart …><Chart.Crosshair dataKey="p95" /></LineChart>`. It renders a guide in 3px dashes in
|
|
1676
|
+
the `border` ink, across the plot at the active row's value, while the tooltip shows a finite value
|
|
1677
|
+
of a series the legend shows. recharts' cursor marks the category, so the two lines cross at the
|
|
1678
|
+
point, and the arrows move both with the tooltip.
|
|
1679
|
+
|
|
1680
|
+
| Axis | Values | Default |
|
|
1681
|
+
| ------- | --------------------------------------------------------------------------------- | ------- |
|
|
1682
|
+
| `ratio` | `square`, `landscape`, `portrait`, `golden`, `video`, `wide`, `ultrawide`, `rows` | `video` |
|
|
1683
|
+
|
|
1684
|
+
recharts renders nothing in a box 0 pixels high, and a plot inside a parent without a height
|
|
1685
|
+
measures 0. The ratio gives the plot its height before recharts measures it. `rows` sizes the plot
|
|
1686
|
+
by its rows in place of its width: `sizes.10` per row of the plot's `--chart-rows`, and `sizes.12`
|
|
1687
|
+
for the axis under them.
|
|
1688
|
+
|
|
1689
|
+
The recipe sets recharts' own inks from the theme: the tick labels and a `Label`'s text in
|
|
1690
|
+
`fg.muted`, the axis lines and tick marks in `border`, the grid in `border.muted`, and the band
|
|
1691
|
+
under the pointer in `neutral.subtle`. These apply over a `fill` or `stroke` passed to an axis or a
|
|
1692
|
+
`Label`.
|
|
1693
|
+
|
|
1694
|
+
### Series and colors
|
|
1695
|
+
|
|
1696
|
+
A series takes a `key`, the field of each row it reads, a `label` for the tooltip and the legend,
|
|
1697
|
+
and an optional `color`.
|
|
1698
|
+
|
|
1699
|
+
- A series without a color takes the theme's series color at its position, `series.1` to `series.8`.
|
|
1700
|
+
The ninth series takes the first again.
|
|
1701
|
+
- Each theme takes its series colors from its own colors first, so a chart's first series wear the
|
|
1702
|
+
brand. The first series is blue in ink, red in cinder and green in pine, and neon's first three
|
|
1703
|
+
are violet, pink and yellow.
|
|
1704
|
+
- `color` names one of the theme's hue palettes, or one of its eight semantic palettes for a series
|
|
1705
|
+
with a meaning, such as an `error` threshold. The chart renders the palette's `chart` role, the
|
|
1706
|
+
lightest color of the palette's hue that keeps 3:1 against the page and the panel.
|
|
1707
|
+
- `color` may also be one of the series colors, `series.1` to `series.8`, which a series keeps at
|
|
1708
|
+
any position, such as the same series in two charts.
|
|
1709
|
+
- Each theme's contrast check measures every `chart` role and every series color at 3:1 or more
|
|
1710
|
+
against the page and the panel.
|
|
1711
|
+
- `Chart.colorOf(color)` returns a palette's chart color for a recharts child, and
|
|
1712
|
+
`chart.color(key)` a series' color.
|
|
1713
|
+
- `ink` mixes a series' color towards the ink, `fg`, by a share in percent, so series of one color
|
|
1714
|
+
read in an order of strength. A chart color mixed towards the ink measured 3:1 or more against the
|
|
1715
|
+
page and the panel at every share, on the 27 chart colors of the ten themes in both modes.
|
|
1716
|
+
|
|
1717
|
+
A pie's sectors are series: each row's name is a series key, and the caller sets each sector's
|
|
1718
|
+
`fill` from `chart.color`. A `Pie` takes `rootTabIndex={-1}`, because recharts makes the pie's group
|
|
1719
|
+
a tab stop without a name beside the keyboard layer.
|
|
1720
|
+
|
|
1721
|
+
### The legend
|
|
1722
|
+
|
|
1723
|
+
The legend renders a button per series outside the plot, so switching a series does not resize the
|
|
1724
|
+
chart.
|
|
1725
|
+
|
|
1726
|
+
- A plain press switches the pressed series and never hides the last one shown.
|
|
1727
|
+
- A press with Ctrl or Cmd shows the pressed series alone, and a second one shows every series
|
|
1728
|
+
again.
|
|
1729
|
+
- The pointer or focus on a button fades every other series' marks to the plot's `--chart-faded`,
|
|
1730
|
+
the theme's `opacity.backdrop`, over the fast duration. A mark fades through its `opacity` prop,
|
|
1731
|
+
from `series.opacity` or `chart.opacity(key)`.
|
|
1732
|
+
- A hidden series' name is struck through and its swatch faded. Each swatch is the data package's
|
|
1733
|
+
`ColorSwatch`.
|
|
1734
|
+
- `hiddenKeys` and `onHiddenKeysChange` control the hidden series, and `defaultHiddenKeys` sets them
|
|
1735
|
+
for the first render.
|
|
1736
|
+
|
|
1737
|
+
### Formatting
|
|
1738
|
+
|
|
1739
|
+
`chart.formatNumber(options)` and `chart.formatDate(options)` take `Intl`'s own options and return
|
|
1740
|
+
functions for recharts' `tickFormatter` and the tooltip's `formatValue` and `formatLabel`. Both
|
|
1741
|
+
format in the chart's `locale` when it is set. Without it they use the locale of the nearest
|
|
1742
|
+
`LocaleProvider`, and without a provider the runtime's. `chart.locale` returns that locale for
|
|
1743
|
+
another `Intl` format. The tooltip formats values as numbers in that locale unless `formatValue` is
|
|
1744
|
+
given, and a band's two ends as a range.
|
|
1745
|
+
|
|
1746
|
+
### Accessibility
|
|
1747
|
+
|
|
1748
|
+
- The caption is the figure's accessible name. A screen reader reads nothing from a plot's paths, so
|
|
1749
|
+
the caption states the finding: "Refunds doubled after Tuesday's release", not "Line chart of
|
|
1750
|
+
refunds". The root points `aria-labelledby` at the caption while one renders, because Chromium
|
|
1751
|
+
computes a `figure`'s name from its `figcaption` only through `aria-labelledby`.
|
|
1752
|
+
- recharts' keyboard layer, `accessibilityLayer`, is on by default. It makes the chart one tab stop
|
|
1753
|
+
in the `application` role, whose left and right arrows move the tooltip through the categories.
|
|
1754
|
+
The `title` prop gives the chart its accessible name. VoiceOver users turn QuickNav off to use the
|
|
1755
|
+
arrows.
|
|
1756
|
+
- The treemap, the sunburst, the sankey, the chord diagram and the timeline walk their marks
|
|
1757
|
+
themselves. recharts gives a treemap no keyboard layer, the arrows of a pie chart's layer move
|
|
1758
|
+
through its first pie alone, a sankey's layer ignores every key, and the marks of a chord diagram
|
|
1759
|
+
and a timeline are not recharts' own. Each chart's `svg` is the one tab stop in the `application`
|
|
1760
|
+
role. The left and right arrows, Home and End send the pointer's events to the next mark, and the
|
|
1761
|
+
tooltip opens there as it does under a pointer. Enter and Space press the mark the walk is at,
|
|
1762
|
+
which selects a timeline's marker.
|
|
1763
|
+
- A chord diagram's readout is the same tooltip in the middle of the ring. Its panel is transparent
|
|
1764
|
+
while it is empty and remains in the accessibility tree. A screen reader announces the first step
|
|
1765
|
+
of the walk from it.
|
|
1766
|
+
- A heatmap is an HTML table in the `grid` role, where the other charts are an `svg` in the
|
|
1767
|
+
`application` role. Its cells are `gridcell`s with one tab stop between them, and a screen reader
|
|
1768
|
+
reads each cell's value with its row and column headings. A calendar's cell reads its date first:
|
|
1769
|
+
"Sunday, October 5, 2025, 0" in Chromium. The readout over a cell is hidden from assistive
|
|
1770
|
+
technology, because the focused cell reads the same words.
|
|
1771
|
+
- The tooltip is an `output`, whose `status` role reads its whole text on a change. It is
|
|
1772
|
+
`aria-live="assertive"` while the keyboard layer is on, as recharts' own tooltip is, so a screen
|
|
1773
|
+
reader reads each point the arrows move to. In a pie the arrows move it from slice to slice.
|
|
1774
|
+
- The figure in a donut's or a sunburst's hole is hidden from assistive technology, because the
|
|
1775
|
+
caption states the finding.
|
|
1776
|
+
- The legend's buttons report `aria-pressed`, and each swatch is hidden from assistive technology.
|
|
1777
|
+
- Under forced colors neither Firefox nor Chromium replaces an SVG `fill` or `stroke`, so the series
|
|
1778
|
+
keep their colors. The tick labels, a `Label`'s text and the axis lines take `CanvasText`, the
|
|
1779
|
+
grid `GrayText`, and each swatch keeps its color inside a `CanvasText` edge. A quadrant chart's
|
|
1780
|
+
lines, its names and the words beside its points take `CanvasText`, and a timeline's markers keep
|
|
1781
|
+
their colors inside a `Canvas` edge. An annotation's words take `CanvasText` inside a `Canvas`
|
|
1782
|
+
halo. The crosshair takes `CanvasText`. A heatmap's cells keep their fills, a missing cell's
|
|
1783
|
+
dashed edge takes `GrayText`, and the lit headings take `Highlight`.
|
|
1784
|
+
- A preset animates its marks only with `animate`. In the kit, recharts animates marks unless the
|
|
1785
|
+
caller passes `isAnimationActive={false}`. recharts turns the animation off under reduced motion
|
|
1786
|
+
either way.
|
|
1787
|
+
|
|
1788
|
+
### Not offered
|
|
1789
|
+
|
|
1790
|
+
- A data table behind the chart. The caption states the finding.
|
|
1791
|
+
- recharts' own `Legend`, which renders inside the plot and takes room from it.
|
|
1792
|
+
- Value labels on the cartesian presets' marks, and charts that share a tooltip through `syncId`.
|
|
1793
|
+
- recharts' `Brush`. Its two travellers are sliders valued in pixels, with no minimum, no maximum
|
|
1794
|
+
and one name for both. A range `Slider` zooms a preset instead.
|
|
1795
|
+
- A press on an annotation. A caption links the release or the incident an annotation marks.
|
|
1796
|
+
|
|
1797
|
+
## Types
|
|
1798
|
+
|
|
1799
|
+
| Type | Describes |
|
|
1800
|
+
| ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
1801
|
+
| `CartesianProps` | The props every preset takes |
|
|
1802
|
+
| `CartesianSeries` | A preset's series: the kit's series, `dashed` and `previousOf` |
|
|
1803
|
+
| `Annotation` | One mark on the categories: its key, words, color, category, period's end or value |
|
|
1804
|
+
| `AlignOptions` | How `alignPeriods` merges two periods: the category field, the copied fields and their suffix |
|
|
1805
|
+
| `BarSeries` | A bar chart's series: a preset's series with the field its `target` reads |
|
|
1806
|
+
| `BulletProps` | The props a bar chart adds: `zones` and `targetLabel` |
|
|
1807
|
+
| `ComboSeries` | A combo chart's series: a preset's series with its `mark` and `axis` |
|
|
1808
|
+
| `Mark` | The mark a series renders as: `area`, `bar` or `line` |
|
|
1809
|
+
| `ValueAxis` | The value axis a series reads: `start` or `end` |
|
|
1810
|
+
| `RangeBand` | A range chart's band: its key, label, color and its `low` and `high` field |
|
|
1811
|
+
| `Curve` | The path of a line between two points, `linear` or `monotone` |
|
|
1812
|
+
| `LineChartProps`, `AreaChartProps`, `StackedAreaChartProps` | A preset's props with its `curve`, and `percent` for stacked area |
|
|
1813
|
+
| `StepLineChartProps`, `StackedBarChartProps`, `PercentStackedBarProps` | A preset's props, which are `CartesianProps` |
|
|
1814
|
+
| `BarChartProps`, `HorizontalBarChartProps` | A bar chart's props: `CartesianProps` with `BarSeries` and `BulletProps` |
|
|
1815
|
+
| `ComboChartProps`, `RangeChartProps`, `ParetoChartProps`, `StreamGraphProps` | A preset's props with the props it adds |
|
|
1816
|
+
| `ParetoShares` | The `share` and `cumulative` share `paretoRows` adds to a row |
|
|
1817
|
+
| `BurndownChartProps`, `BurndownPoint`, `BurndownRow`, `BurndownOptions` | A burndown's props, one period, one row it plots, and its plan |
|
|
1818
|
+
| `WaterfallChartProps`, `WaterfallStep`, `WaterfallBar` | A waterfall's props, one step, and one bar it renders |
|
|
1819
|
+
| `ScatterPlotProps`, `ScatterSeries`, `BubbleChartProps` | A scatter's props, one series with its points, and a bubble's props |
|
|
1820
|
+
| `Quadrants`, `QuadrantId`, `Division` | A quadrant chart's names and where it divides, one quadrant's key, and the values it divides at |
|
|
1821
|
+
| `SpreadOptions`, `Span` | How `spreadPoints` spreads the points, and the two ends of an axis' span |
|
|
1822
|
+
| `TimelineChartProps`, `TimelineLane`, `TimelineEvent` | A timeline's props, one lane with its name, and one moment |
|
|
1823
|
+
| `EventLayout`, `EventLane`, `EventCluster`, `LayoutOptions` | The window and lanes `layoutEvents` returns, one lane, one marker's moments, and its options |
|
|
1824
|
+
| `RegressionOverlayProps`, `LinearFit` | A trend line's points, fields and series, and the fit `linearRegression` returns |
|
|
1825
|
+
| `HistogramChartProps`, `ChartBin`, `BinOptions` | A histogram's props, one bin with its count, and how `binValues` bins |
|
|
1826
|
+
| `DistributionChartProps`, `DistributionSeries` | A distribution chart's props, and one series with its values |
|
|
1827
|
+
| `BoxPlotProps`, `BoxGroup`, `BoxSummary` | A box plot's props, one group, and the summary `boxStats` returns |
|
|
1828
|
+
| `ViolinPlotProps`, `ViolinGroup`, `DensityPoint`, `DensityOptions` | A violin plot's props, one group, one point of a density, and how `kernelDensity` estimates it |
|
|
1829
|
+
| `CandlestickChartProps`, `Candle`, `CandleDirection`, `CandleChange` | A candlestick chart's props, one period's prices, its direction and its change |
|
|
1830
|
+
| `PieSlice` | One slice: its key, label, value and color |
|
|
1831
|
+
| `PolarProps`, `PieChartProps` | The props a pie takes |
|
|
1832
|
+
| `RadarChartProps`, `RadarSeries` | A radar chart's props, and one series with whether its polygon fills |
|
|
1833
|
+
| `RadialBarChartProps`, `RadialBarDatum` | A radial bar chart's props, and one bar with its key, label, value and color |
|
|
1834
|
+
| `PolarAreaChartProps` | A rose's props: the slices of a cycle, `max`, `color`, `names` and `values` |
|
|
1835
|
+
| `GaugeChartProps`, `GaugeZone`, `GaugeBand` | A gauge's props, one zone with where it ends, and one band `gaugeBands` returns |
|
|
1836
|
+
| `FunnelChartProps`, `FunnelStage`, `FunnelStep` | A funnel's props, one stage with its count, and one step `funnelSteps` returns |
|
|
1837
|
+
| `HierarchyProps`, `TreemapChartProps`, `HierarchyNode`, `HierarchyLeaf` | A hierarchy chart's props, a treemap's, one node, and one leaf `hierarchyLeaves` returns |
|
|
1838
|
+
| `SunburstChartProps` | A hierarchy chart's props with `center` and `centerLabel` |
|
|
1839
|
+
| `SankeyChartProps`, `SankeyNode`, `SankeyFlow`, `SankeyBalance` | A sankey's props, one node, one flow, and one node's balance `flowBalance` returns |
|
|
1840
|
+
| `ChordDiagramProps`, `ChordLayout`, `ChordSpan`, `ChordRibbon` | A chord diagram's props, and the arcs and ribbons `chordLayout` returns as spans of the circle |
|
|
1841
|
+
| `HeatmapProps`, `HeatmapCell`, `HeatmapHeading` | A heatmap's props, one reading, and the heading of a row, a column or a group of columns |
|
|
1842
|
+
| `HeatmapDomain`, `HeatmapScale`, `HeatmapColors`, `HeatShape`, `HeatSize` | A heat scale's span, how it reads a value, a diverging scale's colors, a cell's shape and size |
|
|
1843
|
+
| `Valued` | A reading `heatmapDomain` takes: anything with a `value` |
|
|
1844
|
+
| `CalendarDay`, `CalendarCell`, `CalendarOptions`, `Calendar` | One day's reading, one day as a cell, a calendar's window and words, and the props it returns |
|
|
1845
|
+
| `Cohort`, `CohortCell`, `CohortOptions`, `CohortGrid` | One intake, one cell of a cohort grid, the grid's words, and the props `cohortCells` returns |
|
|
1846
|
+
| `Retention`, `RetentionOptions`, `RetentionRow` | The props `retentionSeries` returns, its words and colors, and one row per period |
|
|
1847
|
+
| `DonutChartProps` | A pie's props with `center` and `centerLabel` |
|
|
1848
|
+
| `SparklineProps`, `SparkbarProps` | A spark's run, its color and shape, and the box's props |
|
|
1849
|
+
| `Chart.ChartOptions` | What `useChart` takes: the rows, the series, the hidden keys |
|
|
1850
|
+
| `Chart.ChartApi` | What `useChart` returns and `Chart.Root` provides |
|
|
1851
|
+
| `Chart.SeriesOptions` | A series the caller states |
|
|
1852
|
+
| `Chart.Series` | A series as the chart resolved it: key, label, color and opacity |
|
|
1853
|
+
| `Chart.ChartColor` | A hue palette, a semantic palette or a series color of the theme |
|
|
1854
|
+
| `Chart.TooltipEntry` | One value recharts passes the tooltip |
|
|
1855
|
+
| `Chart.TooltipRow` | One row `rowsOf` returns: its key, name and value |
|
|
1856
|
+
| `Chart.TooltipNote` | One note `notesOf` returns: its key, words and color |
|
|
1857
|
+
|
|
1858
|
+
Each part's props are `Chart.<Part>Props`.
|
|
1859
|
+
|
|
1860
|
+
## Licence
|
|
1861
|
+
|
|
1862
|
+
MIT. See [LICENSE](LICENSE).
|