@cfasim-ui/docs 0.3.18 → 0.4.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/charts/BarChart/BarChart.md +196 -0
- package/charts/BarChart/BarChart.vue +844 -0
- package/charts/ChartMenu/ChartMenu.vue +11 -4
- package/charts/ChoroplethMap/ChoroplethMap.md +42 -0
- package/charts/ChoroplethMap/ChoroplethMap.vue +22 -2
- package/charts/DataTable/DataTable.md +39 -9
- package/charts/DataTable/DataTable.vue +45 -59
- package/charts/LineChart/LineChart.md +3 -2
- package/charts/LineChart/LineChart.vue +86 -291
- package/charts/_shared/axes.ts +69 -0
- package/charts/_shared/computeTicks.ts +42 -0
- package/charts/_shared/index.ts +20 -0
- package/charts/_shared/seriesCsv.ts +68 -0
- package/charts/_shared/useChartMenu.ts +72 -0
- package/charts/_shared/useChartPadding.ts +37 -0
- package/charts/_shared/useChartSize.ts +49 -0
- package/charts/_shared/useChartTooltip.ts +152 -0
- package/charts/index.ts +5 -0
- package/index.json +18 -1
- package/package.json +1 -1
- package/pyodide/index.ts +2 -0
- package/pyodide/pyodide.worker.ts +109 -72
- package/pyodide/pyodideWorkerApi.ts +157 -63
- package/pyodide/useModel.ts +10 -21
|
@@ -12,16 +12,23 @@ export interface ChartMenuItem {
|
|
|
12
12
|
action: () => void;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const props = withDefaults(
|
|
16
|
+
defineProps<{
|
|
17
|
+
items: ChartMenuItem[];
|
|
18
|
+
/** Force the dropdown style even when only one item is provided. */
|
|
19
|
+
forceDropdown?: boolean;
|
|
20
|
+
}>(),
|
|
21
|
+
{ forceDropdown: false },
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const useDropdown = () => props.forceDropdown || props.items.length > 1;
|
|
18
25
|
</script>
|
|
19
26
|
|
|
20
27
|
<template>
|
|
21
28
|
<div class="chart-menu-trigger-area">
|
|
22
29
|
<!-- Single item: plain button -->
|
|
23
30
|
<button
|
|
24
|
-
v-if="
|
|
31
|
+
v-if="!useDropdown()"
|
|
25
32
|
class="chart-menu-button chart-menu-single"
|
|
26
33
|
:aria-label="items[0].label"
|
|
27
34
|
@click="items[0].action"
|
|
@@ -322,6 +322,47 @@ Set `geoType="hsas"` to render Health Service Area boundaries. HSAs are dissolve
|
|
|
322
322
|
</template>
|
|
323
323
|
</ComponentDemo>
|
|
324
324
|
|
|
325
|
+
### Custom tooltip number format
|
|
326
|
+
|
|
327
|
+
Pass `tooltip-value-format` to format numeric values shown in the tooltip
|
|
328
|
+
(both the native SVG `<title>` and the interactive HTML tooltip when
|
|
329
|
+
`tooltip-trigger` is set). Use `tooltip-format` instead if you want full
|
|
330
|
+
control over the tooltip's HTML.
|
|
331
|
+
|
|
332
|
+
<ComponentDemo>
|
|
333
|
+
<ChoroplethMap
|
|
334
|
+
:topology="statesTopo"
|
|
335
|
+
:data="[
|
|
336
|
+
{ id: '06', value: 39538223 },
|
|
337
|
+
{ id: '48', value: 29145505 },
|
|
338
|
+
{ id: '12', value: 21538187 },
|
|
339
|
+
{ id: '36', value: 20201249 },
|
|
340
|
+
]"
|
|
341
|
+
:tooltip-value-format="(v) => v.toLocaleString('en-US')"
|
|
342
|
+
title="US population (2020)"
|
|
343
|
+
:height="300"
|
|
344
|
+
/>
|
|
345
|
+
|
|
346
|
+
<template #code>
|
|
347
|
+
|
|
348
|
+
```vue
|
|
349
|
+
<ChoroplethMap
|
|
350
|
+
:topology="statesTopo"
|
|
351
|
+
:data="[
|
|
352
|
+
{ id: '06', value: 39538223 },
|
|
353
|
+
{ id: '48', value: 29145505 },
|
|
354
|
+
{ id: '12', value: 21538187 },
|
|
355
|
+
{ id: '36', value: 20201249 },
|
|
356
|
+
]"
|
|
357
|
+
:tooltip-value-format="(v) => v.toLocaleString('en-US')"
|
|
358
|
+
title="US population (2020)"
|
|
359
|
+
:height="300"
|
|
360
|
+
/>
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
</template>
|
|
364
|
+
</ComponentDemo>
|
|
365
|
+
|
|
325
366
|
## Props
|
|
326
367
|
|
|
327
368
|
| Prop | Type | Required | Default |
|
|
@@ -346,6 +387,7 @@ Set `geoType="hsas"` to render Health Service Area boundaries. HSAs are dissolve
|
|
|
346
387
|
id: string` | No | — |
|
|
347
388
|
| `name` | `string` | Yes | — |
|
|
348
389
|
| `value` | `number \| string` | No | — |
|
|
390
|
+
| `tooltipValueFormat` | `(value: number) => string` | No | — |
|
|
349
391
|
| `tooltipClamp` | `"none" \| "chart" \| "window"` | No | `"chart"` |
|
|
350
392
|
|
|
351
393
|
|
|
@@ -82,6 +82,12 @@ const props = withDefaults(
|
|
|
82
82
|
name: string;
|
|
83
83
|
value?: number | string;
|
|
84
84
|
}) => string;
|
|
85
|
+
/**
|
|
86
|
+
* Formatter for numeric values shown in the default tooltip. Receives
|
|
87
|
+
* the raw value. Ignored when `tooltipFormat` is provided (the caller
|
|
88
|
+
* controls the entire tooltip in that case).
|
|
89
|
+
*/
|
|
90
|
+
tooltipValueFormat?: (value: number) => string;
|
|
85
91
|
/**
|
|
86
92
|
* Boundary for tooltip flip/clamp. `"none"` always places to the right of
|
|
87
93
|
* the pointer with no clamping. `"chart"` (default) uses the map
|
|
@@ -392,6 +398,14 @@ function stateValue(
|
|
|
392
398
|
return dataMap.value.get(String(feat.id));
|
|
393
399
|
}
|
|
394
400
|
|
|
401
|
+
function formatTooltipValue(value: number | string | undefined): string {
|
|
402
|
+
if (value == null) return "";
|
|
403
|
+
if (typeof value === "number" && props.tooltipValueFormat) {
|
|
404
|
+
return props.tooltipValueFormat(value);
|
|
405
|
+
}
|
|
406
|
+
return String(value);
|
|
407
|
+
}
|
|
408
|
+
|
|
395
409
|
const featMap = computed(() => {
|
|
396
410
|
const m = new Map<string, (typeof featuresGeo.value.features)[number]>();
|
|
397
411
|
for (const f of featuresGeo.value.features) m.set(String(f.id), f);
|
|
@@ -429,8 +443,10 @@ function showTooltip(
|
|
|
429
443
|
const data = { id: String(feat.id), name, value };
|
|
430
444
|
if (props.tooltipFormat) {
|
|
431
445
|
tooltipEl.innerHTML = props.tooltipFormat(data);
|
|
446
|
+
} else if (value == null) {
|
|
447
|
+
tooltipEl.textContent = name;
|
|
432
448
|
} else {
|
|
433
|
-
tooltipEl.textContent =
|
|
449
|
+
tooltipEl.textContent = `${name}: ${formatTooltipValue(value)}`;
|
|
434
450
|
}
|
|
435
451
|
const chartRect = containerRef.value?.getBoundingClientRect();
|
|
436
452
|
const { left, top } = placeTooltip(
|
|
@@ -643,7 +659,11 @@ const menuItems = computed<ChartMenuItem[]>(() => {
|
|
|
643
659
|
>
|
|
644
660
|
<title v-if="!tooltipTrigger">
|
|
645
661
|
{{ stateName(feat)
|
|
646
|
-
}}{{
|
|
662
|
+
}}{{
|
|
663
|
+
stateValue(feat) != null
|
|
664
|
+
? `: ${formatTooltipValue(stateValue(feat))}`
|
|
665
|
+
: ""
|
|
666
|
+
}}
|
|
647
667
|
</title>
|
|
648
668
|
</path>
|
|
649
669
|
<path
|
|
@@ -87,19 +87,48 @@ A table for displaying columnar data. Accepts a plain record of arrays or a `Mod
|
|
|
87
87
|
</template>
|
|
88
88
|
</ComponentDemo>
|
|
89
89
|
|
|
90
|
-
###
|
|
90
|
+
### Full width
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
By default the table sizes to its content (columns default to a fixed
|
|
93
|
+
medium width, so they're evenly spaced). Pass `full-width` to stretch the
|
|
94
|
+
table to fill its container; columns without an explicit width will share
|
|
95
|
+
the available space equally.
|
|
96
|
+
|
|
97
|
+
<ComponentDemo>
|
|
98
|
+
<DataTable
|
|
99
|
+
:data="{ day: [0, 1, 2, 3, 4], susceptible: [1000, 980, 945, 900, 860], infected: [1, 21, 56, 101, 141] }"
|
|
100
|
+
full-width
|
|
101
|
+
/>
|
|
102
|
+
|
|
103
|
+
<template #code>
|
|
104
|
+
|
|
105
|
+
```vue
|
|
106
|
+
<DataTable
|
|
107
|
+
:data="{
|
|
108
|
+
day: [0, 1, 2, 3, 4],
|
|
109
|
+
susceptible: [1000, 980, 945, 900, 860],
|
|
110
|
+
infected: [1, 21, 56, 101, 141],
|
|
111
|
+
}"
|
|
112
|
+
full-width
|
|
113
|
+
/>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
</template>
|
|
117
|
+
</ComponentDemo>
|
|
118
|
+
|
|
119
|
+
### Download menu
|
|
120
|
+
|
|
121
|
+
A `⋯` menu appears in the top-right corner of every table with a
|
|
122
|
+
**Download** item that exports the data as CSV. Use `download-menu-link`
|
|
123
|
+
to customize the menu item label, `filename` to control the downloaded
|
|
124
|
+
filename, and `csv` to supply custom CSV content. Pass `:menu="false"`
|
|
125
|
+
to hide the menu entirely.
|
|
97
126
|
|
|
98
127
|
<ComponentDemo>
|
|
99
128
|
<DataTable
|
|
100
129
|
:data="{ day: [0, 1, 2, 3, 4], cases: [1, 21, 56, 101, 141] }"
|
|
101
130
|
filename="sir-cases"
|
|
102
|
-
download-link="Download cases (CSV)"
|
|
131
|
+
download-menu-link="Download cases (CSV)"
|
|
103
132
|
/>
|
|
104
133
|
|
|
105
134
|
<template #code>
|
|
@@ -111,7 +140,7 @@ custom CSV content.
|
|
|
111
140
|
cases: [1, 21, 56, 101, 141],
|
|
112
141
|
}"
|
|
113
142
|
filename="sir-cases"
|
|
114
|
-
download-link="Download cases (CSV)"
|
|
143
|
+
download-menu-link="Download cases (CSV)"
|
|
115
144
|
/>
|
|
116
145
|
```
|
|
117
146
|
|
|
@@ -128,7 +157,8 @@ custom CSV content.
|
|
|
128
157
|
| `menu` | `boolean \| string` | No | `true` |
|
|
129
158
|
| `csv` | `string \| (() => string)` | No | — |
|
|
130
159
|
| `filename` | `string` | No | — |
|
|
131
|
-
| `
|
|
160
|
+
| `downloadMenuLink` | `string` | No | `"Download"` |
|
|
161
|
+
| `fullWidth` | `boolean` | No | `false` |
|
|
132
162
|
|
|
133
163
|
|
|
134
164
|
### ColumnConfig
|
|
@@ -31,22 +31,22 @@ const props = withDefaults(
|
|
|
31
31
|
columnConfig?: Record<string, ColumnConfig>;
|
|
32
32
|
menu?: boolean | string;
|
|
33
33
|
/**
|
|
34
|
-
* Custom CSV content for the Download
|
|
35
|
-
*
|
|
36
|
-
*
|
|
34
|
+
* Custom CSV content for the Download menu item. Can be a raw CSV string
|
|
35
|
+
* or a function returning one. When omitted, CSV is generated from the
|
|
36
|
+
* table data.
|
|
37
37
|
*/
|
|
38
38
|
csv?: string | (() => string);
|
|
39
39
|
/** Filename (without extension) for downloaded CSV files. */
|
|
40
40
|
filename?: string;
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* to customize the link text. When set, the Download CSV menu item is
|
|
45
|
-
* hidden.
|
|
42
|
+
* Label for the Download item in the table's top-right menu.
|
|
43
|
+
* Defaults to "Download".
|
|
46
44
|
*/
|
|
47
|
-
|
|
45
|
+
downloadMenuLink?: string;
|
|
46
|
+
/** Stretch the table to fill its container's width. */
|
|
47
|
+
fullWidth?: boolean;
|
|
48
48
|
}>(),
|
|
49
|
-
{ menu: true },
|
|
49
|
+
{ menu: true, fullWidth: false, downloadMenuLink: "Download" },
|
|
50
50
|
);
|
|
51
51
|
|
|
52
52
|
function columnLabel(name: string): string {
|
|
@@ -55,7 +55,10 @@ function columnLabel(name: string): string {
|
|
|
55
55
|
|
|
56
56
|
function columnStyle(name: string): Record<string, string> | undefined {
|
|
57
57
|
const w = props.columnConfig?.[name]?.width;
|
|
58
|
-
if (w == null)
|
|
58
|
+
if (w == null) {
|
|
59
|
+
if (props.fullWidth) return undefined;
|
|
60
|
+
return { width: COLUMN_WIDTHS.medium, minWidth: COLUMN_WIDTHS.medium };
|
|
61
|
+
}
|
|
59
62
|
const value = typeof w === "number" ? `${w}px` : COLUMN_WIDTHS[w];
|
|
60
63
|
return { width: value, minWidth: value };
|
|
61
64
|
}
|
|
@@ -135,36 +138,24 @@ function toCsv(): string {
|
|
|
135
138
|
return lines.join("\n");
|
|
136
139
|
}
|
|
137
140
|
|
|
138
|
-
const menuItems = computed<ChartMenuItem[]>(() =>
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
},
|
|
145
|
-
];
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
const downloadLinkText = computed(() => {
|
|
149
|
-
if (!props.downloadLink) return null;
|
|
150
|
-
return typeof props.downloadLink === "string"
|
|
151
|
-
? props.downloadLink
|
|
152
|
-
: "Download data (CSV)";
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
const csvHref = computed(() => {
|
|
156
|
-
if (!props.downloadLink) return null;
|
|
157
|
-
return `data:text/csv;charset=utf-8,${encodeURIComponent(toCsv())}`;
|
|
158
|
-
});
|
|
141
|
+
const menuItems = computed<ChartMenuItem[]>(() => [
|
|
142
|
+
{
|
|
143
|
+
label: props.downloadMenuLink,
|
|
144
|
+
action: () => downloadCsv(toCsv(), menuFilename()),
|
|
145
|
+
},
|
|
146
|
+
]);
|
|
159
147
|
|
|
160
|
-
const showMenu = computed(() => props.menu
|
|
148
|
+
const showMenu = computed(() => Boolean(props.menu));
|
|
161
149
|
</script>
|
|
162
150
|
|
|
163
151
|
<template>
|
|
164
|
-
<div
|
|
165
|
-
|
|
152
|
+
<div
|
|
153
|
+
class="TableOuter"
|
|
154
|
+
:class="{ 'full-width': fullWidth, 'has-menu': showMenu }"
|
|
155
|
+
>
|
|
156
|
+
<ChartMenu v-if="showMenu" :items="menuItems" force-dropdown />
|
|
166
157
|
<div class="TableWrapper">
|
|
167
|
-
<table class="Table">
|
|
158
|
+
<table class="Table" :class="{ 'full-width': fullWidth }">
|
|
168
159
|
<colgroup>
|
|
169
160
|
<col
|
|
170
161
|
v-for="col in columns"
|
|
@@ -197,14 +188,6 @@ const showMenu = computed(() => props.menu && menuItems.value.length > 0);
|
|
|
197
188
|
</tbody>
|
|
198
189
|
</table>
|
|
199
190
|
</div>
|
|
200
|
-
<a
|
|
201
|
-
v-if="downloadLinkText"
|
|
202
|
-
class="data-table-download-link"
|
|
203
|
-
:href="csvHref!"
|
|
204
|
-
:download="`${menuFilename()}.csv`"
|
|
205
|
-
>
|
|
206
|
-
{{ downloadLinkText }}
|
|
207
|
-
</a>
|
|
208
191
|
</div>
|
|
209
192
|
</template>
|
|
210
193
|
|
|
@@ -214,17 +197,8 @@ const showMenu = computed(() => props.menu && menuItems.value.length > 0);
|
|
|
214
197
|
display: inline-block;
|
|
215
198
|
}
|
|
216
199
|
|
|
217
|
-
.TableOuter.
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.TableOuter :deep(.chart-menu-trigger-area) {
|
|
222
|
-
top: -32px;
|
|
223
|
-
right: 0;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
.TableOuter:hover :deep(.chart-menu-button) {
|
|
227
|
-
opacity: 1;
|
|
200
|
+
.TableOuter.full-width {
|
|
201
|
+
display: block;
|
|
228
202
|
}
|
|
229
203
|
|
|
230
204
|
.TableWrapper {
|
|
@@ -238,6 +212,11 @@ const showMenu = computed(() => props.menu && menuItems.value.length > 0);
|
|
|
238
212
|
border-collapse: collapse;
|
|
239
213
|
font-variant-numeric: tabular-nums;
|
|
240
214
|
border: 1px solid var(--color-border);
|
|
215
|
+
table-layout: fixed;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.Table.full-width {
|
|
219
|
+
width: 100%;
|
|
241
220
|
}
|
|
242
221
|
|
|
243
222
|
.Table tr,
|
|
@@ -251,6 +230,7 @@ const showMenu = computed(() => props.menu && menuItems.value.length > 0);
|
|
|
251
230
|
.Table td {
|
|
252
231
|
padding: 0.75em 1.25em;
|
|
253
232
|
white-space: nowrap;
|
|
233
|
+
text-align: left;
|
|
254
234
|
}
|
|
255
235
|
|
|
256
236
|
.Table th {
|
|
@@ -268,10 +248,16 @@ const showMenu = computed(() => props.menu && menuItems.value.length > 0);
|
|
|
268
248
|
border-bottom: none;
|
|
269
249
|
}
|
|
270
250
|
|
|
271
|
-
.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
251
|
+
.TableOuter :deep(.chart-menu-trigger-area) {
|
|
252
|
+
top: 4px;
|
|
253
|
+
right: 4px;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.TableOuter :deep(.chart-menu-button) {
|
|
257
|
+
opacity: 1;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.TableOuter.has-menu .Table thead th:last-child {
|
|
261
|
+
padding-right: 2.5em;
|
|
276
262
|
}
|
|
277
263
|
</style>
|
|
@@ -100,7 +100,7 @@ When `x` is omitted, `y`/`data` values are plotted at indices 0, 1, 2, etc.
|
|
|
100
100
|
|
|
101
101
|
### Tooltip
|
|
102
102
|
|
|
103
|
-
Hover over the chart to see a tooltip with values at each data point. Set `tooltip-trigger="hover"` to enable the built-in tooltip with crosshair and highlight dots. Use the `#tooltip` slot for custom content.
|
|
103
|
+
Hover over the chart to see a tooltip with values at each data point. Set `tooltip-trigger="hover"` to enable the built-in tooltip with crosshair and highlight dots. Use the `#tooltip` slot for custom content. Pass `tooltip-value-format` to control how numeric values render (e.g. percentages, currency); it falls back to `y-tick-format` when omitted.
|
|
104
104
|
|
|
105
105
|
<ComponentDemo>
|
|
106
106
|
<LineChart
|
|
@@ -462,12 +462,13 @@ until the user clicks Download:
|
|
|
462
462
|
| `yTicks` | `number \| number[]` | No | — |
|
|
463
463
|
| `xTickFormat` | `(value: number, index: number) => string` | No | — |
|
|
464
464
|
| `yTickFormat` | `(value: number) => string` | No | — |
|
|
465
|
+
| `tooltipValueFormat` | `(value: number) => string` | No | — |
|
|
465
466
|
| `xLabels` | `string[]` | No | — |
|
|
466
467
|
| `debounce` | `number` | No | — |
|
|
467
468
|
| `menu` | `boolean \| string` | No | `true` |
|
|
468
469
|
| `xGrid` | `boolean` | No | — |
|
|
469
470
|
| `yGrid` | `boolean` | No | — |
|
|
470
|
-
| `tooltipData` | `unknown
|
|
471
|
+
| `tooltipData` | `ArrayLike<unknown>` | No | — |
|
|
471
472
|
| `tooltipTrigger` | `"hover" \| "click"` | No | — |
|
|
472
473
|
| `tooltipClamp` | `"none" \| "chart" \| "window"` | No | `"chart"` |
|
|
473
474
|
| `csv` | `string \| (() => string)` | No | — |
|