@cfasim-ui/docs 0.4.0 → 0.4.2
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 +8 -1
- package/charts/BarChart/BarChart.vue +18 -3
- package/charts/ChartMenu/ChartMenu.vue +11 -4
- package/charts/ChoroplethMap/ChoroplethMap.md +160 -0
- package/charts/ChoroplethMap/ChoroplethMap.vue +600 -274
- package/charts/ChoroplethMap/ChoroplethTooltip.vue +49 -0
- 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 +18 -3
- package/index.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, useTemplateRef } from "vue";
|
|
3
|
+
|
|
4
|
+
export interface ChoroplethTooltipData {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
value?: number | string;
|
|
8
|
+
feature: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
defineSlots<{
|
|
12
|
+
default?(props: ChoroplethTooltipData): unknown;
|
|
13
|
+
}>();
|
|
14
|
+
|
|
15
|
+
// Local reactive state. Held inside the child so the parent's render scope
|
|
16
|
+
// never subscribes to it — hover updates re-render only this small tree,
|
|
17
|
+
// not the parent's 3,000+ paths.
|
|
18
|
+
const data = ref<ChoroplethTooltipData | null>(null);
|
|
19
|
+
const rootRef = useTemplateRef<HTMLDivElement>("root");
|
|
20
|
+
|
|
21
|
+
defineExpose({
|
|
22
|
+
setData(next: ChoroplethTooltipData | null) {
|
|
23
|
+
data.value = next;
|
|
24
|
+
},
|
|
25
|
+
getEl(): HTMLDivElement | null {
|
|
26
|
+
return rootRef.value;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<Teleport to="body">
|
|
33
|
+
<div
|
|
34
|
+
ref="root"
|
|
35
|
+
class="chart-tooltip-content"
|
|
36
|
+
style="
|
|
37
|
+
position: fixed;
|
|
38
|
+
left: 0;
|
|
39
|
+
top: 0;
|
|
40
|
+
visibility: hidden;
|
|
41
|
+
will-change: transform;
|
|
42
|
+
pointer-events: none;
|
|
43
|
+
transform: translateY(-50%);
|
|
44
|
+
"
|
|
45
|
+
>
|
|
46
|
+
<slot v-if="data" v-bind="data" />
|
|
47
|
+
</div>
|
|
48
|
+
</Teleport>
|
|
49
|
+
</template>
|
|
@@ -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 | — |
|
|
@@ -128,6 +128,11 @@ const props = withDefaults(
|
|
|
128
128
|
xTickFormat?: (value: number, index: number) => string;
|
|
129
129
|
/** Formatter for y-axis tick labels. Receives the raw numeric value. */
|
|
130
130
|
yTickFormat?: (value: number) => string;
|
|
131
|
+
/**
|
|
132
|
+
* Formatter for numeric values shown in the default tooltip. Receives
|
|
133
|
+
* the raw value. Defaults to the same tick formatter used for axes.
|
|
134
|
+
*/
|
|
135
|
+
tooltipValueFormat?: (value: number) => string;
|
|
131
136
|
/**
|
|
132
137
|
* @deprecated Use `xTickFormat` (e.g. `(_, i) => labels[i]`) together
|
|
133
138
|
* with `xTicks` for explicit control. Still honored for tooltip x-labels
|
|
@@ -138,8 +143,12 @@ const props = withDefaults(
|
|
|
138
143
|
menu?: boolean | string;
|
|
139
144
|
xGrid?: boolean;
|
|
140
145
|
yGrid?: boolean;
|
|
141
|
-
/**
|
|
142
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Custom per-index data passed to the tooltip slot. Accepts a plain
|
|
148
|
+
* array or any `ArrayLike` (e.g. a typed array column from a
|
|
149
|
+
* `ModelOutput`).
|
|
150
|
+
*/
|
|
151
|
+
tooltipData?: ArrayLike<unknown>;
|
|
143
152
|
/** Tooltip activation mode. Default: 'hover' */
|
|
144
153
|
tooltipTrigger?: "hover" | "click";
|
|
145
154
|
/**
|
|
@@ -216,6 +225,12 @@ function resolveSeries(s: Series): ResolvedSeries {
|
|
|
216
225
|
return { ...s, data: s.y ?? s.data ?? EMPTY_DATA };
|
|
217
226
|
}
|
|
218
227
|
|
|
228
|
+
function formatTooltipValue(v: number): string {
|
|
229
|
+
if (props.tooltipValueFormat) return props.tooltipValueFormat(v);
|
|
230
|
+
if (props.yTickFormat) return props.yTickFormat(v);
|
|
231
|
+
return formatTick(v);
|
|
232
|
+
}
|
|
233
|
+
|
|
219
234
|
const allSeries = computed<ResolvedSeries[]>(() => {
|
|
220
235
|
if (props.series && props.series.length > 0)
|
|
221
236
|
return props.series.map(resolveSeries);
|
|
@@ -1122,7 +1137,7 @@ const {
|
|
|
1122
1137
|
class="line-chart-tooltip-swatch"
|
|
1123
1138
|
:style="{ background: v.color }"
|
|
1124
1139
|
/>
|
|
1125
|
-
{{ isFinite(v.value) ?
|
|
1140
|
+
{{ isFinite(v.value) ? formatTooltipValue(v.value) : "—" }}
|
|
1126
1141
|
</div>
|
|
1127
1142
|
</div>
|
|
1128
1143
|
</slot>
|
package/index.json
CHANGED