@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.
Files changed (67) hide show
  1. package/dist/BarChart/BarChart.d.ts +9 -26
  2. package/dist/ChartMenu/ChartMenu.d.ts +3 -2
  3. package/dist/ChartTooltip/ChartTooltip.d.ts +9 -12
  4. package/dist/ChoroplethMap/ChoroplethMap.d.ts +42 -190
  5. package/dist/ChoroplethMap/ChoroplethTooltip.d.ts +20 -27
  6. package/dist/ChoroplethMap/canvasLayer.d.ts +23 -6
  7. package/dist/ChoroplethMap/cityLayout.d.ts +3 -2
  8. package/dist/ChoroplethMap/mixedGeo.d.ts +74 -0
  9. package/dist/ChoroplethMap/mixedGeo.test.d.ts +1 -0
  10. package/dist/DataTable/DataTable.d.ts +3 -2
  11. package/dist/LineChart/LineChart.d.ts +9 -26
  12. package/dist/_shared/ChartAnnotations.d.ts +3 -2
  13. package/dist/_shared/ChartZoomControls.d.ts +3 -2
  14. package/dist/_shared/index.d.ts +2 -1
  15. package/dist/_shared/mapTheme.d.ts +159 -0
  16. package/dist/_shared/mapTheme.test.d.ts +1 -0
  17. package/dist/index.css +1 -1
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +1821 -1462
  20. package/docs/BarChart.md +776 -0
  21. package/docs/ChoroplethMap.md +1394 -0
  22. package/docs/DataTable.md +386 -0
  23. package/docs/LineChart.md +1267 -0
  24. package/docs/index.json +56 -0
  25. package/package.json +25 -21
  26. package/src/BarChart/BarChart.md +743 -0
  27. package/src/BarChart/BarChart.vue +1901 -0
  28. package/src/ChartMenu/ChartMenu.vue +220 -0
  29. package/src/ChartMenu/download.ts +120 -0
  30. package/src/ChartTooltip/ChartTooltip.vue +97 -0
  31. package/src/ChoroplethMap/ChoroplethMap.md +1354 -0
  32. package/src/ChoroplethMap/ChoroplethMap.vue +3778 -0
  33. package/src/ChoroplethMap/ChoroplethTooltip.vue +103 -0
  34. package/src/ChoroplethMap/canvasLayer.ts +373 -0
  35. package/src/ChoroplethMap/cityLayout.ts +262 -0
  36. package/src/ChoroplethMap/hsaMapping.ts +4116 -0
  37. package/src/ChoroplethMap/mixedGeo.ts +201 -0
  38. package/src/DataTable/DataTable.md +372 -0
  39. package/src/DataTable/DataTable.vue +406 -0
  40. package/src/LineChart/LineChart.md +1225 -0
  41. package/src/LineChart/LineChart.vue +1555 -0
  42. package/src/_shared/ChartAnnotations.vue +420 -0
  43. package/src/_shared/ChartZoomControls.vue +138 -0
  44. package/src/_shared/annotations.ts +106 -0
  45. package/src/_shared/axes.ts +69 -0
  46. package/src/_shared/chartProps.ts +201 -0
  47. package/src/_shared/computeTicks.ts +42 -0
  48. package/src/_shared/contrast.ts +100 -0
  49. package/src/_shared/dateAxis.ts +501 -0
  50. package/src/_shared/index.ts +78 -0
  51. package/src/_shared/mapTheme.ts +551 -0
  52. package/src/_shared/scale.ts +86 -0
  53. package/src/_shared/seriesCsv.ts +68 -0
  54. package/src/_shared/touch.ts +8 -0
  55. package/src/_shared/useChartFoundation.ts +175 -0
  56. package/src/_shared/useChartFullscreen.ts +254 -0
  57. package/src/_shared/useChartMenu.ts +111 -0
  58. package/src/_shared/useChartPadding.ts +235 -0
  59. package/src/_shared/useChartSize.ts +58 -0
  60. package/src/_shared/useChartTooltip.ts +205 -0
  61. package/src/env.d.ts +4 -0
  62. package/src/hsa-mapping.ts +1 -0
  63. package/src/index.ts +41 -0
  64. package/src/tooltip-position.ts +55 -0
  65. package/src/us-cities/data.ts +1371 -0
  66. package/src/us-cities/index.ts +122 -0
  67. package/src/us-cities.ts +7 -0
@@ -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&lt;unknown&gt;` | No | — |
745
+ | `tooltipTrigger` | `"hover" \| "click"` | No | — |
746
+ | `tooltipClamp` | `"none" \| "chart" \| "window"` | No | `"window"` |
747
+ | `tooltipValueFormat` | `NumberFormat` | No | — |
748
+ | `csv` | `string \| (() =&gt; 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) =&gt; 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
+