nuxt-charts 3.0.0 → 3.1.0
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/README.md +53 -6
- package/dist/module.json +1 -1
- package/dist/module.mjs +56 -3
- package/dist/runtime/assets/components.css +1 -1
- package/dist/runtime/assets/theme.css +1 -1
- package/dist/runtime/components/AreaChart.vue +1 -0
- package/dist/runtime/components/BarChart.vue +1 -0
- package/dist/runtime/components/DataChart.vue +91 -0
- package/dist/runtime/components/DataChart.vue.d.ts +27 -0
- package/dist/runtime/components/DottedMap.vue +261 -0
- package/dist/runtime/components/DottedMap.vue.d.ts +45 -0
- package/dist/runtime/components/LineChart.vue +1 -0
- package/dist/runtime/components/MetricChart.vue +322 -0
- package/dist/runtime/components/MetricChart.vue.d.ts +54 -0
- package/dist/runtime/components/TopoJSONMap.vue +273 -0
- package/dist/runtime/components/TopoJSONMap.vue.d.ts +45 -0
- package/dist/runtime/components/internal/CartesianFrame.vue +73 -4
- package/dist/runtime/components/internal/CartesianFrame.vue.d.ts +5 -0
- package/dist/runtime/components/internal/ChartAccessibility.vue.d.ts +1 -1
- package/dist/runtime/components/internal/CubeBarShape.vue.d.ts +1 -1
- package/dist/runtime/components/internal/MapCanvas.vue +297 -0
- package/dist/runtime/components/internal/MapCanvas.vue.d.ts +79 -0
- package/dist/runtime/components/internal/VariantBarShape.vue.d.ts +2 -2
- package/dist/runtime/data/world.json +182 -0
- package/dist/runtime/maps.d.ts +2 -0
- package/dist/runtime/maps.js +1 -0
- package/dist/runtime/spec/csv.d.ts +14 -0
- package/dist/runtime/spec/csv.js +13 -0
- package/dist/runtime/spec/index.d.ts +11 -0
- package/dist/runtime/spec/index.js +5 -0
- package/dist/runtime/spec/metric.d.ts +130 -0
- package/dist/runtime/spec/metric.js +63 -0
- package/dist/runtime/spec/resolve.d.ts +44 -0
- package/dist/runtime/spec/resolve.js +41 -0
- package/dist/runtime/spec/transform.d.ts +58 -0
- package/dist/runtime/spec/transform.js +199 -0
- package/dist/runtime/spec/types.d.ts +132 -0
- package/dist/runtime/spec/types.js +2 -0
- package/dist/runtime/types/charts.d.ts +131 -0
- package/dist/runtime/types/maps.d.ts +237 -0
- package/dist/runtime/types/maps.js +0 -0
- package/dist/runtime/types.d.ts +3 -0
- package/dist/runtime/utils/axis.d.ts +18 -0
- package/dist/runtime/utils/axis.js +44 -1
- package/dist/runtime/utils/dottedMap.d.ts +14 -0
- package/dist/runtime/utils/dottedMap.js +69 -0
- package/dist/runtime/utils/maps.d.ts +33 -0
- package/dist/runtime/utils/maps.js +77 -0
- package/package.json +23 -3
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { DottedMapProps, MapDot, MapPin, MapZoom, MapMarkSlot, MapOverlaySlot, DottedMapTooltipSlot } from "../types/maps.js";
|
|
2
|
+
type __VLS_Slots = {
|
|
3
|
+
tooltip?: (props: DottedMapTooltipSlot) => unknown;
|
|
4
|
+
pin?: (props: MapMarkSlot<MapPin> & {
|
|
5
|
+
points: MapDot[];
|
|
6
|
+
}) => unknown;
|
|
7
|
+
dot?: (props: MapMarkSlot<MapDot> & {
|
|
8
|
+
points: MapDot[];
|
|
9
|
+
}) => unknown;
|
|
10
|
+
legend?: (props: {
|
|
11
|
+
items: Array<{
|
|
12
|
+
value: string;
|
|
13
|
+
color?: string;
|
|
14
|
+
}>;
|
|
15
|
+
}) => unknown;
|
|
16
|
+
default?: (props: MapOverlaySlot & {
|
|
17
|
+
points: MapDot[];
|
|
18
|
+
}) => unknown;
|
|
19
|
+
};
|
|
20
|
+
declare const __VLS_base: import("vue").DefineComponent<DottedMapProps, {
|
|
21
|
+
zoomIn: () => void | undefined;
|
|
22
|
+
zoomOut: () => void | undefined;
|
|
23
|
+
zoomTo: (k: number) => void | undefined;
|
|
24
|
+
resetView: () => void | undefined;
|
|
25
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
26
|
+
mouseenter: (values: MapPin | MapDot, event: MouseEvent | FocusEvent) => any;
|
|
27
|
+
mouseleave: (values: MapPin | MapDot, event: MouseEvent | FocusEvent) => any;
|
|
28
|
+
zoom: (transform: MapZoom) => any;
|
|
29
|
+
"pin-click": (pin: MapPin, event: MouseEvent | KeyboardEvent) => any;
|
|
30
|
+
"point-click": (event: MouseEvent | KeyboardEvent, point: MapDot) => any;
|
|
31
|
+
}, string, import("vue").PublicProps, Readonly<DottedMapProps> & Readonly<{
|
|
32
|
+
onMouseenter?: ((values: MapPin | MapDot, event: MouseEvent | FocusEvent) => any) | undefined;
|
|
33
|
+
onMouseleave?: ((values: MapPin | MapDot, event: MouseEvent | FocusEvent) => any) | undefined;
|
|
34
|
+
onZoom?: ((transform: MapZoom) => any) | undefined;
|
|
35
|
+
"onPin-click"?: ((pin: MapPin, event: MouseEvent | KeyboardEvent) => any) | undefined;
|
|
36
|
+
"onPoint-click"?: ((event: MouseEvent | KeyboardEvent, point: MapDot) => any) | undefined;
|
|
37
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
38
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
39
|
+
declare const _default: typeof __VLS_export;
|
|
40
|
+
export default _default;
|
|
41
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
42
|
+
new (): {
|
|
43
|
+
$slots: S;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, ref, useId, watch } from "vue";
|
|
3
|
+
import DataChart from "./DataChart.vue";
|
|
4
|
+
import { transform } from "../spec/transform";
|
|
5
|
+
import { formatBucket, seriesLabel } from "../spec/resolve";
|
|
6
|
+
import { toCsv } from "../spec/csv";
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_RANGES,
|
|
9
|
+
formatChange,
|
|
10
|
+
formatMetricValue,
|
|
11
|
+
metricDelta,
|
|
12
|
+
metricSpec,
|
|
13
|
+
resolveRange
|
|
14
|
+
} from "../spec/metric";
|
|
15
|
+
const props = defineProps({
|
|
16
|
+
data: { type: Array, required: true },
|
|
17
|
+
title: { type: String, required: false },
|
|
18
|
+
description: { type: String, required: false },
|
|
19
|
+
x: { type: String, required: true },
|
|
20
|
+
y: { type: String, required: false },
|
|
21
|
+
series: { type: String, required: false },
|
|
22
|
+
aggregate: { type: String, required: false, default: "sum" },
|
|
23
|
+
interval: { type: String, required: false },
|
|
24
|
+
compare: { type: String, required: false },
|
|
25
|
+
type: { type: String, required: false, default: "line" },
|
|
26
|
+
format: { type: String, required: false, default: "number" },
|
|
27
|
+
currency: { type: String, required: false, default: "EUR" },
|
|
28
|
+
locale: { type: String, required: false },
|
|
29
|
+
formatOptions: { type: null, required: false },
|
|
30
|
+
valueFormatter: { type: Function, required: false },
|
|
31
|
+
inverseSentiment: { type: Boolean, required: false },
|
|
32
|
+
ranges: { type: Array, required: false },
|
|
33
|
+
range: { type: String, required: false },
|
|
34
|
+
window: { type: Object, required: false },
|
|
35
|
+
anchor: { type: [String, Number, Date], required: false },
|
|
36
|
+
height: { type: Number, required: false, default: 220 },
|
|
37
|
+
stacked: { type: Boolean, required: false },
|
|
38
|
+
hideLegend: { type: Boolean, required: false },
|
|
39
|
+
theme: { type: Object, required: false },
|
|
40
|
+
showTable: { type: Boolean, required: false },
|
|
41
|
+
exportable: { type: Boolean, required: false },
|
|
42
|
+
exportFilename: { type: String, required: false },
|
|
43
|
+
hideValue: { type: Boolean, required: false },
|
|
44
|
+
ariaLabel: { type: String, required: false },
|
|
45
|
+
ariaDescription: { type: String, required: false },
|
|
46
|
+
accessibleDataTable: { type: Boolean, required: false },
|
|
47
|
+
maxDataPoints: { type: Number, required: false },
|
|
48
|
+
emptyLabel: { type: String, required: false },
|
|
49
|
+
error: { type: String, required: false },
|
|
50
|
+
legendVariant: { type: String, required: false },
|
|
51
|
+
tooltipVariant: { type: String, required: false },
|
|
52
|
+
tooltipRoundness: { type: String, required: false },
|
|
53
|
+
loading: { type: Boolean, required: false },
|
|
54
|
+
loadingLabel: { type: String, required: false }
|
|
55
|
+
});
|
|
56
|
+
const rangeModel = defineModel("range", { type: null, ...{ default: void 0 } });
|
|
57
|
+
const emit = defineEmits(["update:window", "export"]);
|
|
58
|
+
const ranges = computed(() => props.ranges ?? DEFAULT_RANGES);
|
|
59
|
+
const activeRange = computed(() => {
|
|
60
|
+
const list = ranges.value;
|
|
61
|
+
if (!list.length) return void 0;
|
|
62
|
+
return list.find((range) => range.key === rangeModel.value) ?? list[1] ?? list[0];
|
|
63
|
+
});
|
|
64
|
+
const window = computed(() => {
|
|
65
|
+
if (props.window) return props.window;
|
|
66
|
+
const range = activeRange.value;
|
|
67
|
+
if (!range) return void 0;
|
|
68
|
+
return resolveRange(range, props.anchor);
|
|
69
|
+
});
|
|
70
|
+
watch(window, (value) => {
|
|
71
|
+
if (value) emit("update:window", value);
|
|
72
|
+
}, { immediate: true });
|
|
73
|
+
const interval = computed(() => props.interval ?? activeRange.value?.interval ?? "day");
|
|
74
|
+
const spec = computed(
|
|
75
|
+
() => metricSpec({
|
|
76
|
+
type: props.type,
|
|
77
|
+
x: props.x,
|
|
78
|
+
y: props.y,
|
|
79
|
+
series: props.series,
|
|
80
|
+
aggregate: props.aggregate,
|
|
81
|
+
interval: interval.value,
|
|
82
|
+
compare: props.compare,
|
|
83
|
+
// With no ranges and no explicit window, the metric covers all of history:
|
|
84
|
+
// an empty range would filter every row out, which reads as "no data" when
|
|
85
|
+
// the truth is "no window was asked for".
|
|
86
|
+
range: window.value ?? { from: "0000-01-01", to: "9999-12-31" },
|
|
87
|
+
label: props.title
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
const result = computed(() => transform(props.data ?? [], spec.value));
|
|
91
|
+
const formatValue = computed(() => {
|
|
92
|
+
if (props.valueFormatter) return props.valueFormatter;
|
|
93
|
+
return (value) => formatMetricValue(value, {
|
|
94
|
+
format: props.format,
|
|
95
|
+
currency: props.currency,
|
|
96
|
+
locale: props.locale,
|
|
97
|
+
options: props.formatOptions
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
const headline = computed(() => {
|
|
101
|
+
const { total, data, series } = result.value;
|
|
102
|
+
const aggregate = props.aggregate;
|
|
103
|
+
if (aggregate === "sum" || aggregate === "count") return total;
|
|
104
|
+
const primary = series.find((key) => key !== "compare");
|
|
105
|
+
if (!primary) return total;
|
|
106
|
+
const values = data.map((datum) => datum[primary]).filter((value) => typeof value === "number");
|
|
107
|
+
if (!values.length) return 0;
|
|
108
|
+
if (aggregate === "min") return Math.min(...values);
|
|
109
|
+
if (aggregate === "max") return Math.max(...values);
|
|
110
|
+
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
111
|
+
});
|
|
112
|
+
const headlineText = computed(() => formatValue.value(headline.value));
|
|
113
|
+
const delta = computed(() => {
|
|
114
|
+
const change = result.value.change;
|
|
115
|
+
const text = formatChange(change, props.locale);
|
|
116
|
+
if (text === void 0) return void 0;
|
|
117
|
+
return { text, ...metricDelta(change, props.inverseSentiment) };
|
|
118
|
+
});
|
|
119
|
+
const compareLabel = computed(
|
|
120
|
+
() => props.compare === "previous-year" ? "vs previous year" : "vs previous period"
|
|
121
|
+
);
|
|
122
|
+
const absoluteChange = computed(() => {
|
|
123
|
+
const { total, compareTotal } = result.value;
|
|
124
|
+
if (compareTotal === void 0) return void 0;
|
|
125
|
+
const difference = total - compareTotal;
|
|
126
|
+
const sign = difference > 0 ? "+" : difference < 0 ? "\u2212" : "";
|
|
127
|
+
return `${sign}${formatValue.value(Math.abs(difference))}`;
|
|
128
|
+
});
|
|
129
|
+
const isEmpty = computed(
|
|
130
|
+
() => !props.loading && !props.error && result.value.data.length === 0
|
|
131
|
+
);
|
|
132
|
+
const showingTable = ref(false);
|
|
133
|
+
const tableColumns = computed(
|
|
134
|
+
() => result.value.series.map((key) => ({ key, label: seriesLabel(key, spec.value) }))
|
|
135
|
+
);
|
|
136
|
+
const tableRows = computed(
|
|
137
|
+
() => result.value.data.map((datum) => ({
|
|
138
|
+
x: formatBucket(datum.x, interval.value, props.locale),
|
|
139
|
+
values: result.value.series.map((key) => {
|
|
140
|
+
const value = datum[key];
|
|
141
|
+
return typeof value === "number" ? formatValue.value(value) : "\u2014";
|
|
142
|
+
})
|
|
143
|
+
}))
|
|
144
|
+
);
|
|
145
|
+
function slugify(value) {
|
|
146
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "metric";
|
|
147
|
+
}
|
|
148
|
+
function exportCsv() {
|
|
149
|
+
const headers = Object.fromEntries(
|
|
150
|
+
tableColumns.value.map((column) => [column.key, column.label])
|
|
151
|
+
);
|
|
152
|
+
const csv = toCsv(result.value.data, result.value.series, headers, props.x);
|
|
153
|
+
emit("export", csv);
|
|
154
|
+
if (typeof document === "undefined") return;
|
|
155
|
+
const blob = new Blob([`\uFEFF${csv}`], { type: "text/csv;charset=utf-8" });
|
|
156
|
+
const url = URL.createObjectURL(blob);
|
|
157
|
+
const link = document.createElement("a");
|
|
158
|
+
link.href = url;
|
|
159
|
+
link.download = props.exportFilename ?? `${slugify(props.title ?? "metric")}.csv`;
|
|
160
|
+
link.click();
|
|
161
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
162
|
+
}
|
|
163
|
+
const titleId = useId();
|
|
164
|
+
const valueId = useId();
|
|
165
|
+
const valueLabel = computed(() => {
|
|
166
|
+
const parts = [props.title, headlineText.value].filter(Boolean);
|
|
167
|
+
if (delta.value) {
|
|
168
|
+
const direction = delta.value.direction === "up" ? "up" : delta.value.direction === "down" ? "down" : "unchanged";
|
|
169
|
+
parts.push(`${direction} ${delta.value.text} ${compareLabel.value}`);
|
|
170
|
+
}
|
|
171
|
+
return parts.join(", ");
|
|
172
|
+
});
|
|
173
|
+
defineExpose({ result, spec, exportCsv });
|
|
174
|
+
defineSlots();
|
|
175
|
+
</script>
|
|
176
|
+
|
|
177
|
+
<template>
|
|
178
|
+
<section
|
|
179
|
+
class="vc-metric"
|
|
180
|
+
:aria-labelledby="title ? titleId : void 0"
|
|
181
|
+
:aria-busy="loading ? 'true' : void 0"
|
|
182
|
+
>
|
|
183
|
+
<header class="vc-metric__header">
|
|
184
|
+
<slot name="header">
|
|
185
|
+
<div class="vc-metric__heading">
|
|
186
|
+
<h3 v-if="title" :id="titleId" class="vc-metric__title">{{ title }}</h3>
|
|
187
|
+
<p v-if="description" class="vc-metric__description">{{ description }}</p>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
<slot
|
|
191
|
+
name="value"
|
|
192
|
+
:value="headline"
|
|
193
|
+
:formatted="headlineText"
|
|
194
|
+
:change="result.change"
|
|
195
|
+
>
|
|
196
|
+
<div v-if="!hideValue" class="vc-metric__value-row">
|
|
197
|
+
<!--
|
|
198
|
+
The skeleton stands in for the number rather than showing a stale
|
|
199
|
+
or zero total while data is in flight: a headline that renders
|
|
200
|
+
"€0" and then jumps is worse than one that visibly waits.
|
|
201
|
+
-->
|
|
202
|
+
<div v-if="loading" class="vc-metric__value-skeleton" aria-hidden="true" />
|
|
203
|
+
<template v-else>
|
|
204
|
+
<p :id="valueId" class="vc-metric__value" :aria-label="valueLabel">
|
|
205
|
+
{{ headlineText }}
|
|
206
|
+
</p>
|
|
207
|
+
<p
|
|
208
|
+
v-if="delta"
|
|
209
|
+
class="vc-metric__delta"
|
|
210
|
+
:data-sentiment="delta.sentiment"
|
|
211
|
+
aria-hidden="true"
|
|
212
|
+
>
|
|
213
|
+
<span class="vc-metric__delta-arrow">{{
|
|
214
|
+
delta.direction === "up" ? "\u2191" : delta.direction === "down" ? "\u2193" : "\u2192"
|
|
215
|
+
}}</span>
|
|
216
|
+
<span>{{ delta.text }}</span>
|
|
217
|
+
<span v-if="absoluteChange" class="vc-metric__delta-absolute">({{ absoluteChange }})</span>
|
|
218
|
+
<span class="vc-metric__delta-label">{{ compareLabel }}</span>
|
|
219
|
+
</p>
|
|
220
|
+
</template>
|
|
221
|
+
</div>
|
|
222
|
+
</slot>
|
|
223
|
+
</slot>
|
|
224
|
+
</header>
|
|
225
|
+
|
|
226
|
+
<!--
|
|
227
|
+
The table replaces the chart rather than sitting beside it, so the two
|
|
228
|
+
never disagree about how much vertical space the card takes — a card that
|
|
229
|
+
grows on toggle shoves everything below it down the page.
|
|
230
|
+
-->
|
|
231
|
+
<div v-if="showingTable" class="vc-metric__table-wrap" :style="{ minHeight: `${height}px` }">
|
|
232
|
+
<table class="vc-metric__table">
|
|
233
|
+
<caption class="vc-sr-only">{{ title ?? "Metric data" }}</caption>
|
|
234
|
+
<thead>
|
|
235
|
+
<tr>
|
|
236
|
+
<th scope="col">{{ x }}</th>
|
|
237
|
+
<th v-for="column in tableColumns" :key="column.key" scope="col">{{ column.label }}</th>
|
|
238
|
+
</tr>
|
|
239
|
+
</thead>
|
|
240
|
+
<tbody>
|
|
241
|
+
<tr v-for="(row, index) in tableRows" :key="index">
|
|
242
|
+
<th scope="row">{{ row.x }}</th>
|
|
243
|
+
<td v-for="(value, column) in row.values" :key="column">{{ value }}</td>
|
|
244
|
+
</tr>
|
|
245
|
+
</tbody>
|
|
246
|
+
</table>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<DataChart
|
|
250
|
+
v-else
|
|
251
|
+
:data="data"
|
|
252
|
+
:spec="spec"
|
|
253
|
+
:height="height"
|
|
254
|
+
:locale="locale"
|
|
255
|
+
:stacked="stacked"
|
|
256
|
+
:hide-legend="hideLegend ?? (!compare && !series)"
|
|
257
|
+
:theme="theme"
|
|
258
|
+
:value-formatter="formatValue"
|
|
259
|
+
:loading="loading"
|
|
260
|
+
:loading-label="loadingLabel"
|
|
261
|
+
:error="error"
|
|
262
|
+
:empty-label="emptyLabel"
|
|
263
|
+
:aria-label="ariaLabel ?? title"
|
|
264
|
+
:aria-description="ariaDescription ?? (delta ? valueLabel : void 0)"
|
|
265
|
+
:accessible-data-table="accessibleDataTable"
|
|
266
|
+
:max-data-points="maxDataPoints"
|
|
267
|
+
:legend-variant="legendVariant"
|
|
268
|
+
:tooltip-variant="tooltipVariant"
|
|
269
|
+
:tooltip-roundness="tooltipRoundness"
|
|
270
|
+
/>
|
|
271
|
+
|
|
272
|
+
<footer v-if="ranges.length || showTable || exportable || $slots.actions" class="vc-metric__footer">
|
|
273
|
+
<!--
|
|
274
|
+
A radiogroup rather than a row of buttons: the ranges are one mutually
|
|
275
|
+
exclusive choice, and that is what lets a screen reader announce
|
|
276
|
+
"30D, selected, 2 of 4" instead of four unrelated buttons.
|
|
277
|
+
-->
|
|
278
|
+
<div
|
|
279
|
+
v-if="ranges.length && !window"
|
|
280
|
+
class="vc-metric__ranges"
|
|
281
|
+
role="radiogroup"
|
|
282
|
+
aria-label="Time range"
|
|
283
|
+
>
|
|
284
|
+
<button
|
|
285
|
+
v-for="range in ranges"
|
|
286
|
+
:key="range.key"
|
|
287
|
+
type="button"
|
|
288
|
+
role="radio"
|
|
289
|
+
class="vc-metric__range"
|
|
290
|
+
:aria-checked="range.key === activeRange?.key"
|
|
291
|
+
:aria-label="range.title ?? range.label"
|
|
292
|
+
:data-active="range.key === activeRange?.key ? 'true' : void 0"
|
|
293
|
+
@click="rangeModel = range.key"
|
|
294
|
+
>
|
|
295
|
+
{{ range.label }}
|
|
296
|
+
</button>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
<div class="vc-metric__actions">
|
|
300
|
+
<slot name="actions" />
|
|
301
|
+
<button
|
|
302
|
+
v-if="showTable"
|
|
303
|
+
type="button"
|
|
304
|
+
class="vc-metric__action"
|
|
305
|
+
:aria-pressed="showingTable"
|
|
306
|
+
@click="showingTable = !showingTable"
|
|
307
|
+
>
|
|
308
|
+
{{ showingTable ? "Chart" : "Table" }}
|
|
309
|
+
</button>
|
|
310
|
+
<button
|
|
311
|
+
v-if="exportable"
|
|
312
|
+
type="button"
|
|
313
|
+
class="vc-metric__action"
|
|
314
|
+
:disabled="isEmpty"
|
|
315
|
+
@click="exportCsv"
|
|
316
|
+
>
|
|
317
|
+
Export
|
|
318
|
+
</button>
|
|
319
|
+
</div>
|
|
320
|
+
</footer>
|
|
321
|
+
</section>
|
|
322
|
+
</template>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { MetricChartProps } from "../types/charts.js";
|
|
2
|
+
declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<(MetricChartProps<T> & {
|
|
4
|
+
/**
|
|
5
|
+
* Selected range key.
|
|
6
|
+
*
|
|
7
|
+
* `v-model:range` is optional: without it the component keeps its own
|
|
8
|
+
* selection, so a metric card works as a drop-in without the host having to
|
|
9
|
+
* own state it does not care about. With it, several cards can share one
|
|
10
|
+
* selection — which is what the eventual `MetricGrid` will use.
|
|
11
|
+
*/
|
|
12
|
+
range?: string | undefined;
|
|
13
|
+
}) & {
|
|
14
|
+
"onUpdate:window"?: ((window: {
|
|
15
|
+
from: string;
|
|
16
|
+
to: string;
|
|
17
|
+
}) => any) | undefined;
|
|
18
|
+
onExport?: ((csv: string) => any) | undefined;
|
|
19
|
+
"onUpdate:range"?: ((value: string | undefined) => any) | undefined;
|
|
20
|
+
}> & (typeof globalThis extends {
|
|
21
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
22
|
+
} ? P : {});
|
|
23
|
+
expose: (exposed: import("vue").ShallowUnwrapRef<{
|
|
24
|
+
result: import("vue").ComputedRef<import("../spec/transform.js").TransformResult>;
|
|
25
|
+
spec: import("vue").ComputedRef<import("../spec/types.js").ChartSpec>;
|
|
26
|
+
exportCsv: () => void;
|
|
27
|
+
}>) => void;
|
|
28
|
+
attrs: any;
|
|
29
|
+
slots: {
|
|
30
|
+
/** Replaces the whole header (title, description, value, delta). */
|
|
31
|
+
header?: () => unknown;
|
|
32
|
+
/** Replaces the headline value and its delta only. */
|
|
33
|
+
value?: (props: {
|
|
34
|
+
value: number;
|
|
35
|
+
formatted: string;
|
|
36
|
+
change: number | undefined;
|
|
37
|
+
}) => unknown;
|
|
38
|
+
/** Extra controls in the footer, beside the table and export buttons. */
|
|
39
|
+
actions?: () => unknown;
|
|
40
|
+
};
|
|
41
|
+
emit: (((evt: "update:window", window: {
|
|
42
|
+
from: string;
|
|
43
|
+
to: string;
|
|
44
|
+
}) => void) & ((evt: "export", csv: string) => void)) & ((event: "update:range", value: string | undefined) => void);
|
|
45
|
+
}>) => import("vue").VNode & {
|
|
46
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
47
|
+
};
|
|
48
|
+
declare const _default: typeof __VLS_export;
|
|
49
|
+
export default _default;
|
|
50
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
51
|
+
[K in keyof T]: T[K];
|
|
52
|
+
} : {
|
|
53
|
+
[K in keyof T as K]: T[K];
|
|
54
|
+
}) & {};
|