@internetstiftelsen/charts 0.9.2 → 0.10.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/README.md +137 -3
- package/dist/area.d.ts +2 -0
- package/dist/area.js +39 -31
- package/dist/bar.d.ts +20 -1
- package/dist/bar.js +395 -519
- package/dist/base-chart.d.ts +21 -1
- package/dist/base-chart.js +166 -93
- package/dist/chart-group.d.ts +137 -0
- package/dist/chart-group.js +1155 -0
- package/dist/chart-interface.d.ts +1 -1
- package/dist/donut-center-content.d.ts +1 -0
- package/dist/donut-center-content.js +21 -38
- package/dist/donut-chart.js +30 -15
- package/dist/gauge-chart.d.ts +20 -0
- package/dist/gauge-chart.js +229 -133
- package/dist/legend-state.d.ts +19 -0
- package/dist/legend-state.js +81 -0
- package/dist/legend.d.ts +5 -2
- package/dist/legend.js +45 -38
- package/dist/line.js +3 -1
- package/dist/pie-chart.d.ts +3 -0
- package/dist/pie-chart.js +45 -19
- package/dist/scatter.d.ts +16 -0
- package/dist/scatter.js +165 -0
- package/dist/tooltip.d.ts +2 -1
- package/dist/tooltip.js +21 -25
- package/dist/types.d.ts +19 -1
- package/dist/utils.js +11 -19
- package/dist/validation.d.ts +4 -0
- package/dist/validation.js +19 -0
- package/dist/x-axis.d.ts +10 -0
- package/dist/x-axis.js +190 -149
- package/dist/xy-chart.d.ts +40 -1
- package/dist/xy-chart.js +488 -165
- package/dist/y-axis.d.ts +7 -2
- package/dist/y-axis.js +99 -10
- package/docs/chart-group.md +213 -0
- package/docs/components.md +321 -0
- package/docs/donut-chart.md +193 -0
- package/docs/gauge-chart.md +175 -0
- package/docs/getting-started.md +311 -0
- package/docs/pie-chart.md +123 -0
- package/docs/theming.md +162 -0
- package/docs/word-cloud-chart.md +98 -0
- package/docs/xy-chart.md +517 -0
- package/package.json +6 -4
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# Components
|
|
2
|
+
|
|
3
|
+
Charts are built by composing components. All components are added via the `addChild()` method.
|
|
4
|
+
|
|
5
|
+
## XAxis
|
|
6
|
+
|
|
7
|
+
Renders the X axis.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
new XAxis({
|
|
11
|
+
display?: boolean, // Render axis and reserve layout space (default: true)
|
|
12
|
+
dataKey?: string, // Key in data objects for X values (auto-detected if omitted)
|
|
13
|
+
labelKey?: string, // Optional display label key (uses dataKey values if omitted)
|
|
14
|
+
groupLabelKey?: string, // Optional key used for second-row grouped labels
|
|
15
|
+
showGroupLabels?: boolean, // Show second-row grouped labels (default: false)
|
|
16
|
+
groupLabelGap?: number, // Vertical gap between tick row and grouped row
|
|
17
|
+
rotatedLabels?: boolean, // Rotate tick labels -45deg
|
|
18
|
+
maxLabelWidth?: number, // Optional cap for tick-label width
|
|
19
|
+
oversizedBehavior?: 'truncate' | 'wrap' | 'hide', // Only applies when maxLabelWidth is set
|
|
20
|
+
autoHideOverlapping?: boolean, // Automatically hide overlapping labels
|
|
21
|
+
minLabelGap?: number, // Minimum gap between visible labels when auto-hide is enabled
|
|
22
|
+
preserveEndLabels?: boolean, // Keep first/last labels visible when auto-hide is enabled
|
|
23
|
+
tickFormat?: string | ((value: string | number | Date) => string) | null
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Grouped label styles come from `theme.axis.groupLabel` and are bold by default.
|
|
28
|
+
|
|
29
|
+
### Example
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
chart.addChild(new XAxis({ dataKey: 'date' }));
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Callback formatter example:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
new XAxis({
|
|
39
|
+
dataKey: 'month',
|
|
40
|
+
tickFormat: (value) => `Month ${value}`,
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Grouped axis example:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
new XAxis({
|
|
48
|
+
// dataKey can be omitted if XAxis auto-detection is enough
|
|
49
|
+
dataKey: '__iis_grouped_category_id__',
|
|
50
|
+
showGroupLabels: true,
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## YAxis
|
|
57
|
+
|
|
58
|
+
Renders the Y axis.
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
new YAxis({
|
|
62
|
+
display?: boolean, // Render axis and reserve layout space (default: true)
|
|
63
|
+
rotatedLabels?: boolean, // Rotate tick labels -45deg
|
|
64
|
+
maxLabelWidth?: number, // Optional cap for tick-label width
|
|
65
|
+
oversizedBehavior?: 'truncate' | 'wrap' | 'hide', // Only applies when maxLabelWidth is set
|
|
66
|
+
tickFormat?: string | ((value: string | number | Date) => string) | null
|
|
67
|
+
})
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
When `maxLabelWidth` is omitted, `YAxis` reserves the measured width of its
|
|
71
|
+
rendered tick labels. Set `maxLabelWidth` to cap the reserved width and use
|
|
72
|
+
`oversizedBehavior` to control truncation, wrapping, or hiding.
|
|
73
|
+
|
|
74
|
+
### Format Examples
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
new YAxis(); // Raw numbers: 35000
|
|
78
|
+
new YAxis({ tickFormat: 's' }); // SI-prefix: 35k
|
|
79
|
+
new YAxis({ tickFormat: '$,' }); // Currency: $35,000
|
|
80
|
+
new YAxis({ tickFormat: '.1%' }); // Percentage: 35.0%
|
|
81
|
+
new YAxis({ tickFormat: '.2f' }); // Fixed decimal: 35000.00
|
|
82
|
+
new YAxis({ tickFormat: (value) => `Value ${value}` });
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
See [D3 format specifiers](https://github.com/d3/d3-format) for all options.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Grid
|
|
90
|
+
|
|
91
|
+
Renders grid lines in the plot area.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
new Grid({
|
|
95
|
+
value?: boolean, // Show grid lines for the value axis (default: true)
|
|
96
|
+
category?: boolean, // Show grid lines for the category axis (default: true)
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`value` and `category` stay semantically stable when `XYChart.orientation`
|
|
101
|
+
changes. In vertical charts, `value: true` renders horizontal grid lines. In
|
|
102
|
+
horizontal charts, `value: true` renders vertical grid lines.
|
|
103
|
+
|
|
104
|
+
### Example
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
// Value-axis grid only
|
|
108
|
+
chart.addChild(new Grid({ category: false, value: true }));
|
|
109
|
+
|
|
110
|
+
// Both axes
|
|
111
|
+
chart.addChild(new Grid());
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Tooltip
|
|
117
|
+
|
|
118
|
+
Renders interactive tooltips on hover and keyboard focus.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
new Tooltip({
|
|
122
|
+
formatter?: (dataKey: string, value: DataValue, data: DataItem) => string
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The formatter receives:
|
|
127
|
+
|
|
128
|
+
- `dataKey` - The series key (e.g., 'revenue')
|
|
129
|
+
- `value` - The data value at this point
|
|
130
|
+
- `data` - The full data item object
|
|
131
|
+
|
|
132
|
+
### Example
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
new Tooltip({
|
|
136
|
+
formatter: (dataKey, value, data) =>
|
|
137
|
+
`<strong>${dataKey}</strong><br/>
|
|
138
|
+
Value: ${value.toLocaleString()}<br/>
|
|
139
|
+
Date: ${data.date}`,
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Legend
|
|
146
|
+
|
|
147
|
+
Renders an interactive legend. Click items to toggle series visibility.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
new Legend({
|
|
151
|
+
mode?: 'inline' | 'disconnected' | 'hidden', // Rendering mode (default: 'inline')
|
|
152
|
+
position?: 'bottom', // Position (currently only 'bottom')
|
|
153
|
+
disconnectedTarget?: string | HTMLElement, // External mount target when mode is 'disconnected'
|
|
154
|
+
marginTop?: number, // Space above legend (default: 20)
|
|
155
|
+
marginBottom?: number, // Space below legend (default: 10)
|
|
156
|
+
paddingX?: number, // Horizontal inset for legend layout (default: theme.legend.paddingX)
|
|
157
|
+
itemSpacingX?: number, // Horizontal space between legend items (default: theme.legend.itemSpacingX)
|
|
158
|
+
itemSpacingY?: number, // Vertical space between wrapped legend rows (default: theme.legend.itemSpacingY)
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Legend items automatically wrap to new rows when they do not fit available
|
|
163
|
+
width. Each wrapped row is centered independently.
|
|
164
|
+
|
|
165
|
+
- `inline`: current default behavior (legend rendered inside the chart SVG)
|
|
166
|
+
- `disconnected`: legend rendered in a separate SVG outside chart layout
|
|
167
|
+
- `hidden`: no legend rendering; use chart legend APIs for custom HTML legends
|
|
168
|
+
|
|
169
|
+
### Example
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
chart.addChild(new Legend({ position: 'bottom' }));
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Custom HTML Legend
|
|
176
|
+
|
|
177
|
+
```javascript
|
|
178
|
+
chart.addChild(new Legend({ mode: 'hidden' }));
|
|
179
|
+
|
|
180
|
+
const items = chart.getLegendItems();
|
|
181
|
+
// Render items in HTML and call:
|
|
182
|
+
chart.toggleLegendSeries(items[0].dataKey);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Legend control APIs also work without mounting a `Legend` component at all. This
|
|
186
|
+
is what allows [`ChartGroup`](./chart-group.md) to coordinate multiple child
|
|
187
|
+
charts from one shared legend.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Title
|
|
192
|
+
|
|
193
|
+
Renders a title for the chart.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
new Title({
|
|
197
|
+
display?: boolean, // Render title and reserve layout space (default: true)
|
|
198
|
+
text: string, // Title text (required)
|
|
199
|
+
fontSize?: number, // Font size in pixels (default: 18)
|
|
200
|
+
fontWeight?: string, // Font weight (default: 'bold')
|
|
201
|
+
align?: 'left' | 'center' | 'right', // Alignment (default: 'center')
|
|
202
|
+
marginTop?: number, // Space above title (default: 10)
|
|
203
|
+
marginBottom?: number, // Space below title (default: 15)
|
|
204
|
+
})
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Example
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
chart.addChild(
|
|
211
|
+
new Title({
|
|
212
|
+
text: 'Monthly Revenue',
|
|
213
|
+
align: 'left',
|
|
214
|
+
fontSize: 20,
|
|
215
|
+
}),
|
|
216
|
+
);
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Export Hooks
|
|
222
|
+
|
|
223
|
+
Components can provide `exportHooks` to adjust export-only settings or mutate
|
|
224
|
+
the exported SVG right before serialization. Hooks run only for visual exports
|
|
225
|
+
(`svg`, `png`, `jpg`, `pdf`) and never touch the live chart instance. Use
|
|
226
|
+
`beforeRender` to return config overrides and `before` to mutate the export
|
|
227
|
+
SVG.
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
const title = new Title({
|
|
231
|
+
text: 'Original',
|
|
232
|
+
exportHooks: {
|
|
233
|
+
before({ svg }) {
|
|
234
|
+
const text = svg.querySelector('.title text');
|
|
235
|
+
if (text) {
|
|
236
|
+
text.textContent = 'Exported';
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
chart.addChild(title);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
You can control the export size via options:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
await chart.export('svg', { width: 1200, height: 800 });
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
You can also return a partial config to re-render the export:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
new Line({
|
|
255
|
+
dataKey: 'revenue',
|
|
256
|
+
exportHooks: {
|
|
257
|
+
beforeRender(_context, currentConfig) {
|
|
258
|
+
if (!currentConfig.valueLabel?.show) {
|
|
259
|
+
return {
|
|
260
|
+
valueLabel: {
|
|
261
|
+
show: true,
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
For container-query responsive behavior, use chart-level `responsive`
|
|
271
|
+
breakpoints in chart constructor config (see
|
|
272
|
+
[XYChart API](./xy-chart.md#responsive-overrides)).
|
|
273
|
+
|
|
274
|
+
## Export Formats
|
|
275
|
+
|
|
276
|
+
`chart.export()` is async and supports:
|
|
277
|
+
|
|
278
|
+
- `svg`
|
|
279
|
+
- `json`
|
|
280
|
+
- `csv`
|
|
281
|
+
- `xlsx` (lazy-loads optional `xlsx`)
|
|
282
|
+
- `png`
|
|
283
|
+
- `jpg`
|
|
284
|
+
- `pdf` (lazy-loads optional `jspdf`)
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
await chart.export('png', { download: true });
|
|
288
|
+
await chart.export('csv', { download: true, delimiter: ';' });
|
|
289
|
+
await chart.export('xlsx', { download: true, sheetName: 'Data' });
|
|
290
|
+
await chart.export('pdf', { download: true, pdfMargin: 16 });
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Export option highlights:
|
|
294
|
+
|
|
295
|
+
- `columns`: choose specific columns for `csv` and `xlsx`
|
|
296
|
+
- `delimiter`: CSV delimiter (default `,`)
|
|
297
|
+
- `pixelRatio`: render scale for `png`, `jpg`, `pdf`
|
|
298
|
+
- `backgroundColor`: defaults transparent for `png`, white for `jpg`/`pdf`
|
|
299
|
+
- `jpegQuality`: JPEG quality for `jpg` (default `0.92`)
|
|
300
|
+
- `sheetName`: sheet name for `xlsx` (default `Sheet1`)
|
|
301
|
+
- `pdfMargin`: page margin for `pdf` (default `0`)
|
|
302
|
+
|
|
303
|
+
`json` export returns only the chart data payload
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Component Order
|
|
308
|
+
|
|
309
|
+
Components are rendered in the order they're added. A typical order:
|
|
310
|
+
|
|
311
|
+
```javascript
|
|
312
|
+
chart
|
|
313
|
+
.addChild(new Title({ text: 'Chart Title' }))
|
|
314
|
+
.addChild(new Grid({ value: true }))
|
|
315
|
+
.addChild(new XAxis({ dataKey: 'date' }))
|
|
316
|
+
.addChild(new YAxis())
|
|
317
|
+
.addChild(new Tooltip())
|
|
318
|
+
.addChild(new Legend({ position: 'bottom' }))
|
|
319
|
+
.addChild(new Line({ dataKey: 'value1' }))
|
|
320
|
+
.addChild(new Line({ dataKey: 'value2' }));
|
|
321
|
+
```
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# DonutChart API
|
|
2
|
+
|
|
3
|
+
A chart for displaying proportional data as segments of a donut/pie.
|
|
4
|
+
|
|
5
|
+
## Constructor
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
new DonutChart(config: DonutChartConfig)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Config Options
|
|
12
|
+
|
|
13
|
+
| Option | Type | Default | Description |
|
|
14
|
+
| ------------ | ------------------------- | --------- | --------------------------------------------------------------------- |
|
|
15
|
+
| `data` | `DataItem[]` | required | Array of data objects |
|
|
16
|
+
| `width` | `number` | - | Explicit chart width in pixels |
|
|
17
|
+
| `height` | `number` | - | Explicit chart height in pixels |
|
|
18
|
+
| `valueKey` | `string` | `'value'` | Key for numeric values in data |
|
|
19
|
+
| `labelKey` | `string` | `'name'` | Key for segment labels in data |
|
|
20
|
+
| `donut` | `DonutConfig` | - | Donut-specific configuration |
|
|
21
|
+
| `valueLabel` | `DonutValueLabelConfig` | - | On-chart outside label/value rendering configuration |
|
|
22
|
+
| `theme` | `DeepPartial<ChartTheme>` | - | Theme customization |
|
|
23
|
+
| `responsive` | `ResponsiveConfig` | - | Declarative container-query responsive overrides (theme + components) |
|
|
24
|
+
|
|
25
|
+
### Donut Config
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
donut: {
|
|
29
|
+
innerRadius?: number, // 0-1, percentage of outer radius (default: 0.5)
|
|
30
|
+
padAngle?: number, // Radians between segments (default: 0.02)
|
|
31
|
+
cornerRadius?: number, // Corner radius in pixels (default: 0)
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### ValueLabel Config
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
valueLabel: {
|
|
39
|
+
show?: boolean, // default: false
|
|
40
|
+
position?: 'outside' | 'auto', // default: 'auto'
|
|
41
|
+
outsideOffset?: number, // default: 16
|
|
42
|
+
minVerticalSpacing?: number, // default: 14
|
|
43
|
+
formatter?: (label, value, data, percentage) => string, // default: `${label}: ${value}`
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Donut value labels are rendered outside the ring with leader lines. `auto`
|
|
48
|
+
currently resolves to the same outside placement as `outside`.
|
|
49
|
+
|
|
50
|
+
## Example
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
import { DonutChart } from '@internetstiftelsen/charts/donut-chart';
|
|
54
|
+
import { DonutCenterContent } from '@internetstiftelsen/charts/donut-center-content';
|
|
55
|
+
import { Legend } from '@internetstiftelsen/charts/legend';
|
|
56
|
+
import { Tooltip } from '@internetstiftelsen/charts/tooltip';
|
|
57
|
+
import { Title } from '@internetstiftelsen/charts/title';
|
|
58
|
+
|
|
59
|
+
const data = [
|
|
60
|
+
{ name: 'Desktop', value: 450 },
|
|
61
|
+
{ name: 'Mobile', value: 320 },
|
|
62
|
+
{ name: 'Tablet', value: 130 },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const chart = new DonutChart({
|
|
66
|
+
data,
|
|
67
|
+
valueKey: 'value',
|
|
68
|
+
labelKey: 'name',
|
|
69
|
+
donut: {
|
|
70
|
+
innerRadius: 0.6,
|
|
71
|
+
padAngle: 0.02,
|
|
72
|
+
cornerRadius: 4,
|
|
73
|
+
},
|
|
74
|
+
valueLabel: {
|
|
75
|
+
show: true,
|
|
76
|
+
formatter: (label, _value, _data, percentage) =>
|
|
77
|
+
`${label}: ${percentage.toFixed(1)}%`,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
chart
|
|
82
|
+
.addChild(new Title({ text: 'Device Distribution' }))
|
|
83
|
+
.addChild(
|
|
84
|
+
new DonutCenterContent({
|
|
85
|
+
mainValue: '900',
|
|
86
|
+
title: 'Total',
|
|
87
|
+
subtitle: 'visitors',
|
|
88
|
+
}),
|
|
89
|
+
)
|
|
90
|
+
.addChild(new Legend({ position: 'bottom' }))
|
|
91
|
+
.addChild(new Tooltip());
|
|
92
|
+
|
|
93
|
+
chart.render('#chart-container');
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Responsive Height and Center Text
|
|
97
|
+
|
|
98
|
+
- DonutChart uses container-driven `100%` height (same behavior model as width).
|
|
99
|
+
- Set an explicit container height for predictable visual sizing.
|
|
100
|
+
- Donut center-content text shrinks with chart size so it scales with the donut.
|
|
101
|
+
- Donut value-label text also scales down with the chart size.
|
|
102
|
+
|
|
103
|
+
## Custom Colors
|
|
104
|
+
|
|
105
|
+
You can specify colors directly in your data:
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
const data = [
|
|
109
|
+
{ name: 'Desktop', value: 450, color: '#4ecdc4' },
|
|
110
|
+
{ name: 'Mobile', value: 320, color: '#ff6b6b' },
|
|
111
|
+
{ name: 'Tablet', value: 130, color: '#45b7d1' },
|
|
112
|
+
];
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Or use the theme's color palette (colors are auto-assigned by index).
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## DonutCenterContent
|
|
120
|
+
|
|
121
|
+
Displays text content in the center of the donut.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
new DonutCenterContent(config: DonutCenterContentConfig)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Config Options
|
|
128
|
+
|
|
129
|
+
| Option | Type | Description |
|
|
130
|
+
| ---------------- | ----------- | --------------------------------- |
|
|
131
|
+
| `mainValue` | `string` | Large primary value (e.g., "900") |
|
|
132
|
+
| `title` | `string` | Title text below main value |
|
|
133
|
+
| `subtitle` | `string` | Smaller subtitle text |
|
|
134
|
+
| `mainValueStyle` | `TextStyle` | Style for main value |
|
|
135
|
+
| `titleStyle` | `TextStyle` | Style for title |
|
|
136
|
+
| `subtitleStyle` | `TextStyle` | Style for subtitle |
|
|
137
|
+
|
|
138
|
+
### TextStyle Options
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
{
|
|
142
|
+
fontSize?: number,
|
|
143
|
+
fontWeight?: string,
|
|
144
|
+
fontFamily?: string,
|
|
145
|
+
color?: string,
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Example
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
new DonutCenterContent({
|
|
153
|
+
mainValue: '$1.2M',
|
|
154
|
+
title: 'Revenue',
|
|
155
|
+
subtitle: 'Q4 2024',
|
|
156
|
+
mainValueStyle: {
|
|
157
|
+
fontSize: 36,
|
|
158
|
+
fontWeight: 'bold',
|
|
159
|
+
color: '#333',
|
|
160
|
+
},
|
|
161
|
+
titleStyle: {
|
|
162
|
+
fontSize: 14,
|
|
163
|
+
color: '#666',
|
|
164
|
+
},
|
|
165
|
+
subtitleStyle: {
|
|
166
|
+
fontSize: 12,
|
|
167
|
+
color: '#999',
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Supported Components
|
|
175
|
+
|
|
176
|
+
DonutChart supports the following components via `addChild()`:
|
|
177
|
+
|
|
178
|
+
- `DonutCenterContent` - Center text content
|
|
179
|
+
- `Title` - Chart title
|
|
180
|
+
- `Legend` - Interactive legend (click to toggle segments)
|
|
181
|
+
- `Tooltip` - Hover tooltips with segment info
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Tooltip Formatting
|
|
186
|
+
|
|
187
|
+
The default tooltip shows label, value, and percentage. Customize with a formatter:
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
new Tooltip({
|
|
191
|
+
formatter: (dataKey, value, data) => `${dataKey}: ${value.toLocaleString()} visitors`,
|
|
192
|
+
});
|
|
193
|
+
```
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# GaugeChart API
|
|
2
|
+
|
|
3
|
+
A chart for displaying a single KPI value against a range with optional
|
|
4
|
+
threshold bands and target marker.
|
|
5
|
+
|
|
6
|
+
## Constructor
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
new GaugeChart(config: GaugeChartConfig)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Config Options
|
|
13
|
+
|
|
14
|
+
| Option | Type | Default | Description |
|
|
15
|
+
| ---------------- | ------------------------- | --------- | --------------------------------------------------------------------- |
|
|
16
|
+
| `data` | `DataItem[]` | required | Data array (first row is used) |
|
|
17
|
+
| `width` | `number` | - | Explicit chart width in pixels |
|
|
18
|
+
| `height` | `number` | - | Explicit chart height in pixels |
|
|
19
|
+
| `valueKey` | `string` | `'value'` | Key for the current value in the first row |
|
|
20
|
+
| `targetValueKey` | `string` | - | Optional key for target value in the first row |
|
|
21
|
+
| `gauge` | `GaugeConfig` | - | Gauge-specific configuration |
|
|
22
|
+
| `theme` | `DeepPartial<ChartTheme>` | - | Theme customization |
|
|
23
|
+
| `responsive` | `ResponsiveConfig` | - | Declarative container-query responsive overrides (theme + components) |
|
|
24
|
+
|
|
25
|
+
### Gauge Config
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
gauge: {
|
|
29
|
+
value?: number, // Explicit value (overrides valueKey)
|
|
30
|
+
targetValue?: number, // Explicit target (overrides targetValueKey)
|
|
31
|
+
min?: number, // default: 0
|
|
32
|
+
max?: number, // default: 100
|
|
33
|
+
animate?: boolean | { // default: false
|
|
34
|
+
show?: boolean, // default: true when object is used
|
|
35
|
+
duration?: number, // ms, default: 700
|
|
36
|
+
easing?: // default: 'ease-in-out'
|
|
37
|
+
| 'linear'
|
|
38
|
+
| 'ease-in'
|
|
39
|
+
| 'ease-out'
|
|
40
|
+
| 'ease-in-out'
|
|
41
|
+
| 'bounce-out'
|
|
42
|
+
| 'elastic-out'
|
|
43
|
+
| `linear(...)` // CSS-like piecewise linear easing
|
|
44
|
+
| ((t: number) => number),
|
|
45
|
+
},
|
|
46
|
+
halfCircle?: boolean, // default: false
|
|
47
|
+
startAngle?: number, // default: -Math.PI * 0.75 (or -Math.PI / 2 in halfCircle mode)
|
|
48
|
+
endAngle?: number, // default: Math.PI * 0.75 (or Math.PI / 2 in halfCircle mode)
|
|
49
|
+
innerRadius?: number, // 0-1, default: 0.68
|
|
50
|
+
thickness?: number, // px, overrides innerRadius when provided
|
|
51
|
+
cornerRadius?: number, // px, default: 4
|
|
52
|
+
trackColor?: string, // default: #e5e7eb
|
|
53
|
+
progressColor?: string, // default: theme.colorPalette[0]
|
|
54
|
+
targetColor?: string, // default: #111827
|
|
55
|
+
segmentStyle?: 'solid' | 'gradient', // default: 'solid'
|
|
56
|
+
showValue?: boolean, // default: true
|
|
57
|
+
valueFormatter?: (value) => string,
|
|
58
|
+
valueLabelStyle?: {
|
|
59
|
+
fontSize?: number | string, // default: 28
|
|
60
|
+
fontFamily?: string, // default: theme.axis.fontFamily
|
|
61
|
+
fontWeight?: number | string, // default: 700
|
|
62
|
+
color?: string, // default: #111827
|
|
63
|
+
},
|
|
64
|
+
needle?: boolean | {
|
|
65
|
+
show?: boolean, // default: true
|
|
66
|
+
color?: string, // default: #111827
|
|
67
|
+
width?: number,
|
|
68
|
+
lengthRatio?: number,
|
|
69
|
+
capRadius?: number,
|
|
70
|
+
},
|
|
71
|
+
marker?: boolean | {
|
|
72
|
+
show?: boolean, // default: !needle.show
|
|
73
|
+
color?: string,
|
|
74
|
+
width?: number,
|
|
75
|
+
},
|
|
76
|
+
ticks?: {
|
|
77
|
+
count?: number, // default: 5
|
|
78
|
+
show?: boolean, // default: true
|
|
79
|
+
showLines?: boolean, // default: true
|
|
80
|
+
showLabels?: boolean, // default: true
|
|
81
|
+
size?: number, // default: 8
|
|
82
|
+
labelOffset?: number, // default: 12
|
|
83
|
+
formatter?: (value) => string,
|
|
84
|
+
labelStyle?: {
|
|
85
|
+
fontSize?: number | string, // default: 11
|
|
86
|
+
fontFamily?: string, // default: theme.axis.fontFamily
|
|
87
|
+
fontWeight?: number | string, // default: theme.axis.fontWeight
|
|
88
|
+
color?: string, // default: #4b5563
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
segments?: [
|
|
92
|
+
{
|
|
93
|
+
from: number,
|
|
94
|
+
to: number,
|
|
95
|
+
color?: string, // uses theme.colorPalette[index] when omitted
|
|
96
|
+
label?: string,
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Example
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
import { GaugeChart } from '@internetstiftelsen/charts/gauge-chart';
|
|
106
|
+
import { Tooltip } from '@internetstiftelsen/charts/tooltip';
|
|
107
|
+
import { Legend } from '@internetstiftelsen/charts/legend';
|
|
108
|
+
import { Title } from '@internetstiftelsen/charts/title';
|
|
109
|
+
|
|
110
|
+
const chart = new GaugeChart({
|
|
111
|
+
data: [{ label: 'KPI', value: 72, target: 80 }],
|
|
112
|
+
valueKey: 'value',
|
|
113
|
+
targetValueKey: 'target',
|
|
114
|
+
gauge: {
|
|
115
|
+
min: 0,
|
|
116
|
+
max: 100,
|
|
117
|
+
animate: {
|
|
118
|
+
show: true,
|
|
119
|
+
duration: 900,
|
|
120
|
+
easing: 'ease-out',
|
|
121
|
+
},
|
|
122
|
+
halfCircle: true,
|
|
123
|
+
showValue: true,
|
|
124
|
+
needle: true,
|
|
125
|
+
ticks: { count: 5 },
|
|
126
|
+
segments: [
|
|
127
|
+
{ from: 0, to: 60, color: '#10b981', label: 'Good' },
|
|
128
|
+
{ from: 60, to: 85, color: '#f59e0b', label: 'Warning' },
|
|
129
|
+
{ from: 85, to: 100, color: '#ef4444', label: 'Risk' },
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
chart
|
|
135
|
+
.addChild(new Title({ text: 'Operational KPI' }))
|
|
136
|
+
.addChild(new Tooltip())
|
|
137
|
+
.addChild(new Legend({ position: 'bottom' }));
|
|
138
|
+
|
|
139
|
+
chart.render('#gauge-container');
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Validation and Clamping
|
|
143
|
+
|
|
144
|
+
- `gauge.min` must be less than `gauge.max`.
|
|
145
|
+
- Segment ranges must stay inside `[min, max]` and must not overlap.
|
|
146
|
+
- You can pass any number of threshold segments.
|
|
147
|
+
- Segment colors are optional; omitted colors use `theme.colorPalette`
|
|
148
|
+
(or a built-in fallback palette if the theme palette is empty).
|
|
149
|
+
- Out-of-range `value` and `targetValue` are clamped to `[min, max]` with a
|
|
150
|
+
non-fatal warning.
|
|
151
|
+
|
|
152
|
+
## Animation Easing Example
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
const chart = new GaugeChart({
|
|
156
|
+
data: [{ value: 72 }],
|
|
157
|
+
gauge: {
|
|
158
|
+
min: 0,
|
|
159
|
+
max: 100,
|
|
160
|
+
animate: {
|
|
161
|
+
show: true,
|
|
162
|
+
duration: 1200,
|
|
163
|
+
easing: 'linear(0, 0.029 1.6%, 0.123 3.5%, 0.651 10.6%, 0.862 14.1%, 1.002 17.7%, 1.046 19.6%, 1.074 21.6%, 1.087 23.9%, 1.086 26.6%, 1.014 38.5%, 0.994 46.3%, 1)',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Supported Components
|
|
170
|
+
|
|
171
|
+
GaugeChart supports the following components via `addChild()`:
|
|
172
|
+
|
|
173
|
+
- `Title` - Chart title
|
|
174
|
+
- `Tooltip` - Hover tooltip (value, target)
|
|
175
|
+
- `Legend` - Segment legend with visibility toggles
|