@cfasim-ui/charts 0.7.8 → 0.8.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/dist/BarChart/BarChart.d.ts +9 -26
- package/dist/ChartMenu/ChartMenu.d.ts +3 -2
- package/dist/ChartTooltip/ChartTooltip.d.ts +9 -12
- package/dist/ChoroplethMap/ChoroplethMap.d.ts +42 -190
- package/dist/ChoroplethMap/ChoroplethTooltip.d.ts +20 -27
- package/dist/ChoroplethMap/canvasLayer.d.ts +23 -6
- package/dist/ChoroplethMap/cityLayout.d.ts +3 -2
- package/dist/ChoroplethMap/mixedGeo.d.ts +74 -0
- package/dist/ChoroplethMap/mixedGeo.test.d.ts +1 -0
- package/dist/DataTable/DataTable.d.ts +3 -2
- package/dist/LineChart/LineChart.d.ts +9 -26
- package/dist/_shared/ChartAnnotations.d.ts +3 -2
- package/dist/_shared/ChartZoomControls.d.ts +3 -2
- package/dist/_shared/index.d.ts +2 -1
- package/dist/_shared/mapTheme.d.ts +159 -0
- package/dist/_shared/mapTheme.test.d.ts +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1821 -1462
- package/docs/BarChart.md +776 -0
- package/docs/ChoroplethMap.md +1394 -0
- package/docs/DataTable.md +386 -0
- package/docs/LineChart.md +1267 -0
- package/docs/index.json +56 -0
- package/package.json +25 -21
- package/src/BarChart/BarChart.md +743 -0
- package/src/BarChart/BarChart.vue +1901 -0
- package/src/ChartMenu/ChartMenu.vue +220 -0
- package/src/ChartMenu/download.ts +120 -0
- package/src/ChartTooltip/ChartTooltip.vue +97 -0
- package/src/ChoroplethMap/ChoroplethMap.md +1354 -0
- package/src/ChoroplethMap/ChoroplethMap.vue +3778 -0
- package/src/ChoroplethMap/ChoroplethTooltip.vue +103 -0
- package/src/ChoroplethMap/canvasLayer.ts +373 -0
- package/src/ChoroplethMap/cityLayout.ts +262 -0
- package/src/ChoroplethMap/hsaMapping.ts +4116 -0
- package/src/ChoroplethMap/mixedGeo.ts +201 -0
- package/src/DataTable/DataTable.md +372 -0
- package/src/DataTable/DataTable.vue +406 -0
- package/src/LineChart/LineChart.md +1225 -0
- package/src/LineChart/LineChart.vue +1555 -0
- package/src/_shared/ChartAnnotations.vue +420 -0
- package/src/_shared/ChartZoomControls.vue +138 -0
- package/src/_shared/annotations.ts +106 -0
- package/src/_shared/axes.ts +69 -0
- package/src/_shared/chartProps.ts +201 -0
- package/src/_shared/computeTicks.ts +42 -0
- package/src/_shared/contrast.ts +100 -0
- package/src/_shared/dateAxis.ts +501 -0
- package/src/_shared/index.ts +78 -0
- package/src/_shared/mapTheme.ts +551 -0
- package/src/_shared/scale.ts +86 -0
- package/src/_shared/seriesCsv.ts +68 -0
- package/src/_shared/touch.ts +8 -0
- package/src/_shared/useChartFoundation.ts +175 -0
- package/src/_shared/useChartFullscreen.ts +254 -0
- package/src/_shared/useChartMenu.ts +111 -0
- package/src/_shared/useChartPadding.ts +235 -0
- package/src/_shared/useChartSize.ts +58 -0
- package/src/_shared/useChartTooltip.ts +205 -0
- package/src/env.d.ts +4 -0
- package/src/hsa-mapping.ts +1 -0
- package/src/index.ts +41 -0
- package/src/tooltip-position.ts +55 -0
- package/src/us-cities/data.ts +1371 -0
- package/src/us-cities/index.ts +122 -0
- package/src/us-cities.ts +7 -0
package/docs/BarChart.md
ADDED
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
# BarChart
|
|
2
|
+
|
|
3
|
+
A responsive SVG bar chart supporting single, grouped, stacked, and overlay
|
|
4
|
+
series in either vertical (column) or horizontal orientation. Shares axis,
|
|
5
|
+
tooltip, menu, and CSV export wiring with [`LineChart`](./line-chart).
|
|
6
|
+
|
|
7
|
+
## Examples
|
|
8
|
+
|
|
9
|
+
### Single series
|
|
10
|
+
|
|
11
|
+
<ComponentDemo>
|
|
12
|
+
<BarChart
|
|
13
|
+
:data="[12, 19, 7, 24, 16]"
|
|
14
|
+
:categories="['Mon', 'Tue', 'Wed', 'Thu', 'Fri']"
|
|
15
|
+
:height="220"
|
|
16
|
+
x-label="Day"
|
|
17
|
+
y-label="Cases"
|
|
18
|
+
tooltip-trigger="hover"
|
|
19
|
+
/>
|
|
20
|
+
|
|
21
|
+
<template #code>
|
|
22
|
+
|
|
23
|
+
```vue
|
|
24
|
+
<BarChart
|
|
25
|
+
:data="[12, 19, 7, 24, 16]"
|
|
26
|
+
:categories="['Mon', 'Tue', 'Wed', 'Thu', 'Fri']"
|
|
27
|
+
:height="220"
|
|
28
|
+
x-label="Day"
|
|
29
|
+
y-label="Cases"
|
|
30
|
+
tooltip-trigger="hover"
|
|
31
|
+
/>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
</template>
|
|
35
|
+
</ComponentDemo>
|
|
36
|
+
|
|
37
|
+
### Grouped series
|
|
38
|
+
|
|
39
|
+
<ComponentDemo>
|
|
40
|
+
<BarChart
|
|
41
|
+
:series="[
|
|
42
|
+
{ data: [10, 14, 9, 18], legend: 'Pediatric', color: '#0057b7' },
|
|
43
|
+
{ data: [22, 28, 19, 30], legend: 'Adult', color: '#f4a261' },
|
|
44
|
+
]"
|
|
45
|
+
:categories="['Q1', 'Q2', 'Q3', 'Q4']"
|
|
46
|
+
:height="220"
|
|
47
|
+
y-label="Doses (thousands)"
|
|
48
|
+
tooltip-trigger="hover"
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
<template #code>
|
|
52
|
+
|
|
53
|
+
```vue
|
|
54
|
+
<BarChart
|
|
55
|
+
:series="[
|
|
56
|
+
{ data: [10, 14, 9, 18], legend: 'Pediatric', color: '#0057b7' },
|
|
57
|
+
{ data: [22, 28, 19, 30], legend: 'Adult', color: '#f4a261' },
|
|
58
|
+
]"
|
|
59
|
+
:categories="['Q1', 'Q2', 'Q3', 'Q4']"
|
|
60
|
+
:height="220"
|
|
61
|
+
y-label="Doses (thousands)"
|
|
62
|
+
tooltip-trigger="hover"
|
|
63
|
+
/>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
</template>
|
|
67
|
+
</ComponentDemo>
|
|
68
|
+
|
|
69
|
+
### Stacked series
|
|
70
|
+
|
|
71
|
+
<ComponentDemo>
|
|
72
|
+
<BarChart
|
|
73
|
+
:series="[
|
|
74
|
+
{ data: [10, 14, 9, 18], legend: 'Pediatric', color: '#0057b7' },
|
|
75
|
+
{ data: [22, 28, 19, 30], legend: 'Adult', color: '#f4a261' },
|
|
76
|
+
]"
|
|
77
|
+
:categories="['Q1', 'Q2', 'Q3', 'Q4']"
|
|
78
|
+
layout="stacked"
|
|
79
|
+
:height="220"
|
|
80
|
+
y-label="Doses (thousands)"
|
|
81
|
+
tooltip-trigger="hover"
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
<template #code>
|
|
85
|
+
|
|
86
|
+
```vue
|
|
87
|
+
<BarChart
|
|
88
|
+
:series="[
|
|
89
|
+
{ data: [10, 14, 9, 18], legend: 'Pediatric', color: '#0057b7' },
|
|
90
|
+
{ data: [22, 28, 19, 30], legend: 'Adult', color: '#f4a261' },
|
|
91
|
+
]"
|
|
92
|
+
:categories="['Q1', 'Q2', 'Q3', 'Q4']"
|
|
93
|
+
layout="stacked"
|
|
94
|
+
:height="220"
|
|
95
|
+
y-label="Doses (thousands)"
|
|
96
|
+
tooltip-trigger="hover"
|
|
97
|
+
/>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
</template>
|
|
101
|
+
</ComponentDemo>
|
|
102
|
+
|
|
103
|
+
### Overlay series
|
|
104
|
+
|
|
105
|
+
`layout="overlay"` draws each series at full group width from the shared
|
|
106
|
+
baseline, painting later series on top of earlier ones. Use it to compare
|
|
107
|
+
a backdrop value (e.g. target, capacity, prior period) against a shorter
|
|
108
|
+
foreground value sharing the same axis.
|
|
109
|
+
|
|
110
|
+
Series render in the order they appear in `series`, so put the taller
|
|
111
|
+
backdrop first and the shorter foreground after. Use `opacity`, a
|
|
112
|
+
distinct color, or a per-series `blendMode` (see below) if bars might
|
|
113
|
+
overlap fully.
|
|
114
|
+
|
|
115
|
+
<ComponentDemo>
|
|
116
|
+
<BarChart
|
|
117
|
+
:series="[
|
|
118
|
+
{ data: [40, 40, 40, 40], legend: 'Target', color: '#d4d4d8' },
|
|
119
|
+
{ data: [22, 35, 28, 38], legend: 'Actual', color: '#0057b7' },
|
|
120
|
+
]"
|
|
121
|
+
:categories="['Q1', 'Q2', 'Q3', 'Q4']"
|
|
122
|
+
layout="overlay"
|
|
123
|
+
:height="220"
|
|
124
|
+
y-label="Doses (thousands)"
|
|
125
|
+
tooltip-trigger="hover"
|
|
126
|
+
/>
|
|
127
|
+
|
|
128
|
+
<template #code>
|
|
129
|
+
|
|
130
|
+
```vue
|
|
131
|
+
<BarChart
|
|
132
|
+
:series="[
|
|
133
|
+
{ data: [40, 40, 40, 40], legend: 'Target', color: '#d4d4d8' },
|
|
134
|
+
{ data: [22, 35, 28, 38], legend: 'Actual', color: '#0057b7' },
|
|
135
|
+
]"
|
|
136
|
+
:categories="['Q1', 'Q2', 'Q3', 'Q4']"
|
|
137
|
+
layout="overlay"
|
|
138
|
+
:height="220"
|
|
139
|
+
y-label="Doses (thousands)"
|
|
140
|
+
tooltip-trigger="hover"
|
|
141
|
+
/>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
</template>
|
|
145
|
+
</ComponentDemo>
|
|
146
|
+
|
|
147
|
+
### Blend mode
|
|
148
|
+
|
|
149
|
+
Set `blendMode` on a series to apply a CSS `mix-blend-mode` to every
|
|
150
|
+
bar in that series. Useful with `layout="overlay"` (or just two
|
|
151
|
+
overlapping grouped bars) to combine colors instead of one obscuring
|
|
152
|
+
the other. Common picks: `"multiply"` (darkens light fills where bars
|
|
153
|
+
overlap), `"screen"` (lightens dark fills), `"difference"` (high
|
|
154
|
+
contrast).
|
|
155
|
+
|
|
156
|
+
<ComponentDemo>
|
|
157
|
+
<BarChart
|
|
158
|
+
:series="[
|
|
159
|
+
{ data: [22, 35, 28, 38], legend: 'Treatment A', color: '#ef4444', blendMode: 'multiply' },
|
|
160
|
+
{ data: [30, 24, 36, 32], legend: 'Treatment B', color: '#3b82f6', blendMode: 'multiply' },
|
|
161
|
+
]"
|
|
162
|
+
:categories="['Q1', 'Q2', 'Q3', 'Q4']"
|
|
163
|
+
layout="overlay"
|
|
164
|
+
:height="220"
|
|
165
|
+
y-label="Cases avoided"
|
|
166
|
+
tooltip-trigger="hover"
|
|
167
|
+
/>
|
|
168
|
+
|
|
169
|
+
<template #code>
|
|
170
|
+
|
|
171
|
+
```vue
|
|
172
|
+
<BarChart
|
|
173
|
+
:series="[
|
|
174
|
+
{
|
|
175
|
+
data: [22, 35, 28, 38],
|
|
176
|
+
legend: 'Treatment A',
|
|
177
|
+
color: '#ef4444',
|
|
178
|
+
blendMode: 'multiply',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
data: [30, 24, 36, 32],
|
|
182
|
+
legend: 'Treatment B',
|
|
183
|
+
color: '#3b82f6',
|
|
184
|
+
blendMode: 'multiply',
|
|
185
|
+
},
|
|
186
|
+
]"
|
|
187
|
+
:categories="['Q1', 'Q2', 'Q3', 'Q4']"
|
|
188
|
+
layout="overlay"
|
|
189
|
+
:height="220"
|
|
190
|
+
y-label="Cases avoided"
|
|
191
|
+
tooltip-trigger="hover"
|
|
192
|
+
/>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
</template>
|
|
196
|
+
</ComponentDemo>
|
|
197
|
+
|
|
198
|
+
### Horizontal orientation
|
|
199
|
+
|
|
200
|
+
On touch devices the tooltip scrubs along the category axis: drag horizontally on a vertical (column) chart and vertically on a horizontal chart. A swipe in the other direction scrolls the page instead of being captured by the chart.
|
|
201
|
+
|
|
202
|
+
<ComponentDemo>
|
|
203
|
+
<BarChart
|
|
204
|
+
:data="[42, 31, 18, 12, 7]"
|
|
205
|
+
:categories="['Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon']"
|
|
206
|
+
orientation="horizontal"
|
|
207
|
+
:height="220"
|
|
208
|
+
x-label="Cases"
|
|
209
|
+
tooltip-trigger="hover"
|
|
210
|
+
/>
|
|
211
|
+
|
|
212
|
+
<template #code>
|
|
213
|
+
|
|
214
|
+
```vue
|
|
215
|
+
<BarChart
|
|
216
|
+
:data="[42, 31, 18, 12, 7]"
|
|
217
|
+
:categories="['Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon']"
|
|
218
|
+
orientation="horizontal"
|
|
219
|
+
:height="220"
|
|
220
|
+
x-label="Cases"
|
|
221
|
+
tooltip-trigger="hover"
|
|
222
|
+
/>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
</template>
|
|
226
|
+
</ComponentDemo>
|
|
227
|
+
|
|
228
|
+
### Value labels on bars
|
|
229
|
+
|
|
230
|
+
Set `bar-labels` to write values directly on the bars (pair with
|
|
231
|
+
`:value-axis="false"` for an axis-free look). Pass an options object to tune:
|
|
232
|
+
|
|
233
|
+
- `format` — a `NumberFormat` or `(value, ctx) => string` (`ctx` has
|
|
234
|
+
`categoryIndex` / `seriesIndex`); return `""` to drop a label.
|
|
235
|
+
- `align` — `"center"` (default), `"start"`, or `"end"` within the segment.
|
|
236
|
+
- `color` — auto-contrast per segment by default, or a fixed color.
|
|
237
|
+
- Small/overlapping labels overflow past the segment edge and are spread
|
|
238
|
+
apart (`overlap: "shift"`) or dropped (`overlap: "hide"`).
|
|
239
|
+
|
|
240
|
+
`category-header` / `value-header` title the two columns, and
|
|
241
|
+
`category-align="start"` left-aligns the category labels.
|
|
242
|
+
|
|
243
|
+
<ComponentDemo>
|
|
244
|
+
<BarChart
|
|
245
|
+
:series="[
|
|
246
|
+
{ data: [99, 88, 50, 1], legend: 'High', color: '#3f1f5e' },
|
|
247
|
+
{ data: [0.5, 10, 30, 20], legend: 'Medium', color: '#8a5fb0' },
|
|
248
|
+
{ data: [0.5, 2, 20, 79], legend: 'Low', color: '#c9aee0' },
|
|
249
|
+
]"
|
|
250
|
+
:categories="['Alpha', 'Beta', 'Gamma', 'Delta']"
|
|
251
|
+
title="Projected outbreak size by scenario"
|
|
252
|
+
:title-style="{ fontSize: 16 }"
|
|
253
|
+
orientation="horizontal"
|
|
254
|
+
layout="stacked"
|
|
255
|
+
:value-axis="false"
|
|
256
|
+
category-header="Scenario"
|
|
257
|
+
value-header="Proportion of simulations in each category"
|
|
258
|
+
category-align="start"
|
|
259
|
+
:height="260"
|
|
260
|
+
:chart-padding="{ left: 18, right: 64 }"
|
|
261
|
+
:tick-label-style="{ fontSize: 11, color: 'currentColor' }"
|
|
262
|
+
:bar-labels="{
|
|
263
|
+
align: 'start',
|
|
264
|
+
fontWeight: 600,
|
|
265
|
+
format: (v, { categoryIndex }) =>
|
|
266
|
+
v < 1
|
|
267
|
+
? '<1%'
|
|
268
|
+
: categoryIndex === 0
|
|
269
|
+
? `${Math.round(v)}%`
|
|
270
|
+
: String(Math.round(v)),
|
|
271
|
+
}"
|
|
272
|
+
/>
|
|
273
|
+
|
|
274
|
+
<template #code>
|
|
275
|
+
|
|
276
|
+
```vue
|
|
277
|
+
<BarChart
|
|
278
|
+
:series="[
|
|
279
|
+
{ data: [99, 88, 50, 1], legend: 'High', color: '#3f1f5e' },
|
|
280
|
+
{ data: [0.5, 10, 30, 20], legend: 'Medium', color: '#8a5fb0' },
|
|
281
|
+
{ data: [0.5, 2, 20, 79], legend: 'Low', color: '#c9aee0' },
|
|
282
|
+
]"
|
|
283
|
+
:categories="['Alpha', 'Beta', 'Gamma', 'Delta']"
|
|
284
|
+
title="Projected outbreak size by scenario"
|
|
285
|
+
:title-style="{ fontSize: 16 }"
|
|
286
|
+
orientation="horizontal"
|
|
287
|
+
layout="stacked"
|
|
288
|
+
:value-axis="false"
|
|
289
|
+
category-header="Scenario"
|
|
290
|
+
value-header="Proportion of simulations in each category"
|
|
291
|
+
category-align="start"
|
|
292
|
+
:height="260"
|
|
293
|
+
:chart-padding="{ left: 18, right: 64 }"
|
|
294
|
+
:tick-label-style="{ fontSize: 11, color: 'currentColor' }"
|
|
295
|
+
:bar-labels="{
|
|
296
|
+
align: 'start',
|
|
297
|
+
fontWeight: 600,
|
|
298
|
+
format: (v, { categoryIndex }) =>
|
|
299
|
+
v < 1
|
|
300
|
+
? '<1%'
|
|
301
|
+
: categoryIndex === 0
|
|
302
|
+
? `${Math.round(v)}%`
|
|
303
|
+
: String(Math.round(v)),
|
|
304
|
+
}"
|
|
305
|
+
/>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
</template>
|
|
309
|
+
</ComponentDemo>
|
|
310
|
+
|
|
311
|
+
#### Small segments
|
|
312
|
+
|
|
313
|
+
A single proportion bar with two tiny tail segments (98% / 1% / 1%). The
|
|
314
|
+
dominant segment labels inside; the two slivers can't fit their text, so the
|
|
315
|
+
labels overflow to the right and the overlap pass spreads them apart instead
|
|
316
|
+
of letting them pile up.
|
|
317
|
+
|
|
318
|
+
<ComponentDemo>
|
|
319
|
+
<BarChart
|
|
320
|
+
:series="[
|
|
321
|
+
{ data: [98], legend: 'Recovered', color: '#3f1f5e' },
|
|
322
|
+
{ data: [1], legend: 'Hospitalized', color: '#8a5fb0' },
|
|
323
|
+
{ data: [1], legend: 'Deceased', color: '#c9aee0' },
|
|
324
|
+
]"
|
|
325
|
+
:categories="['Outcome']"
|
|
326
|
+
orientation="horizontal"
|
|
327
|
+
layout="stacked"
|
|
328
|
+
:value-axis="false"
|
|
329
|
+
:height="120"
|
|
330
|
+
:chart-padding="{ left: 60, right: 40 }"
|
|
331
|
+
:bar-labels="{ format: (v) => `${v}%` }"
|
|
332
|
+
/>
|
|
333
|
+
|
|
334
|
+
<template #code>
|
|
335
|
+
|
|
336
|
+
```vue
|
|
337
|
+
<BarChart
|
|
338
|
+
:series="[
|
|
339
|
+
{ data: [98], legend: 'Recovered', color: '#3f1f5e' },
|
|
340
|
+
{ data: [1], legend: 'Hospitalized', color: '#8a5fb0' },
|
|
341
|
+
{ data: [1], legend: 'Deceased', color: '#c9aee0' },
|
|
342
|
+
]"
|
|
343
|
+
:categories="['Outcome']"
|
|
344
|
+
orientation="horizontal"
|
|
345
|
+
layout="stacked"
|
|
346
|
+
:value-axis="false"
|
|
347
|
+
:height="120"
|
|
348
|
+
:chart-padding="{ left: 60, right: 40 }"
|
|
349
|
+
:bar-labels="{ format: (v) => `${v}%` }"
|
|
350
|
+
/>
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
</template>
|
|
354
|
+
</ComponentDemo>
|
|
355
|
+
|
|
356
|
+
### Custom value tick format
|
|
357
|
+
|
|
358
|
+
Use `value-tick-format` to format the value-axis labels. `tooltip-value-format` controls the tooltip values independently; if omitted, the tooltip uses `value-tick-format`.
|
|
359
|
+
|
|
360
|
+
<ComponentDemo>
|
|
361
|
+
<BarChart
|
|
362
|
+
:data="[0.12, 0.34, 0.56, 0.78]"
|
|
363
|
+
:categories="['A', 'B', 'C', 'D']"
|
|
364
|
+
:value-tick-format="(v) => `${(v * 100).toFixed(0)}%`"
|
|
365
|
+
:tooltip-value-format="(v) => `${(v * 100).toFixed(1)}%`"
|
|
366
|
+
:height="220"
|
|
367
|
+
y-label="Coverage"
|
|
368
|
+
tooltip-trigger="hover"
|
|
369
|
+
/>
|
|
370
|
+
|
|
371
|
+
<template #code>
|
|
372
|
+
|
|
373
|
+
```vue
|
|
374
|
+
<BarChart
|
|
375
|
+
:data="[0.12, 0.34, 0.56, 0.78]"
|
|
376
|
+
:categories="['A', 'B', 'C', 'D']"
|
|
377
|
+
:value-tick-format="(v) => `${(v * 100).toFixed(0)}%`"
|
|
378
|
+
:tooltip-value-format="(v) => `${(v * 100).toFixed(1)}%`"
|
|
379
|
+
:height="220"
|
|
380
|
+
y-label="Coverage"
|
|
381
|
+
tooltip-trigger="hover"
|
|
382
|
+
/>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
</template>
|
|
386
|
+
</ComponentDemo>
|
|
387
|
+
|
|
388
|
+
### Date categories
|
|
389
|
+
|
|
390
|
+
When every entry of `categories` parses as a date (`YYYY-MM-DD` string
|
|
391
|
+
or `Date` instance), labels switch to date-aware formatting and the
|
|
392
|
+
chart thins them to clean date boundaries (week / month / year
|
|
393
|
+
depending on range). Bar positions stay ordinal — every category gets
|
|
394
|
+
a bar — only the labels are thinned.
|
|
395
|
+
|
|
396
|
+
<ComponentDemo>
|
|
397
|
+
<BarChart
|
|
398
|
+
:data="[12, 18, 24, 35, 41, 38, 29, 21, 14, 9, 7, 4]"
|
|
399
|
+
:categories="['2026-01-05','2026-01-12','2026-01-19','2026-01-26','2026-02-02','2026-02-09','2026-02-16','2026-02-23','2026-03-02','2026-03-09','2026-03-16','2026-03-23']"
|
|
400
|
+
:height="220"
|
|
401
|
+
y-label="% ED visits"
|
|
402
|
+
/>
|
|
403
|
+
|
|
404
|
+
<template #code>
|
|
405
|
+
|
|
406
|
+
```vue
|
|
407
|
+
<BarChart
|
|
408
|
+
:data="[12, 18, 24, 35, 41, 38, 29, 21, 14, 9, 7, 4]"
|
|
409
|
+
:categories="[
|
|
410
|
+
'2026-01-05',
|
|
411
|
+
'2026-01-12',
|
|
412
|
+
'2026-01-19',
|
|
413
|
+
'2026-01-26',
|
|
414
|
+
'2026-02-02',
|
|
415
|
+
'2026-02-09',
|
|
416
|
+
'2026-02-16',
|
|
417
|
+
'2026-02-23',
|
|
418
|
+
'2026-03-02',
|
|
419
|
+
'2026-03-09',
|
|
420
|
+
'2026-03-16',
|
|
421
|
+
'2026-03-23',
|
|
422
|
+
]"
|
|
423
|
+
:height="220"
|
|
424
|
+
y-label="% ED visits"
|
|
425
|
+
/>
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
</template>
|
|
429
|
+
</ComponentDemo>
|
|
430
|
+
|
|
431
|
+
Use `date-format` to override the auto-picked preset. Same values as
|
|
432
|
+
LineChart's `x-tick-format` (e.g. `"iso"`, `"month-year"`, an
|
|
433
|
+
`Intl.DateTimeFormatOptions` object, or a `(ms, unit?) => string`
|
|
434
|
+
function).
|
|
435
|
+
|
|
436
|
+
### Crowded categorical labels
|
|
437
|
+
|
|
438
|
+
Non-date categorical labels are also thinned when they don't all fit:
|
|
439
|
+
the chart estimates label width from character count and font size,
|
|
440
|
+
then strides through the categories so adjacent labels never overlap.
|
|
441
|
+
Every bar still renders — only the tick labels are skipped.
|
|
442
|
+
|
|
443
|
+
<ComponentDemo>
|
|
444
|
+
<BarChart
|
|
445
|
+
data-testid="thinned-bar-chart"
|
|
446
|
+
:data="[3, 5, 8, 12, 18, 24, 31, 27, 22, 16, 11, 7, 4, 2]"
|
|
447
|
+
:categories="['2.11', '2.33', '2.56', '2.78', '3.00', '3.22', '3.44', '3.67', '3.89', '4.11', '4.33', '4.56', '4.78', '5.00']"
|
|
448
|
+
:width="380"
|
|
449
|
+
:height="220"
|
|
450
|
+
x-label="R₀"
|
|
451
|
+
y-label="Frequency"
|
|
452
|
+
/>
|
|
453
|
+
|
|
454
|
+
<template #code>
|
|
455
|
+
|
|
456
|
+
```vue
|
|
457
|
+
<BarChart
|
|
458
|
+
:data="[3, 5, 8, 12, 18, 24, 31, 27, 22, 16, 11, 7, 4, 2]"
|
|
459
|
+
:categories="[
|
|
460
|
+
'2.11',
|
|
461
|
+
'2.33',
|
|
462
|
+
'2.56',
|
|
463
|
+
'2.78',
|
|
464
|
+
'3.00',
|
|
465
|
+
'3.22',
|
|
466
|
+
'3.44',
|
|
467
|
+
'3.67',
|
|
468
|
+
'3.89',
|
|
469
|
+
'4.11',
|
|
470
|
+
'4.33',
|
|
471
|
+
'4.56',
|
|
472
|
+
'4.78',
|
|
473
|
+
'5.00',
|
|
474
|
+
]"
|
|
475
|
+
:height="220"
|
|
476
|
+
x-label="R₀"
|
|
477
|
+
y-label="Frequency"
|
|
478
|
+
/>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
</template>
|
|
482
|
+
</ComponentDemo>
|
|
483
|
+
|
|
484
|
+
### Logarithmic value axis
|
|
485
|
+
|
|
486
|
+
Set `value-scale-type="log"` to switch the value axis to base-10 log
|
|
487
|
+
scaling. Non-positive values collapse to the axis floor, and
|
|
488
|
+
`valueMin <= 0` is ignored. With `layout="stacked"`, individual segment
|
|
489
|
+
sizes are no longer proportional to their raw values, but the cumulative
|
|
490
|
+
top still represents the sum.
|
|
491
|
+
|
|
492
|
+
<ComponentDemo>
|
|
493
|
+
<BarChart
|
|
494
|
+
:data="[3, 24, 180, 1450, 9800]"
|
|
495
|
+
:categories="['Wk1', 'Wk2', 'Wk3', 'Wk4', 'Wk5']"
|
|
496
|
+
value-scale-type="log"
|
|
497
|
+
:height="220"
|
|
498
|
+
x-label="Week"
|
|
499
|
+
y-label="Cases"
|
|
500
|
+
/>
|
|
501
|
+
|
|
502
|
+
<template #code>
|
|
503
|
+
|
|
504
|
+
```vue
|
|
505
|
+
<BarChart
|
|
506
|
+
:data="[3, 24, 180, 1450, 9800]"
|
|
507
|
+
:categories="['Wk1', 'Wk2', 'Wk3', 'Wk4', 'Wk5']"
|
|
508
|
+
value-scale-type="log"
|
|
509
|
+
:height="220"
|
|
510
|
+
x-label="Week"
|
|
511
|
+
y-label="Cases"
|
|
512
|
+
/>
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
</template>
|
|
516
|
+
</ComponentDemo>
|
|
517
|
+
|
|
518
|
+
### Summary line overlay
|
|
519
|
+
|
|
520
|
+
Layer a curve (KDE, rolling mean, target) on top of the bars with
|
|
521
|
+
`summary-lines`. Each line scales to **its own value extent** —
|
|
522
|
+
independent of the bar axis — so a probability-density curve in
|
|
523
|
+
`[0, 0.02]` and a histogram in `[0, 500]` can share the same chart.
|
|
524
|
+
|
|
525
|
+
<ComponentDemo>
|
|
526
|
+
<BarChart
|
|
527
|
+
:data="[3, 5, 8, 12, 18, 24, 31, 27, 22, 16, 11, 7, 4, 2]"
|
|
528
|
+
:categories="['2.11', '2.33', '2.56', '2.78', '3.00', '3.22', '3.44', '3.67', '3.89', '4.11', '4.33', '4.56', '4.78', '5.00']"
|
|
529
|
+
:summary-lines="[
|
|
530
|
+
{
|
|
531
|
+
data: [0.05, 0.10, 0.20, 0.40, 0.75, 1.10, 1.32, 1.20, 0.92, 0.60, 0.32, 0.16, 0.07, 0.03],
|
|
532
|
+
color: '#ef4444',
|
|
533
|
+
strokeWidth: 2,
|
|
534
|
+
legend: 'KDE',
|
|
535
|
+
},
|
|
536
|
+
]"
|
|
537
|
+
:width="380"
|
|
538
|
+
:height="240"
|
|
539
|
+
x-label="R₀"
|
|
540
|
+
y-label="Frequency"
|
|
541
|
+
/>
|
|
542
|
+
|
|
543
|
+
<template #code>
|
|
544
|
+
|
|
545
|
+
```vue
|
|
546
|
+
<BarChart
|
|
547
|
+
:data="[3, 5, 8, 12, 18, 24, 31, 27, 22, 16, 11, 7, 4, 2]"
|
|
548
|
+
:categories="[
|
|
549
|
+
'2.11',
|
|
550
|
+
'2.33',
|
|
551
|
+
'2.56',
|
|
552
|
+
'2.78',
|
|
553
|
+
'3.00',
|
|
554
|
+
'3.22',
|
|
555
|
+
'3.44',
|
|
556
|
+
'3.67',
|
|
557
|
+
'3.89',
|
|
558
|
+
'4.11',
|
|
559
|
+
'4.33',
|
|
560
|
+
'4.56',
|
|
561
|
+
'4.78',
|
|
562
|
+
'5.00',
|
|
563
|
+
]"
|
|
564
|
+
:summary-lines="[
|
|
565
|
+
{
|
|
566
|
+
data: [
|
|
567
|
+
0.05, 0.1, 0.2, 0.4, 0.75, 1.1, 1.32, 1.2, 0.92, 0.6, 0.32, 0.16, 0.07,
|
|
568
|
+
0.03,
|
|
569
|
+
],
|
|
570
|
+
color: '#ef4444',
|
|
571
|
+
strokeWidth: 2,
|
|
572
|
+
legend: 'KDE',
|
|
573
|
+
},
|
|
574
|
+
]"
|
|
575
|
+
:height="240"
|
|
576
|
+
x-label="R₀"
|
|
577
|
+
y-label="Frequency"
|
|
578
|
+
/>
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
</template>
|
|
582
|
+
</ComponentDemo>
|
|
583
|
+
|
|
584
|
+
Each line accepts `color`, `strokeWidth`, `dashed`, `opacity`,
|
|
585
|
+
`blendMode`, `dots`, and `dotRadius` for styling — same vocabulary as
|
|
586
|
+
`LineChart` series (both extend `LineMarkStyle`). Override the line's
|
|
587
|
+
extent with `valueMin` / `valueMax`. Pass `x: number[]` (in
|
|
588
|
+
category-index space — `0` is the first category center, fractional
|
|
589
|
+
values land between) to plot at custom positions; useful when the
|
|
590
|
+
summary samples are denser or sparser than the histogram bins.
|
|
591
|
+
|
|
592
|
+
### Annotations
|
|
593
|
+
|
|
594
|
+
Pin callouts to specific bars with `annotations`. `x` is the category
|
|
595
|
+
index (fractional values land between categories — e.g. `x: 1.5` sits at
|
|
596
|
+
the boundary between categories 1 and 2). `y` is on the value axis. See
|
|
597
|
+
[`LineChart` → Annotations](./line-chart#annotations) for the full
|
|
598
|
+
`ChartAnnotation` shape.
|
|
599
|
+
|
|
600
|
+
<ComponentDemo>
|
|
601
|
+
<BarChart
|
|
602
|
+
:data="[12, 19, 7, 24, 16]"
|
|
603
|
+
:categories="['Mon', 'Tue', 'Wed', 'Thu', 'Fri']"
|
|
604
|
+
:annotations="[
|
|
605
|
+
{ x: 3, y: 24, offset: { x: 18, y: -22 }, text: 'Peak day' },
|
|
606
|
+
]"
|
|
607
|
+
:chart-padding="{ top: 32, right: 32 }"
|
|
608
|
+
:height="240"
|
|
609
|
+
x-label="Day"
|
|
610
|
+
y-label="Cases"
|
|
611
|
+
/>
|
|
612
|
+
|
|
613
|
+
<template #code>
|
|
614
|
+
|
|
615
|
+
```vue
|
|
616
|
+
<BarChart
|
|
617
|
+
:data="[12, 19, 7, 24, 16]"
|
|
618
|
+
:categories="['Mon', 'Tue', 'Wed', 'Thu', 'Fri']"
|
|
619
|
+
:annotations="[{ x: 3, y: 24, offset: { x: 18, y: -22 }, text: 'Peak day' }]"
|
|
620
|
+
:chart-padding="{ top: 32, right: 32 }"
|
|
621
|
+
:height="240"
|
|
622
|
+
x-label="Day"
|
|
623
|
+
y-label="Cases"
|
|
624
|
+
/>
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
</template>
|
|
628
|
+
</ComponentDemo>
|
|
629
|
+
|
|
630
|
+
`chart-padding` reserves extra space outside the plot so the annotation
|
|
631
|
+
label and pointer don't get clipped by the data area. Pass a number for
|
|
632
|
+
uniform padding or an object with `top` / `right` / `bottom` / `left`.
|
|
633
|
+
|
|
634
|
+
Set `pointer` to a rule value (`"ruleX"`, `"ruleY"`, `"ruleUp"`,
|
|
635
|
+
`"ruleDown"`, `"ruleFromLeft"`, `"ruleFromRight"`) to draw a line
|
|
636
|
+
through the anchor instead of the default curved connector. The first
|
|
637
|
+
two span the full plot; the latter four extend from one edge to the
|
|
638
|
+
anchor. `lineColor`, `lineWidth`, and `lineDash` style the line.
|
|
639
|
+
|
|
640
|
+
<ComponentDemo>
|
|
641
|
+
<BarChart
|
|
642
|
+
:data="[12, 19, 7, 24, 16]"
|
|
643
|
+
:categories="['Mon', 'Tue', 'Wed', 'Thu', 'Fri']"
|
|
644
|
+
:annotations="[
|
|
645
|
+
{
|
|
646
|
+
x: 0,
|
|
647
|
+
y: 15.6,
|
|
648
|
+
offset: { x: 6, y: -6 },
|
|
649
|
+
text: 'Avg 15.6',
|
|
650
|
+
pointer: 'ruleY',
|
|
651
|
+
lineDash: '4 3',
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
x: 3,
|
|
655
|
+
y: 20,
|
|
656
|
+
offset: { x: 8, y: 4 },
|
|
657
|
+
text: 'Target hit',
|
|
658
|
+
pointer: 'ruleFromLeft',
|
|
659
|
+
lineColor: '#0a7',
|
|
660
|
+
},
|
|
661
|
+
]"
|
|
662
|
+
:chart-padding="{ top: 24, right: 24 }"
|
|
663
|
+
:height="240"
|
|
664
|
+
x-label="Day"
|
|
665
|
+
y-label="Cases"
|
|
666
|
+
/>
|
|
667
|
+
|
|
668
|
+
<template #code>
|
|
669
|
+
|
|
670
|
+
```vue
|
|
671
|
+
<BarChart
|
|
672
|
+
:data="[12, 19, 7, 24, 16]"
|
|
673
|
+
:categories="['Mon', 'Tue', 'Wed', 'Thu', 'Fri']"
|
|
674
|
+
:annotations="[
|
|
675
|
+
{
|
|
676
|
+
x: 0,
|
|
677
|
+
y: 15.6,
|
|
678
|
+
offset: { x: 6, y: -6 },
|
|
679
|
+
text: 'Avg 15.6',
|
|
680
|
+
pointer: 'ruleY',
|
|
681
|
+
lineDash: '4 3',
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
x: 3,
|
|
685
|
+
y: 20,
|
|
686
|
+
offset: { x: 8, y: 4 },
|
|
687
|
+
text: 'Target hit',
|
|
688
|
+
pointer: 'ruleFromLeft',
|
|
689
|
+
lineColor: '#0a7',
|
|
690
|
+
},
|
|
691
|
+
]"
|
|
692
|
+
:chart-padding="{ top: 24, right: 24 }"
|
|
693
|
+
:height="240"
|
|
694
|
+
x-label="Day"
|
|
695
|
+
y-label="Cases"
|
|
696
|
+
/>
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
</template>
|
|
700
|
+
</ComponentDemo>
|
|
701
|
+
|
|
702
|
+
## Accessibility
|
|
703
|
+
|
|
704
|
+
The chart's `<svg>` is exposed as a single labeled image so screen readers
|
|
705
|
+
announce one accessible name instead of wandering through the individual bars.
|
|
706
|
+
When the chart has a `title`, the `<svg>` gets `role="img"` and an `aria-label`
|
|
707
|
+
set to the title. The menu and download controls live outside the `<svg>`, so
|
|
708
|
+
they stay reachable regardless.
|
|
709
|
+
|
|
710
|
+
Set `ariaLabel` to give screen readers a fuller summary than the visible title
|
|
711
|
+
(it overrides `title` for the accessible name only):
|
|
712
|
+
|
|
713
|
+
```vue
|
|
714
|
+
<BarChart
|
|
715
|
+
:data="cases"
|
|
716
|
+
:categories="regions"
|
|
717
|
+
title="Cases by region"
|
|
718
|
+
aria-label="Cases by region: North 120, South 80, West 45"
|
|
719
|
+
/>
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
Pass `role` to override the default `"img"` (e.g. `role="figure"`, or a role of
|
|
723
|
+
your own).
|
|
724
|
+
|
|
725
|
+
## API
|
|
726
|
+
|
|
727
|
+
## Props
|
|
728
|
+
|
|
729
|
+
| Prop | Type | Required | Default |
|
|
730
|
+
|------|------|----------|---------|
|
|
731
|
+
| `width` | `number` | No | — |
|
|
732
|
+
| `height` | `number` | No | — |
|
|
733
|
+
| `title` | `string` | No | — |
|
|
734
|
+
| `titleStyle` | `TitleStyle` | No | — |
|
|
735
|
+
| `role` | `string` | No | — |
|
|
736
|
+
| `ariaLabel` | `string` | No | — |
|
|
737
|
+
| `axisLabelStyle` | `LabelStyle` | No | — |
|
|
738
|
+
| `tickLabelStyle` | `LabelStyle` | No | — |
|
|
739
|
+
| `legendStyle` | `LabelStyle` | No | — |
|
|
740
|
+
| `xLabel` | `string` | No | — |
|
|
741
|
+
| `yLabel` | `string` | No | — |
|
|
742
|
+
| `debounce` | `number` | No | — |
|
|
743
|
+
| `menu` | `boolean \| string` | No | `true` |
|
|
744
|
+
| `tooltipData` | `ArrayLike<unknown>` | No | — |
|
|
745
|
+
| `tooltipTrigger` | `"hover" \| "click"` | No | — |
|
|
746
|
+
| `tooltipClamp` | `"none" \| "chart" \| "window"` | No | `"window"` |
|
|
747
|
+
| `tooltipValueFormat` | `NumberFormat` | No | — |
|
|
748
|
+
| `csv` | `string \| (() => string)` | No | — |
|
|
749
|
+
| `filename` | `string` | No | — |
|
|
750
|
+
| `downloadLink` | `boolean \| string` | No | — |
|
|
751
|
+
| `downloadButton` | `boolean \| string` | No | — |
|
|
752
|
+
| `fullscreenTarget` | `string \| HTMLElement` | No | — |
|
|
753
|
+
| `annotations` | `readonly ChartAnnotation[]` | No | — |
|
|
754
|
+
| `chartPadding` | `ChartPadding` | No | — |
|
|
755
|
+
| `data` | `BarChartData` | No | — |
|
|
756
|
+
| `y` | `BarChartData` | No | — |
|
|
757
|
+
| `series` | `BarSeries[]` | No | — |
|
|
758
|
+
| `summaryLines` | `BarSummaryLine[]` | No | — |
|
|
759
|
+
| `categories` | `readonly (string \| Date)[]` | No | — |
|
|
760
|
+
| `orientation` | `"vertical" \| "horizontal"` | No | `"vertical"` |
|
|
761
|
+
| `layout` | `"grouped" \| "stacked" \| "overlay"` | No | `"grouped"` |
|
|
762
|
+
| `valueMin` | `number` | No | `0` |
|
|
763
|
+
| `valueScaleType` | `"linear" \| "log"` | No | `"linear"` |
|
|
764
|
+
| `valueTicks` | `number \| number[]` | No | — |
|
|
765
|
+
| `valueTickFormat` | `NumberFormat` | No | — |
|
|
766
|
+
| `categoryFormat` | `(label: string, index: number) => string` | No | — |
|
|
767
|
+
| `categoryAlign` | `"start" \| "center" \| "end"` | No | — |
|
|
768
|
+
| `dateFormat` | `DateFormat` | No | — |
|
|
769
|
+
| `barPadding` | `number` | No | `0.2` |
|
|
770
|
+
| `groupGap` | `number` | No | `1` |
|
|
771
|
+
| `valueGrid` | `boolean` | No | `true` |
|
|
772
|
+
| `valueAxis` | `boolean` | No | `true` |
|
|
773
|
+
| `barLabels` | `boolean \| BarLabelStyle` | No | — |
|
|
774
|
+
| `categoryHeader` | `string` | No | — |
|
|
775
|
+
| `valueHeader` | `string` | No | — |
|
|
776
|
+
|