@pushwoosh/kit-charts 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/ai-documentation.md +378 -0
- package/core/Chart/Axes.d.ts +19 -0
- package/core/Chart/Axes.js +204 -0
- package/core/Chart/Chart.d.ts +33 -0
- package/core/Chart/Chart.js +616 -0
- package/core/Chart/Chart.styles.d.ts +109 -0
- package/core/Chart/Chart.styles.js +227 -0
- package/core/Chart/ChartErrorBoundary.d.ts +15 -0
- package/core/Chart/ChartErrorBoundary.js +27 -0
- package/core/Chart/Hinted.d.ts +11 -0
- package/core/Chart/Hinted.js +63 -0
- package/core/Chart/Legend.d.ts +15 -0
- package/core/Chart/Legend.js +92 -0
- package/core/Chart/PlotDecorations.d.ts +17 -0
- package/core/Chart/PlotDecorations.js +172 -0
- package/core/Chart/StackLabels.d.ts +6 -0
- package/core/Chart/StackLabels.js +85 -0
- package/core/Chart/Titles.d.ts +6 -0
- package/core/Chart/Titles.js +67 -0
- package/core/Chart/Tooltip.d.ts +13 -0
- package/core/Chart/Tooltip.js +87 -0
- package/core/Chart/useSize.d.ts +5 -0
- package/core/Chart/useSize.js +45 -0
- package/core/animation/motion.d.ts +20 -0
- package/core/animation/motion.js +20 -0
- package/core/animation/useTweenedNumbers.d.ts +1 -0
- package/core/animation/useTweenedNumbers.js +51 -0
- package/core/data/points.d.ts +14 -0
- package/core/data/points.js +73 -0
- package/core/format/time.d.ts +2 -0
- package/core/format/time.js +38 -0
- package/core/geometry/path.d.ts +19 -0
- package/core/geometry/path.js +76 -0
- package/core/layout/legend-layout.d.ts +21 -0
- package/core/layout/legend-layout.js +103 -0
- package/core/layout/measure-text.d.ts +9 -0
- package/core/layout/measure-text.js +57 -0
- package/core/layout/metrics.d.ts +63 -0
- package/core/layout/metrics.js +66 -0
- package/core/layout/point-width.d.ts +11 -0
- package/core/layout/point-width.js +86 -0
- package/core/layout/title-layout.d.ts +30 -0
- package/core/layout/title-layout.js +99 -0
- package/core/model/build-model.d.ts +69 -0
- package/core/model/build-model.js +556 -0
- package/core/model/palette.d.ts +15 -0
- package/core/model/palette.js +17 -0
- package/core/options/normalize.d.ts +8 -0
- package/core/options/normalize.js +44 -0
- package/core/registry/registry.d.ts +5 -0
- package/core/registry/registry.js +32 -0
- package/core/scale/band.d.ts +10 -0
- package/core/scale/band.js +33 -0
- package/core/scale/color.d.ts +9 -0
- package/core/scale/color.js +69 -0
- package/core/scale/linear.d.ts +17 -0
- package/core/scale/linear.js +64 -0
- package/core/scale/time.d.ts +19 -0
- package/core/scale/time.js +187 -0
- package/core/stack/stack.d.ts +9 -0
- package/core/stack/stack.js +76 -0
- package/core/types/formatter.d.ts +59 -0
- package/core/types/formatter.js +1 -0
- package/core/types/geometry.d.ts +16 -0
- package/core/types/geometry.js +1 -0
- package/core/types/options.d.ts +331 -0
- package/core/types/options.js +2 -0
- package/core/types/renderer.d.ts +101 -0
- package/core/types/renderer.js +1 -0
- package/core/types/scale.d.ts +34 -0
- package/core/types/scale.js +1 -0
- package/index.d.ts +16 -0
- package/index.js +10 -0
- package/package.json +64 -0
- package/presets/AreaChart.d.ts +3 -0
- package/presets/AreaChart.js +13 -0
- package/presets/ColumnChart.d.ts +3 -0
- package/presets/ColumnChart.js +13 -0
- package/presets/LineChart.d.ts +3 -0
- package/presets/LineChart.js +13 -0
- package/presets/PieChart.d.ts +3 -0
- package/presets/PieChart.js +13 -0
- package/presets/StackedColumnChart.d.ts +7 -0
- package/presets/StackedColumnChart.js +31 -0
- package/presets/index.d.ts +5 -0
- package/presets/index.js +5 -0
- package/series/area/area.d.ts +2 -0
- package/series/area/area.js +57 -0
- package/series/area/index.d.ts +1 -0
- package/series/area/index.js +1 -0
- package/series/column/column.d.ts +2 -0
- package/series/column/column.js +163 -0
- package/series/column/index.d.ts +1 -0
- package/series/column/index.js +1 -0
- package/series/heatmap/heatmap.d.ts +2 -0
- package/series/heatmap/heatmap.js +172 -0
- package/series/heatmap/index.d.ts +1 -0
- package/series/heatmap/index.js +1 -0
- package/series/line/index.d.ts +1 -0
- package/series/line/index.js +1 -0
- package/series/line/line.d.ts +2 -0
- package/series/line/line.js +49 -0
- package/series/pie/index.d.ts +1 -0
- package/series/pie/index.js +1 -0
- package/series/pie/pie.d.ts +2 -0
- package/series/pie/pie.js +224 -0
- package/series/shared/DataLabels.d.ts +14 -0
- package/series/shared/DataLabels.js +124 -0
- package/series/shared/Markers.d.ts +9 -0
- package/series/shared/Markers.js +34 -0
- package/series/shared/color.d.ts +1 -0
- package/series/shared/color.js +19 -0
- package/series/shared/domain.d.ts +3 -0
- package/series/shared/domain.js +14 -0
- package/series/shared/hit.d.ts +3 -0
- package/series/shared/hit.js +33 -0
- package/series/shared/legend.d.ts +3 -0
- package/series/shared/legend.js +12 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pushwoosh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# @pushwoosh/kit-charts - AI Agent Documentation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
React chart library rendering to SVG, with a Highcharts-shaped configuration
|
|
6
|
+
model.
|
|
7
|
+
|
|
8
|
+
**Current state:** all five v1 renderers are implemented — `line`, `column`,
|
|
9
|
+
`area`, `pie`, `heatmap` — with `normal` and `percent` stacking, grouped columns,
|
|
10
|
+
donuts via `innerSize`, data labels and stack labels. A series type with no
|
|
11
|
+
registered renderer is skipped: the chart draws its other series and logs which
|
|
12
|
+
module is missing.
|
|
13
|
+
|
|
14
|
+
`heatmap` takes `[x, row, value]` points, colours each cell through
|
|
15
|
+
`colorAxis` (`min`/`max` plus `stops`), and takes its rows from
|
|
16
|
+
`yAxis.categories`. A second y axis with `linkedTo` mirrors the first one's
|
|
17
|
+
geometry while showing its own labels. The colour axis itself is not drawn as a
|
|
18
|
+
legend yet, so a heatmap contributes no legend items.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @pushwoosh/kit-charts
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Core Dependencies
|
|
27
|
+
|
|
28
|
+
- **react**: >= 18.0.0 (peer dependency)
|
|
29
|
+
- **styled-components**: >= 5.3.3 (peer dependency)
|
|
30
|
+
- **@pushwoosh/kit-constants**: runtime dependency — fonts, greys and tooltip
|
|
31
|
+
colours come from the design system tokens. The series palette is built in.
|
|
32
|
+
|
|
33
|
+
Scales, tick selection and time formatting are hand-written rather than taken
|
|
34
|
+
from d3, because size is the point of the library.
|
|
35
|
+
|
|
36
|
+
## Public API
|
|
37
|
+
|
|
38
|
+
- `Chart` — the general component; takes `options`, optional `renderers`,
|
|
39
|
+
`isLoading`, `empty`, `loading`, `className`, `containerProps`.
|
|
40
|
+
- `LineChart`, `ColumnChart`, `AreaChart`, `PieChart` — presets that pin one renderer each.
|
|
41
|
+
- `StackedColumnChart` — `ColumnChart` with `stacking` on by default; takes a
|
|
42
|
+
`stacking` prop (`'normal'` | `'percent'`), and an explicit
|
|
43
|
+
`plotOptions.column.stacking` still wins.
|
|
44
|
+
- `line`, `column`, `area`, `pie`, `heatmap` — the renderers themselves, for
|
|
45
|
+
`Chart`'s `renderers` prop or for `registerSeries`.
|
|
46
|
+
- `registerSeries(...renderers)` / `resolveRenderers(local?)` / `getRenderer(map, type)`.
|
|
47
|
+
- `formatTime(pattern, epochMs)` — strftime-style, UTC.
|
|
48
|
+
- `DEFAULT_COLORS` and the other palette constants.
|
|
49
|
+
- All configuration and contract types.
|
|
50
|
+
|
|
51
|
+
## Configuration Model
|
|
52
|
+
|
|
53
|
+
`chart`, `title`, `subtitle`, `xAxis`, `yAxis`, `colorAxis`, `legend`, `tooltip`,
|
|
54
|
+
`plotOptions`, `series`, `colors`, `meta`, `numberFormat`.
|
|
55
|
+
|
|
56
|
+
Three-level inheritance is preserved: `plotOptions.series` → `plotOptions[type]` →
|
|
57
|
+
the series' own options, later winning. Nested objects merge rather than replace.
|
|
58
|
+
|
|
59
|
+
Deliberate differences from Highcharts:
|
|
60
|
+
|
|
61
|
+
1. `meta` replaces `custom`, typed via
|
|
62
|
+
`ChartOptions<TChartMeta, TSeriesMeta, TPointMeta>`.
|
|
63
|
+
2. Formatters take a typed context **argument**; there is no `this`.
|
|
64
|
+
3. Formatters return a `ReactNode`, never an HTML string. There is no `useHTML`.
|
|
65
|
+
4. `TooltipPoint` exposes `prev` and `next` neighbours directly.
|
|
66
|
+
5. Formatters see `ctx.chart.title` and `ctx.chart.meta`, not the option tree.
|
|
67
|
+
6. No `credits` option.
|
|
68
|
+
7. No `reflow`; resize is observed automatically.
|
|
69
|
+
8. `chart.margin*` replaces the computed distance from that edge to the plot,
|
|
70
|
+
the same as Highcharts, rather than adding to it.
|
|
71
|
+
9. `tooltip` defaults to `shape: 'callout'`: a dark bubble with a beak anchored
|
|
72
|
+
to the point, matching the product hint style. `shape: 'box'` or
|
|
73
|
+
`followPointer` gives the floating-near-the-cursor behaviour instead.
|
|
74
|
+
10. A y axis with `categories` becomes a band axis. `reversed` puts the first
|
|
75
|
+
category on top; `linkedTo` takes the target axis' geometry and keeps its own
|
|
76
|
+
labels, which is how one grid carries two label columns.
|
|
77
|
+
11. `min`/`max` rounding (`nice`) is automatic and per end, not a user-facing
|
|
78
|
+
option: an end left unset is rounded outward to a whole tick step; an end the
|
|
79
|
+
caller set explicitly is used as-is. `yAxis: { min: 0 }` still gets a rounded,
|
|
80
|
+
label-friendly top end instead of losing headroom to a raw data max.
|
|
81
|
+
12. `states.inactive.opacity` is off unless set. Highcharts dims the series a
|
|
82
|
+
hover is not on by default; switching that on here would restyle every chart
|
|
83
|
+
already drawn.
|
|
84
|
+
|
|
85
|
+
`numberFormat` is the chart-wide number-to-text rule. Axis labels, data labels,
|
|
86
|
+
stack labels and the default tooltip all print through it, so a thousands
|
|
87
|
+
separator is configured once rather than in four callbacks. Anything with its
|
|
88
|
+
own `formatter` or `format` wins over it.
|
|
89
|
+
|
|
90
|
+
`allowDecimals: false` keeps every tick on an axis whole, as a count axis needs:
|
|
91
|
+
the step is chosen from the candidates that stay integral at its magnitude, and
|
|
92
|
+
never falls below 1.
|
|
93
|
+
|
|
94
|
+
`labels.allowOverlap` defaults to false on both axes and on data labels. A label
|
|
95
|
+
whose box would run into one already placed loses its text — Highcharts hides
|
|
96
|
+
the label and keeps the tick, and so do we. A data-label formatter returning an
|
|
97
|
+
element cannot be measured, so it is always drawn.
|
|
98
|
+
|
|
99
|
+
## Plot bands and lines
|
|
100
|
+
|
|
101
|
+
`plotLines` and `plotBands` live on either axis. A band takes `from`/`to`, a line
|
|
102
|
+
takes `value`; on a category axis both address a category by name and a band
|
|
103
|
+
covers the whole slot. Both accept a `label`, and a `zIndex` of 5 or more draws
|
|
104
|
+
them over the series instead of under it — the value Highcharts users reach for.
|
|
105
|
+
|
|
106
|
+
## Interaction
|
|
107
|
+
|
|
108
|
+
`onPointClick(point, event)` reports the point nearest a click inside the plot,
|
|
109
|
+
with its `meta`. Drill-down hangs off this; there is no per-point `events` object.
|
|
110
|
+
|
|
111
|
+
`chart.zoomType: 'x'` lets a drag across the plot narrow the x axis to what was
|
|
112
|
+
dragged over, with a reset button in the corner (`chart.resetZoomText` renames
|
|
113
|
+
it). The zoom is applied as a `min`/`max` override on the first x axis, so the
|
|
114
|
+
model is built from the options as usual. Only a continuous axis zooms, and a
|
|
115
|
+
drag shorter than a few pixels is treated as a click.
|
|
116
|
+
|
|
117
|
+
A horizontally scrolling plot (`chart.scrollablePlotArea.minWidth`) lifts its
|
|
118
|
+
left-hand y axes into a fixed layer, so the plot slides under the row labels
|
|
119
|
+
rather than taking them with it. A right-hand axis and a vertically scrolling
|
|
120
|
+
chart stay in the scrolling canvas.
|
|
121
|
+
|
|
122
|
+
Time values are epoch milliseconds and are formatted in UTC, so output does not
|
|
123
|
+
shift with the viewer's timezone.
|
|
124
|
+
|
|
125
|
+
## Legend
|
|
126
|
+
|
|
127
|
+
`legendSymbol` on a series overrides the shape its item keys with — a line series
|
|
128
|
+
draws a dash by default, and a dot reads better when the legend is a plain key
|
|
129
|
+
above the plot rather than a swatch column beside it.
|
|
130
|
+
|
|
131
|
+
`layout` (`'horizontal' | 'vertical'`, default `'horizontal'`) chooses one row of
|
|
132
|
+
items or one item per row. `verticalAlign` (`'top' | 'middle' | 'bottom'`,
|
|
133
|
+
default `'bottom'`) picks which edge the legend takes: `'top'`/`'bottom'` reserve
|
|
134
|
+
height above/below the plot; `'middle'` has no plot edge to sit on, so it
|
|
135
|
+
reserves width and sits beside the plot instead — the same trade Highcharts
|
|
136
|
+
makes. `align` (`'left' | 'center' | 'right'`, default `'left'`) positions items
|
|
137
|
+
along whichever axis the legend occupies — the row when it's below/above, the
|
|
138
|
+
column when it's at the side. `x`/`y` shift the computed position by an offset.
|
|
139
|
+
|
|
140
|
+
Geometry lives in one module, `src/core/layout/legend-layout.ts`
|
|
141
|
+
(`measureLegend` reserves the space, `layoutLegend` places the items), so
|
|
142
|
+
`Chart` and the legend component never disagree on where it sits. A heatmap
|
|
143
|
+
draws no legend items — see Overview.
|
|
144
|
+
|
|
145
|
+
## Data Shapes
|
|
146
|
+
|
|
147
|
+
A series accepts `number`, `null`, `[x, y]`, `[category, y]`, `[x, y, value]`, or
|
|
148
|
+
`{ x, y, value, name, color, meta }`. A bare number takes its x from the array
|
|
149
|
+
index. `null` is a gap; `NaN` and `Infinity` become gaps with a development
|
|
150
|
+
warning, never zero.
|
|
151
|
+
|
|
152
|
+
## Extension Point
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
interface SeriesRenderer<TSeries extends SeriesOptionsBase> {
|
|
156
|
+
readonly type: string;
|
|
157
|
+
readonly scales: { x: ScaleKind; y: ScaleKind };
|
|
158
|
+
extendDomain(series: TSeries, acc: DomainAcc): DomainAcc;
|
|
159
|
+
render(ctx: RenderContext<TSeries>): ReactNode;
|
|
160
|
+
hitTest(ctx: RenderContext<TSeries>, pointer: Point): PointHit[] | null;
|
|
161
|
+
legendItems(series: TSeries, seriesIndex: number, colors: readonly string[]): LegendItem[];
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`RenderContext` carries already-normalised `points`, the plot rectangle, the
|
|
166
|
+
scales, the resolved colour, hover state and stack values.
|
|
167
|
+
`extendDomain` is where a renderer widens an axis beyond its data — columns and
|
|
168
|
+
areas need the zero baseline included, a heatmap needs half a cell of slack at
|
|
169
|
+
both ends of x. `scales.y` may be a band scale (a categorical axis): a renderer
|
|
170
|
+
that needs numbers on y takes them through `continuousY(ctx)` and draws nothing
|
|
171
|
+
when the axis is categorical. `scales.color` is present when the options declare
|
|
172
|
+
a `colorAxis`.
|
|
173
|
+
|
|
174
|
+
Register before use:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { registerSeries } from '@pushwoosh/kit-charts';
|
|
178
|
+
import { line } from '@pushwoosh/kit-charts/series/line';
|
|
179
|
+
|
|
180
|
+
registerSeries(line);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`registerSeries` is idempotent. Registering a *different* renderer under an
|
|
184
|
+
occupied type throws in development and overwrites in production. `Chart` also
|
|
185
|
+
accepts a `renderers` prop that takes priority over the global registry.
|
|
186
|
+
|
|
187
|
+
**Invariant:** `src/core` must never import from `src/series`. An eslint rule
|
|
188
|
+
enforces it. Breaking it puts every renderer into every consumer's bundle.
|
|
189
|
+
|
|
190
|
+
## Error Handling
|
|
191
|
+
|
|
192
|
+
`Chart` wraps its body in an error boundary placed *outside* the component that
|
|
193
|
+
builds the model — a formatter throwing during that build would otherwise escape
|
|
194
|
+
and unmount the surrounding tree. A failed chart shows a fallback in its own box.
|
|
195
|
+
|
|
196
|
+
An unregistered series type is skipped with a development-time console error; the
|
|
197
|
+
rest of the chart still draws.
|
|
198
|
+
|
|
199
|
+
## Build and configuration
|
|
200
|
+
|
|
201
|
+
The package is a *library* on `@pushwoosh/frontend-builder-engine`: `lib:build`
|
|
202
|
+
(tsc, then the engine's own babel pass for styled-components) and `lib:check-types`.
|
|
203
|
+
It therefore carries none of the application-template configuration — no
|
|
204
|
+
`babel.config.js` (the engine's babel pass runs with `configFile: false` and never
|
|
205
|
+
reads one), no `.browserslistrc` and no `.stylelintrc`. Only what something
|
|
206
|
+
actually reads is here: `tsconfig.json`, `playground/tsconfig.json`,
|
|
207
|
+
`eslint.config.mjs` (the engine's config plus this project's two rules),
|
|
208
|
+
`pushwoosh.config.js`, and `jest.config.js`.
|
|
209
|
+
|
|
210
|
+
`jest.config.js` is hand-written because the engine has no test runner. The
|
|
211
|
+
`playground` script calls webpack directly for the same reason in reverse: the
|
|
212
|
+
engine's `lib:command:playground` currently only prints the command it would run —
|
|
213
|
+
its spawn is commented out upstream — so deferring to it would start nothing.
|
|
214
|
+
|
|
215
|
+
## Testing
|
|
216
|
+
|
|
217
|
+
`tests/fixtures/` is the golden corpus: every configuration the library must
|
|
218
|
+
render, named by capability. `renderableCases(implementedTypes)` filters it to
|
|
219
|
+
what the current renderers can draw, so tests do not fail on unwritten types.
|
|
220
|
+
|
|
221
|
+
Tests live outside `src/` so the build cannot ship them.
|
|
222
|
+
|
|
223
|
+
## Legend
|
|
224
|
+
|
|
225
|
+
Legend items are buttons: clicking one hides or shows what it stands for, and the
|
|
226
|
+
chart rescales to what is left. Usually that is a series, but a renderer whose
|
|
227
|
+
legend keys *points* — a pie, one item per slice — sets `pointIndex` on its items,
|
|
228
|
+
and then a click takes out that slice alone and the rest share the circle out
|
|
229
|
+
again. The point stays in `ctx.points` with its index listed in
|
|
230
|
+
`ctx.hiddenPoints`, so the renderer can animate it away: a pie gives it no angle,
|
|
231
|
+
which closes it like a fan while the slices after it slide into the room it
|
|
232
|
+
leaves — the path keeps its command structure throughout, which is what lets the
|
|
233
|
+
browser interpolate it. Nothing counts a hidden point: not a total, not an axis
|
|
234
|
+
extent, not hit testing. Hidden series keep their legend entry — dimmed and
|
|
235
|
+
struck through — and stay mounted, which is what lets the change animate instead
|
|
236
|
+
of popping. How a series leaves is the renderer's call through `exit`: with
|
|
237
|
+
`'collapse'` the renderer redraws its shapes flat on their own baseline when
|
|
238
|
+
`ctx.hidden` is set — a column shrinks into the segment it grew from, so a stacked
|
|
239
|
+
one does not slide through its neighbour — and the fade waits for that to play.
|
|
240
|
+
`'fade'` is the default, for shapes with no baseline to fall onto: lines, areas,
|
|
241
|
+
pie slices, heatmap cells; they are excluded from the domains, the stacks, the
|
|
242
|
+
column slots and hit testing. Keyboard users get the same through Enter/Space and
|
|
243
|
+
a focus ring; `prefers-reduced-motion` turns the transitions off.
|
|
244
|
+
|
|
245
|
+
Switching off the *last* series would otherwise reshape the axes under it, and the
|
|
246
|
+
line would jump to the new scale before it finished fading — so with nothing on
|
|
247
|
+
show the chart holds the geometry of the last state that had something, and only
|
|
248
|
+
the opacity changes. Switching a series back on rebuilds from the data as usual.
|
|
249
|
+
|
|
250
|
+
`Chart` owns that state, seeded from `series[].visible` and reset when the series
|
|
251
|
+
list changes. `toggleSeries={false}` makes the legend a plain key again, and
|
|
252
|
+
`onSeriesToggle(seriesIndex, visible)` reports every click for consumers that
|
|
253
|
+
persist the choice.
|
|
254
|
+
|
|
255
|
+
## Title
|
|
256
|
+
|
|
257
|
+
`title.text` and `subtitle.text` are drawn at the top of the chart — 18px semibold
|
|
258
|
+
and 12px muted by default, aligned by `align`, and the layout reserves their height.
|
|
259
|
+
|
|
260
|
+
`title.hint` adds an info disc after the title text: hovering or focusing it opens
|
|
261
|
+
the same dark callout a point uses, with the text wrapped. Escape closes it. Use it
|
|
262
|
+
for what the chart counts — the definition a reader needs and a subtitle has no room
|
|
263
|
+
for. `xAxis.labels.hint(ctx)` puts the same callout on a tick label, for a step
|
|
264
|
+
name that means nothing to a reader who has not been told.
|
|
265
|
+
|
|
266
|
+
`title.caption` is a muted note on the far end of the title's own row — the period
|
|
267
|
+
the figures cover, when they were counted. Only the main title draws one.
|
|
268
|
+
|
|
269
|
+
## Animation
|
|
270
|
+
|
|
271
|
+
`series.animation` sets one series' own transition length — `{ duration: 500 }`,
|
|
272
|
+
or `false` for none. Everything left alone runs on the chart clock below.
|
|
273
|
+
|
|
274
|
+
Two mechanisms, chosen by the shape being moved.
|
|
275
|
+
|
|
276
|
+
**CSS transitions** carry shapes whose geometry interpolates correctly component by
|
|
277
|
+
component: the vertices of a line or an area, and the `x`/`y`/`width`/`height` of a
|
|
278
|
+
column. `SeriesLayer` in `Chart.styles.ts` owns those.
|
|
279
|
+
|
|
280
|
+
**A frame-by-frame tween** (`useTweenedNumbers`) carries shapes that are *generated*
|
|
281
|
+
from numbers, where blending the drawing instructions is not the same as blending
|
|
282
|
+
the shape. A pie is the case in point: interpolating an arc's endpoints drags them
|
|
283
|
+
along straight lines while the radius stays put, so the arc bulges, and the
|
|
284
|
+
large-arc flag flips halfway through into a visible jump. The pie therefore tweens
|
|
285
|
+
its angles and radius and regenerates the arc every frame — the same approach as
|
|
286
|
+
D3's `arcTween`, which interpolates `startAngle`/`endAngle` and re-runs the arc
|
|
287
|
+
generator, and as Highcharts, whose animator tweens the `start`, `end` and `r`
|
|
288
|
+
attributes of its arc symbol. A renderer that does this sets `animatesGeometry`, and
|
|
289
|
+
the chart then keeps CSS off its shapes so the two do not fight.
|
|
290
|
+
|
|
291
|
+
Both read their duration and easing from `src/core/animation/motion.ts`, so one
|
|
292
|
+
click moves everything at one speed: `MOTION_MS` with `MOTION_EASING`, the CSS
|
|
293
|
+
spelling of the `easeOutCubic` the tween uses. Hover feedback — the hint badge —
|
|
294
|
+
is the one exception at `HOVER_MS`, because an affordance should answer faster
|
|
295
|
+
than the chart does. `prefers-reduced-motion: reduce` switches both mechanisms
|
|
296
|
+
off.
|
|
297
|
+
|
|
298
|
+
## Tooltip
|
|
299
|
+
|
|
300
|
+
`tooltip.table` lays the body out as a table instead: a header line, a heading
|
|
301
|
+
row, and one row per series with its swatch. Each column takes a `cell(point)`
|
|
302
|
+
returning a `ReactNode`. Use it when a point carries more than one figure — a
|
|
303
|
+
count and its share — and a single line would run them together. A tabular
|
|
304
|
+
tooltip defaults to a light card, because the dark hint chrome reads badly at
|
|
305
|
+
that size.
|
|
306
|
+
|
|
307
|
+
A column `title` is a string or a function of the hover, and a function that
|
|
308
|
+
returns `null` drops its column for that hover. That is what a column comparing
|
|
309
|
+
against the previous point needs: it names the date it compares with, and on the
|
|
310
|
+
first point there is no such date and no column.
|
|
311
|
+
|
|
312
|
+
Default shape is `callout`: a dark bubble with a beak anchored to the point,
|
|
313
|
+
flipping under the point when there is no room above and sliding sideways to stay
|
|
314
|
+
on screen while the beak keeps pointing at the point. `shape: 'box'` or
|
|
315
|
+
`followPointer: true` gives a box that trails the cursor instead.
|
|
316
|
+
|
|
317
|
+
The default body shows the x (dates through `dateFormat`), a swatch per series and
|
|
318
|
+
the value wrapped in `valuePrefix`/`valueSuffix`. `formatter` replaces the body and
|
|
319
|
+
returns a `ReactNode`; `ctx.points[].prev`/`.next` carry the neighbouring points, so
|
|
320
|
+
a delta needs no extra lookup. `shared: true` collects every series at the hovered x.
|
|
321
|
+
|
|
322
|
+
Golden cases: `tooltip/callout`, `tooltip/shared-formatter`.
|
|
323
|
+
|
|
324
|
+
## Styling
|
|
325
|
+
|
|
326
|
+
The library ships no inline styles: everything visual is either an SVG
|
|
327
|
+
presentation attribute or a styled-component in `*.styles.ts`. Every layout
|
|
328
|
+
distance — label gaps, axis gutters, legend rows, tooltip padding — is a named
|
|
329
|
+
constant in `src/core/layout/metrics.ts`, not a number typed at the point of use;
|
|
330
|
+
that is what keeps the chrome consistent across components. Fonts, greys and
|
|
331
|
+
tooltip colours come from `@pushwoosh/kit-constants`, so charts match the product
|
|
332
|
+
without the consumer configuring anything.
|
|
333
|
+
|
|
334
|
+
## Columns
|
|
335
|
+
|
|
336
|
+
`borderPosition: 'top'` strokes only the edge the value reaches — the top of a
|
|
337
|
+
bar counting up, the bottom of one counting down — instead of boxing the bar.
|
|
338
|
+
With `fillOpacity` on the body and `states.hover.fillOpacity` to deepen it, that
|
|
339
|
+
is the funnel look: the series keeps its real colour, so the legend swatch and
|
|
340
|
+
the tooltip dot stay readable, and only the bar is washed out.
|
|
341
|
+
|
|
342
|
+
A data label whose text carries `\n` is drawn as stacked lines, styled by
|
|
343
|
+
`style` for the first and `secondaryStyle` for the rest. It is one label: it
|
|
344
|
+
moves, measures and hides as one.
|
|
345
|
+
|
|
346
|
+
## The device pixel grid
|
|
347
|
+
|
|
348
|
+
`useSize` floors the measured box, so the canvas is always a whole number of
|
|
349
|
+
pixels and never wider than the box that holds it. Browser zoom sizes a box in
|
|
350
|
+
fractions of a css pixel; storing the fraction left the canvas at its old width
|
|
351
|
+
whenever the box moved by less than half a pixel, and the container's
|
|
352
|
+
`overflow: hidden` then shaved the right edge.
|
|
353
|
+
|
|
354
|
+
`crisp(position, strokeWidth)` in `layout/metrics.ts` puts a stroke where the
|
|
355
|
+
device can draw it in one row: an odd stroke on a half pixel, an even one on a
|
|
356
|
+
whole. Grid lines, the x axis line and its ticks, and plot lines all go through
|
|
357
|
+
it. Without it a 1px line centred on a whole pixel is split across the two rows
|
|
358
|
+
either side and reads as two grey ones, and which lines land where changes at
|
|
359
|
+
every zoom step — the chart appears to change weight as the page zooms. Series
|
|
360
|
+
geometry is deliberately *not* snapped: it carries the data positions the tooltip
|
|
361
|
+
and hit-testing share.
|
|
362
|
+
|
|
363
|
+
Both rules are Highcharts', arithmetic included, and both work in css pixels:
|
|
364
|
+
neither library reads `devicePixelRatio` (Highcharts 12.6 does not mention it
|
|
365
|
+
once). So the snapping is exact at whole device ratios, and at the rest — 125%,
|
|
366
|
+
or any zoom out, where a 1px css line is thinner than a device pixel and grey
|
|
367
|
+
wherever it sits — it buys consistency rather than sharpness. What it does buy at
|
|
368
|
+
every ratio is that the geometry never changes: measured across zoom 50%–150%,
|
|
369
|
+
the canvas stays 688 in a 688.5 box and the grid stays on the same half pixels,
|
|
370
|
+
which is exactly what Highcharts emits under the same sweep.
|
|
371
|
+
|
|
372
|
+
## Size
|
|
373
|
+
|
|
374
|
+
One runtime dependency (`@pushwoosh/kit-constants`, for design-system tokens);
|
|
375
|
+
SVG output; renderers are only bundled when imported, which is what the
|
|
376
|
+
`src/core` import restriction protects. There is no automated size gate —
|
|
377
|
+
adding a dependency or letting the core reach into `src/series` will not be
|
|
378
|
+
caught by CI.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
2
|
+
import { type AxisModel } from '../model/build-model';
|
|
3
|
+
import { type Rect } from '../types/geometry';
|
|
4
|
+
interface GridProps {
|
|
5
|
+
plot: Rect;
|
|
6
|
+
yAxis: AxisModel | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare function Grid({ plot, yAxis }: GridProps): ReactElement | null;
|
|
9
|
+
interface XAxisProps {
|
|
10
|
+
plot: Rect;
|
|
11
|
+
axis: AxisModel;
|
|
12
|
+
}
|
|
13
|
+
export declare function XAxis({ plot, axis }: XAxisProps): ReactElement | null;
|
|
14
|
+
interface YAxisProps {
|
|
15
|
+
plot: Rect;
|
|
16
|
+
axis: AxisModel;
|
|
17
|
+
}
|
|
18
|
+
export declare function YAxis({ plot, axis }: YAxisProps): ReactElement | null;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Hinted } from './Hinted';
|
|
3
|
+
import { DEFAULT_FONT_SIZE, measureText } from '../layout/measure-text';
|
|
4
|
+
import { AXIS_LABEL_GAP, AXIS_TITLE_GAP, crisp, TEXT_BASELINE_SHIFT, X_AXIS_LABEL_DROP, X_AXIS_TITLE_GAP } from '../layout/metrics';
|
|
5
|
+
import { DEFAULT_AXIS_COLOR, DEFAULT_GRID_COLOR, DEFAULT_MUTED_TEXT_COLOR } from '../model/palette';
|
|
6
|
+
const textProps = style => ({
|
|
7
|
+
fill: (style === null || style === void 0 ? void 0 : style.color) ?? DEFAULT_MUTED_TEXT_COLOR,
|
|
8
|
+
fontFamily: style === null || style === void 0 ? void 0 : style.fontFamily,
|
|
9
|
+
fontSize: (style === null || style === void 0 ? void 0 : style.fontSize) ?? DEFAULT_FONT_SIZE,
|
|
10
|
+
fontWeight: style === null || style === void 0 ? void 0 : style.fontWeight
|
|
11
|
+
});
|
|
12
|
+
const ANCHOR = {
|
|
13
|
+
left: 'start',
|
|
14
|
+
center: 'middle',
|
|
15
|
+
right: 'end'
|
|
16
|
+
};
|
|
17
|
+
/** A tick label, wrapped in its explanation when the axis gave it one. */
|
|
18
|
+
function TickLabel({
|
|
19
|
+
hint,
|
|
20
|
+
x,
|
|
21
|
+
y,
|
|
22
|
+
anchor,
|
|
23
|
+
style,
|
|
24
|
+
children
|
|
25
|
+
}) {
|
|
26
|
+
const text = _jsx("text", {
|
|
27
|
+
x: x,
|
|
28
|
+
y: y,
|
|
29
|
+
textAnchor: anchor,
|
|
30
|
+
...textProps(style),
|
|
31
|
+
children: children
|
|
32
|
+
});
|
|
33
|
+
return hint ? _jsx(Hinted, {
|
|
34
|
+
text: hint,
|
|
35
|
+
children: text
|
|
36
|
+
}) : text;
|
|
37
|
+
}
|
|
38
|
+
export function Grid({
|
|
39
|
+
plot,
|
|
40
|
+
yAxis
|
|
41
|
+
}) {
|
|
42
|
+
// A hidden axis takes its grid with it: `visible: false` is how a chart that
|
|
43
|
+
// labels its bars instead of its axis — a funnel — gets a bare plot.
|
|
44
|
+
if (!yAxis || yAxis.options.visible === false || yAxis.options.gridLineWidth === 0) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const color = yAxis.options.gridLineColor ?? DEFAULT_GRID_COLOR;
|
|
48
|
+
const width = yAxis.options.gridLineWidth ?? 1;
|
|
49
|
+
const {
|
|
50
|
+
scale
|
|
51
|
+
} = yAxis;
|
|
52
|
+
// A categorical axis draws its grid on the row boundaries rather than through
|
|
53
|
+
// the middle of a row, so that a row sits inside one cell of the grid.
|
|
54
|
+
const positions = scale.kind === 'band' ? [...scale.domain().map(value => scale.map(value)), plot.y + plot.height] : yAxis.ticks.map(tick => tick.position);
|
|
55
|
+
return _jsx("g", {
|
|
56
|
+
"aria-hidden": true,
|
|
57
|
+
children: positions.map(position => _jsx("line", {
|
|
58
|
+
x1: plot.x,
|
|
59
|
+
x2: plot.x + plot.width,
|
|
60
|
+
y1: crisp(position, width),
|
|
61
|
+
y2: crisp(position, width),
|
|
62
|
+
stroke: color,
|
|
63
|
+
strokeWidth: width
|
|
64
|
+
}, `grid-${position}`))
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function AxisTitle({
|
|
68
|
+
title,
|
|
69
|
+
x,
|
|
70
|
+
y,
|
|
71
|
+
anchor,
|
|
72
|
+
defaultRotation
|
|
73
|
+
}) {
|
|
74
|
+
var _title$style;
|
|
75
|
+
if (!(title !== null && title !== void 0 && title.text) || ((_title$style = title.style) === null || _title$style === void 0 ? void 0 : _title$style.display) === 'none') {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const rotation = title.rotation ?? defaultRotation;
|
|
79
|
+
const at = {
|
|
80
|
+
x: x + (title.x ?? 0),
|
|
81
|
+
y: y + (title.y ?? 0)
|
|
82
|
+
};
|
|
83
|
+
return _jsx("text", {
|
|
84
|
+
x: at.x,
|
|
85
|
+
y: at.y,
|
|
86
|
+
textAnchor: rotation === 0 ? anchor : 'middle',
|
|
87
|
+
transform: rotation === 0 ? undefined : `rotate(${rotation} ${at.x} ${at.y})`,
|
|
88
|
+
...textProps(title.style),
|
|
89
|
+
children: title.text
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const titleY = (options, plot) => {
|
|
93
|
+
var _options$title;
|
|
94
|
+
const align = ((_options$title = options.title) === null || _options$title === void 0 ? void 0 : _options$title.align) ?? 'middle';
|
|
95
|
+
if (align === 'high') {
|
|
96
|
+
return plot.y;
|
|
97
|
+
}
|
|
98
|
+
if (align === 'low') {
|
|
99
|
+
return plot.y + plot.height;
|
|
100
|
+
}
|
|
101
|
+
return plot.y + plot.height / 2;
|
|
102
|
+
};
|
|
103
|
+
export function XAxis({
|
|
104
|
+
plot,
|
|
105
|
+
axis
|
|
106
|
+
}) {
|
|
107
|
+
var _options$labels, _options$labels2, _options$title2;
|
|
108
|
+
const {
|
|
109
|
+
options
|
|
110
|
+
} = axis;
|
|
111
|
+
if (options.visible === false) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const onTop = options.opposite === true;
|
|
115
|
+
const offset = options.offset ?? 0;
|
|
116
|
+
const direction = onTop ? -1 : 1;
|
|
117
|
+
const baseline = onTop ? plot.y - offset : plot.y + plot.height + offset;
|
|
118
|
+
const lineColor = options.lineColor ?? DEFAULT_AXIS_COLOR;
|
|
119
|
+
const tickLength = options.tickLength ?? 0;
|
|
120
|
+
const tickWidth = options.tickWidth ?? 1;
|
|
121
|
+
const labelsEnabled = ((_options$labels = options.labels) === null || _options$labels === void 0 ? void 0 : _options$labels.enabled) !== false;
|
|
122
|
+
const labelY = baseline + direction * (tickLength + (onTop ? AXIS_LABEL_GAP : X_AXIS_LABEL_DROP)) + (((_options$labels2 = options.labels) === null || _options$labels2 === void 0 ? void 0 : _options$labels2.y) ?? 0);
|
|
123
|
+
return _jsxs("g", {
|
|
124
|
+
children: [options.lineWidth === 0 ? null : _jsx("line", {
|
|
125
|
+
x1: plot.x,
|
|
126
|
+
x2: plot.x + plot.width,
|
|
127
|
+
y1: crisp(baseline, options.lineWidth ?? 1),
|
|
128
|
+
y2: crisp(baseline, options.lineWidth ?? 1),
|
|
129
|
+
stroke: lineColor,
|
|
130
|
+
strokeWidth: options.lineWidth ?? 1
|
|
131
|
+
}), axis.ticks.map(tick => {
|
|
132
|
+
var _options$labels3, _options$labels4;
|
|
133
|
+
return _jsxs("g", {
|
|
134
|
+
children: [tickLength > 0 && tickWidth > 0 ? _jsx("line", {
|
|
135
|
+
x1: crisp(tick.position, tickWidth),
|
|
136
|
+
x2: crisp(tick.position, tickWidth),
|
|
137
|
+
y1: crisp(baseline, tickWidth),
|
|
138
|
+
y2: crisp(baseline + direction * tickLength, tickWidth),
|
|
139
|
+
stroke: options.tickColor ?? lineColor,
|
|
140
|
+
strokeWidth: tickWidth
|
|
141
|
+
}) : null, labelsEnabled && tick.label ? _jsx(TickLabel, {
|
|
142
|
+
hint: tick.hint,
|
|
143
|
+
x: tick.position + (((_options$labels3 = options.labels) === null || _options$labels3 === void 0 ? void 0 : _options$labels3.x) ?? 0),
|
|
144
|
+
y: labelY,
|
|
145
|
+
anchor: tick.anchor,
|
|
146
|
+
style: (_options$labels4 = options.labels) === null || _options$labels4 === void 0 ? void 0 : _options$labels4.style,
|
|
147
|
+
children: tick.label
|
|
148
|
+
}) : null]
|
|
149
|
+
}, `x-${tick.value}`);
|
|
150
|
+
}), _jsx(AxisTitle, {
|
|
151
|
+
title: options.title,
|
|
152
|
+
x: plot.x + plot.width / 2,
|
|
153
|
+
y: labelY + direction * ((((_options$title2 = options.title) === null || _options$title2 === void 0 ? void 0 : _options$title2.offset) ?? 0) + X_AXIS_TITLE_GAP),
|
|
154
|
+
anchor: "middle",
|
|
155
|
+
defaultRotation: 0
|
|
156
|
+
})]
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
export function YAxis({
|
|
160
|
+
plot,
|
|
161
|
+
axis
|
|
162
|
+
}) {
|
|
163
|
+
var _options$labels5, _options$labels6, _options$title3, _options$title4;
|
|
164
|
+
const {
|
|
165
|
+
options
|
|
166
|
+
} = axis;
|
|
167
|
+
if (options.visible === false) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
const labelsEnabled = ((_options$labels5 = options.labels) === null || _options$labels5 === void 0 ? void 0 : _options$labels5.enabled) !== false;
|
|
171
|
+
const onRight = options.opposite === true;
|
|
172
|
+
const offset = options.offset ?? 0;
|
|
173
|
+
// `offset` pushes the whole axis — labels and title — away from the plot.
|
|
174
|
+
const edge = onRight ? plot.x + plot.width + AXIS_LABEL_GAP + offset : plot.x - AXIS_LABEL_GAP - offset;
|
|
175
|
+
const anchor = (((_options$labels6 = options.labels) === null || _options$labels6 === void 0 ? void 0 : _options$labels6.align) && ANCHOR[options.labels.align]) ?? (onRight ? 'start' : 'end');
|
|
176
|
+
const columnWidth = labelsEnabled ? axis.ticks.reduce((widest, tick) => {
|
|
177
|
+
var _options$labels7;
|
|
178
|
+
return Math.max(widest, measureText(tick.label, (_options$labels7 = options.labels) === null || _options$labels7 === void 0 ? void 0 : _options$labels7.style));
|
|
179
|
+
}, 0) : 0;
|
|
180
|
+
// Where the labels grow away from the plot, the title has to clear the whole
|
|
181
|
+
// column of them or it lands on top of the numbers.
|
|
182
|
+
const outwards = onRight ? anchor !== 'end' : anchor !== 'start';
|
|
183
|
+
const titleShift = outwards ? Math.ceil(columnWidth * (anchor === 'middle' ? 0.5 : 1)) + AXIS_TITLE_GAP : 0;
|
|
184
|
+
const titleX = onRight ? edge + titleShift + (((_options$title3 = options.title) === null || _options$title3 === void 0 ? void 0 : _options$title3.offset) ?? 0) : edge - titleShift - (((_options$title4 = options.title) === null || _options$title4 === void 0 ? void 0 : _options$title4.offset) ?? 0);
|
|
185
|
+
return _jsxs("g", {
|
|
186
|
+
children: [labelsEnabled ? axis.ticks.map(tick => {
|
|
187
|
+
var _options$labels8, _options$labels9;
|
|
188
|
+
return tick.label ? _jsx(TickLabel, {
|
|
189
|
+
x: edge + (((_options$labels8 = options.labels) === null || _options$labels8 === void 0 ? void 0 : _options$labels8.x) ?? 0),
|
|
190
|
+
y: tick.position + TEXT_BASELINE_SHIFT,
|
|
191
|
+
anchor: anchor,
|
|
192
|
+
hint: tick.hint,
|
|
193
|
+
style: (_options$labels9 = options.labels) === null || _options$labels9 === void 0 ? void 0 : _options$labels9.style,
|
|
194
|
+
children: tick.label
|
|
195
|
+
}, `y-${tick.value}`) : null;
|
|
196
|
+
}) : null, _jsx(AxisTitle, {
|
|
197
|
+
title: options.title,
|
|
198
|
+
x: titleX,
|
|
199
|
+
y: titleY(options, plot),
|
|
200
|
+
anchor: anchor,
|
|
201
|
+
defaultRotation: onRight ? 90 : -90
|
|
202
|
+
})]
|
|
203
|
+
});
|
|
204
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type HTMLAttributes, type MouseEvent as ReactMouseEvent, type ReactElement, type ReactNode } from 'react';
|
|
2
|
+
import { type ChartOptions } from '../types/options';
|
|
3
|
+
import { type AnySeriesRenderer } from '../types/renderer';
|
|
4
|
+
/** The point a click landed on, as handed to `onPointClick`. */
|
|
5
|
+
export interface ClickedPoint<TPointMeta = unknown> {
|
|
6
|
+
seriesIndex: number;
|
|
7
|
+
seriesName: string;
|
|
8
|
+
pointIndex: number;
|
|
9
|
+
x: number | string;
|
|
10
|
+
y: number | null;
|
|
11
|
+
value: number | null;
|
|
12
|
+
color: string;
|
|
13
|
+
meta: TPointMeta;
|
|
14
|
+
}
|
|
15
|
+
export interface ChartProps<TChartMeta = unknown, TSeriesMeta = unknown> {
|
|
16
|
+
options: ChartOptions<TChartMeta, TSeriesMeta>;
|
|
17
|
+
renderers?: readonly AnySeriesRenderer[];
|
|
18
|
+
isLoading?: boolean;
|
|
19
|
+
empty?: ReactNode;
|
|
20
|
+
loading?: ReactNode;
|
|
21
|
+
className?: string;
|
|
22
|
+
containerProps?: HTMLAttributes<HTMLDivElement>;
|
|
23
|
+
/** Set false to make the legend a plain key again. */
|
|
24
|
+
toggleSeries?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Called after a legend click with the visibility it lands on. `pointIndex` is
|
|
27
|
+
* set when the item stood for a single point, as a pie's items do.
|
|
28
|
+
*/
|
|
29
|
+
onSeriesToggle?: (seriesIndex: number, visible: boolean, pointIndex?: number) => void;
|
|
30
|
+
/** Called with the point nearest a click inside the plot; drill-down hangs off this. */
|
|
31
|
+
onPointClick?: (point: ClickedPoint, event: ReactMouseEvent<SVGSVGElement>) => void;
|
|
32
|
+
}
|
|
33
|
+
export declare function Chart<TChartMeta = unknown, TSeriesMeta = unknown>(props: ChartProps<TChartMeta, TSeriesMeta>): ReactElement;
|