@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.
Files changed (46) hide show
  1. package/README.md +137 -3
  2. package/dist/area.d.ts +2 -0
  3. package/dist/area.js +39 -31
  4. package/dist/bar.d.ts +20 -1
  5. package/dist/bar.js +395 -519
  6. package/dist/base-chart.d.ts +21 -1
  7. package/dist/base-chart.js +166 -93
  8. package/dist/chart-group.d.ts +137 -0
  9. package/dist/chart-group.js +1155 -0
  10. package/dist/chart-interface.d.ts +1 -1
  11. package/dist/donut-center-content.d.ts +1 -0
  12. package/dist/donut-center-content.js +21 -38
  13. package/dist/donut-chart.js +30 -15
  14. package/dist/gauge-chart.d.ts +20 -0
  15. package/dist/gauge-chart.js +229 -133
  16. package/dist/legend-state.d.ts +19 -0
  17. package/dist/legend-state.js +81 -0
  18. package/dist/legend.d.ts +5 -2
  19. package/dist/legend.js +45 -38
  20. package/dist/line.js +3 -1
  21. package/dist/pie-chart.d.ts +3 -0
  22. package/dist/pie-chart.js +45 -19
  23. package/dist/scatter.d.ts +16 -0
  24. package/dist/scatter.js +165 -0
  25. package/dist/tooltip.d.ts +2 -1
  26. package/dist/tooltip.js +21 -25
  27. package/dist/types.d.ts +19 -1
  28. package/dist/utils.js +11 -19
  29. package/dist/validation.d.ts +4 -0
  30. package/dist/validation.js +19 -0
  31. package/dist/x-axis.d.ts +10 -0
  32. package/dist/x-axis.js +190 -149
  33. package/dist/xy-chart.d.ts +40 -1
  34. package/dist/xy-chart.js +488 -165
  35. package/dist/y-axis.d.ts +7 -2
  36. package/dist/y-axis.js +99 -10
  37. package/docs/chart-group.md +213 -0
  38. package/docs/components.md +321 -0
  39. package/docs/donut-chart.md +193 -0
  40. package/docs/gauge-chart.md +175 -0
  41. package/docs/getting-started.md +311 -0
  42. package/docs/pie-chart.md +123 -0
  43. package/docs/theming.md +162 -0
  44. package/docs/word-cloud-chart.md +98 -0
  45. package/docs/xy-chart.md +517 -0
  46. 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