nuxt-charts 3.0.0-beta.2 → 3.0.0-beta.3
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 +77 -7
- package/dist/module.json +1 -1
- package/dist/module.mjs +20 -1
- package/dist/runtime/assets/components.css +1 -1
- package/dist/runtime/assets/theme.css +1 -1
- package/dist/runtime/components/AreaChart.d.vue.ts +5 -1
- package/dist/runtime/components/AreaChart.vue +84 -9
- package/dist/runtime/components/AreaChart.vue.d.ts +5 -1
- package/dist/runtime/components/BarChart.d.vue.ts +5 -1
- package/dist/runtime/components/BarChart.vue +125 -8
- package/dist/runtime/components/BarChart.vue.d.ts +5 -1
- package/dist/runtime/components/BubbleChart.d.vue.ts +5 -1
- package/dist/runtime/components/BubbleChart.vue +74 -13
- package/dist/runtime/components/BubbleChart.vue.d.ts +5 -1
- package/dist/runtime/components/DonutChart.vue +67 -15
- package/dist/runtime/components/FunnelChart.d.vue.ts +8 -1
- package/dist/runtime/components/FunnelChart.vue +127 -35
- package/dist/runtime/components/FunnelChart.vue.d.ts +8 -1
- package/dist/runtime/components/LineChart.d.vue.ts +5 -1
- package/dist/runtime/components/LineChart.vue +72 -7
- package/dist/runtime/components/LineChart.vue.d.ts +5 -1
- package/dist/runtime/components/RadarChart.vue +93 -12
- package/dist/runtime/components/RadialBarChart.vue +40 -8
- package/dist/runtime/components/SankeyChart.d.vue.ts +36 -0
- package/dist/runtime/components/SankeyChart.vue +146 -0
- package/dist/runtime/components/SankeyChart.vue.d.ts +36 -0
- package/dist/runtime/components/internal/AreaVariantDefs.d.ts +68 -0
- package/dist/runtime/components/internal/AreaVariantDefs.js +165 -0
- package/dist/runtime/components/internal/BarVariantDefs.d.ts +74 -0
- package/dist/runtime/components/internal/BarVariantDefs.js +200 -0
- package/dist/runtime/components/internal/CartesianFrame.d.vue.ts +18 -1
- package/dist/runtime/components/internal/CartesianFrame.vue +65 -14
- package/dist/runtime/components/internal/CartesianFrame.vue.d.ts +18 -1
- package/dist/runtime/components/internal/ChartBackground.d.ts +44 -0
- package/dist/runtime/components/internal/ChartBackground.js +53 -0
- package/dist/runtime/components/internal/ChartDot.d.ts +85 -0
- package/dist/runtime/components/internal/ChartDot.js +59 -0
- package/dist/runtime/components/internal/ChartLegend.d.vue.ts +24 -1
- package/dist/runtime/components/internal/ChartLegend.vue +11 -3
- package/dist/runtime/components/internal/ChartLegend.vue.d.ts +24 -1
- package/dist/runtime/components/internal/ChartSkeleton.d.vue.ts +18 -0
- package/dist/runtime/components/internal/ChartSkeleton.vue +64 -0
- package/dist/runtime/components/internal/ChartSkeleton.vue.d.ts +18 -0
- package/dist/runtime/components/internal/ChartTooltip.vue +8 -2
- package/dist/runtime/components/internal/CubeBarShape.d.vue.ts +9 -1
- package/dist/runtime/components/internal/CubeBarShape.vue +8 -8
- package/dist/runtime/components/internal/CubeBarShape.vue.d.ts +9 -1
- package/dist/runtime/components/internal/FunnelClassicShape.d.vue.ts +81 -0
- package/dist/runtime/components/internal/FunnelClassicShape.vue +97 -0
- package/dist/runtime/components/internal/FunnelClassicShape.vue.d.ts +81 -0
- package/dist/runtime/components/internal/FunnelLayeredShape.d.vue.ts +21 -0
- package/dist/runtime/components/internal/FunnelLayeredShape.vue +71 -37
- package/dist/runtime/components/internal/FunnelLayeredShape.vue.d.ts +21 -0
- package/dist/runtime/components/internal/FunnelTooltip.d.vue.ts +16 -0
- package/dist/runtime/components/internal/FunnelTooltip.vue +26 -0
- package/dist/runtime/components/internal/FunnelTooltip.vue.d.ts +16 -0
- package/dist/runtime/components/internal/PolarCenterSync.d.ts +38 -0
- package/dist/runtime/components/internal/PolarCenterSync.js +27 -0
- package/dist/runtime/components/internal/RadarGradientDefs.d.ts +46 -0
- package/dist/runtime/components/internal/RadarGradientDefs.js +46 -0
- package/dist/runtime/components/internal/SegmentVariantDefs.d.ts +67 -0
- package/dist/runtime/components/internal/SegmentVariantDefs.js +51 -0
- package/dist/runtime/components/internal/StrokeGradientDefs.d.ts +36 -0
- package/dist/runtime/components/internal/StrokeGradientDefs.js +35 -0
- package/dist/runtime/components/internal/VariantBarShape.d.vue.ts +36 -0
- package/dist/runtime/components/internal/VariantBarShape.vue +53 -0
- package/dist/runtime/components/internal/VariantBarShape.vue.d.ts +36 -0
- package/dist/runtime/components/internal/tooltipContent.d.ts +13 -0
- package/dist/runtime/components/internal/tooltipContent.js +5 -0
- package/dist/runtime/types/charts.d.ts +192 -12
- package/dist/runtime/types/shared.d.ts +37 -0
- package/dist/runtime/types.d.ts +3 -0
- package/dist/runtime/utils/axis.d.ts +12 -1
- package/dist/runtime/utils/axis.js +23 -0
- package/dist/runtime/utils/background.d.ts +36 -0
- package/dist/runtime/utils/background.js +140 -0
- package/dist/runtime/utils/gradient.d.ts +8 -0
- package/dist/runtime/utils/gradient.js +3 -0
- package/dist/runtime/utils/theme.js +14 -0
- package/dist/runtime/utils/variants.d.ts +101 -0
- package/dist/runtime/utils/variants.js +48 -0
- package/package.json +16 -28
|
@@ -5,7 +5,11 @@ declare const __VLS_export: <T extends Record<string, unknown>>(__VLS_props: Non
|
|
|
5
5
|
} ? P : {});
|
|
6
6
|
expose: (exposed: {}) => void;
|
|
7
7
|
attrs: any;
|
|
8
|
-
slots: {
|
|
8
|
+
slots: {
|
|
9
|
+
tooltip?: (props: {
|
|
10
|
+
values: T | undefined;
|
|
11
|
+
}) => unknown;
|
|
12
|
+
};
|
|
9
13
|
emit: {};
|
|
10
14
|
}>) => import("vue").VNode & {
|
|
11
15
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed } from "vue";
|
|
2
|
+
import { computed, h } from "vue";
|
|
3
3
|
import {
|
|
4
4
|
CartesianGrid,
|
|
5
5
|
Legend,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
ZAxis
|
|
12
12
|
} from "vccs";
|
|
13
13
|
import ChartContainer from "./internal/ChartContainer";
|
|
14
|
+
import ChartSkeleton from "./internal/ChartSkeleton.vue";
|
|
14
15
|
import ChartTooltip from "./internal/ChartTooltip.vue";
|
|
15
16
|
import ChartLegend from "./internal/ChartLegend.vue";
|
|
16
17
|
import { categoriesToSeries } from "../utils/categories";
|
|
@@ -69,16 +70,32 @@ const props = defineProps({
|
|
|
69
70
|
xExplicitTicks: { type: Array, required: false },
|
|
70
71
|
yExplicitTicks: { type: Array, required: false },
|
|
71
72
|
crosshairConfig: { type: Object, required: false },
|
|
72
|
-
theme: { type: Object, required: false }
|
|
73
|
+
theme: { type: Object, required: false },
|
|
74
|
+
backgroundPattern: { type: String, required: false },
|
|
75
|
+
legendVariant: { type: String, required: false },
|
|
76
|
+
tooltipVariant: { type: String, required: false },
|
|
77
|
+
tooltipRoundness: { type: String, required: false },
|
|
78
|
+
loading: { type: Boolean, required: false },
|
|
79
|
+
loadingLabel: { type: String, required: false }
|
|
73
80
|
});
|
|
81
|
+
const slots = defineSlots();
|
|
74
82
|
function resolveAccessor(accessor) {
|
|
75
83
|
if (accessor === void 0) return void 0;
|
|
76
84
|
return typeof accessor === "function" ? accessor : String(accessor);
|
|
77
85
|
}
|
|
86
|
+
function accessorName(accessor, fallback) {
|
|
87
|
+
if (accessor === void 0) return fallback;
|
|
88
|
+
if (typeof accessor !== "function") return String(accessor);
|
|
89
|
+
const match = /(?:=>|return)\s*[\w$]+(?:\?\.|\.)([\w$]+)\s*;?\s*\}?\s*$/.exec(String(accessor));
|
|
90
|
+
return match?.[1] ?? fallback;
|
|
91
|
+
}
|
|
78
92
|
const xKey = computed(() => resolveAccessor(props.xAccessor));
|
|
79
93
|
const yKey = computed(() => resolveAccessor(props.yAccessor));
|
|
80
94
|
const zKey = computed(() => resolveAccessor(props.sizeAccessor));
|
|
81
95
|
const categoryKey = computed(() => String(props.categoryKey));
|
|
96
|
+
const xName = computed(() => props.xLabel ?? accessorName(props.xAccessor, "x"));
|
|
97
|
+
const yName = computed(() => props.yLabel ?? accessorName(props.yAccessor, "y"));
|
|
98
|
+
const zName = computed(() => accessorName(props.sizeAccessor, "size"));
|
|
82
99
|
const legend = computed(() => legendPositionToLegendProps(props.legendPosition));
|
|
83
100
|
const sizeRange = computed(() => {
|
|
84
101
|
if (props.sizeRange) return props.sizeRange;
|
|
@@ -127,9 +144,12 @@ const cursor = computed(
|
|
|
127
144
|
radius: resolveHoverRadius(props.theme)
|
|
128
145
|
} : false
|
|
129
146
|
);
|
|
147
|
+
const colorByCategory = computed(() => {
|
|
148
|
+
const map = /* @__PURE__ */ new Map();
|
|
149
|
+
for (const s of categoriesToSeries(props.categories)) map.set(s.dataKey, s.color);
|
|
150
|
+
return map;
|
|
151
|
+
});
|
|
130
152
|
const groups = computed(() => {
|
|
131
|
-
const colorByCat = /* @__PURE__ */ new Map();
|
|
132
|
-
for (const s of categoriesToSeries(props.categories)) colorByCat.set(s.dataKey, s.color);
|
|
133
153
|
const byCategory = /* @__PURE__ */ new Map();
|
|
134
154
|
for (const row of props.data) {
|
|
135
155
|
const cat = String(row[categoryKey.value]);
|
|
@@ -140,26 +160,64 @@ const groups = computed(() => {
|
|
|
140
160
|
return Array.from(byCategory.entries()).map(([name, data]) => ({
|
|
141
161
|
name,
|
|
142
162
|
data,
|
|
143
|
-
color:
|
|
163
|
+
color: colorByCategory.value.get(name)
|
|
144
164
|
}));
|
|
145
165
|
});
|
|
166
|
+
const tooltipContent = computed(() => {
|
|
167
|
+
const fields = [
|
|
168
|
+
{ key: xKey.value, name: xName.value, format: xTickFormatter.value },
|
|
169
|
+
{ key: yKey.value, name: yName.value, format: yTickFormatter.value },
|
|
170
|
+
...zKey.value ? [{ key: zKey.value, name: zName.value, format: void 0 }] : []
|
|
171
|
+
];
|
|
172
|
+
return (tooltipProps) => {
|
|
173
|
+
const row = tooltipProps.payload?.[0]?.payload;
|
|
174
|
+
if (slots.tooltip) return slots.tooltip({ values: row });
|
|
175
|
+
const category = row != null ? String(row[categoryKey.value]) : void 0;
|
|
176
|
+
const color = category != null ? colorByCategory.value.get(category) : void 0;
|
|
177
|
+
const payload = (tooltipProps.payload ?? []).map((entry) => {
|
|
178
|
+
const field = fields.find((f) => f.key === entry.dataKey);
|
|
179
|
+
return {
|
|
180
|
+
...entry,
|
|
181
|
+
color,
|
|
182
|
+
name: field?.name ?? entry.name,
|
|
183
|
+
value: field?.format ? field.format(entry.value, 0) : entry.value
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
return h(ChartTooltip, {
|
|
187
|
+
...tooltipProps,
|
|
188
|
+
label: category,
|
|
189
|
+
payload,
|
|
190
|
+
variant: props.tooltipVariant,
|
|
191
|
+
roundness: props.tooltipRoundness
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
});
|
|
146
195
|
</script>
|
|
147
196
|
|
|
148
197
|
<template>
|
|
149
198
|
<div class="vue-chrts" :style="themeVars">
|
|
150
|
-
<
|
|
199
|
+
<ChartSkeleton
|
|
200
|
+
v-if="loading"
|
|
201
|
+
:height="height"
|
|
202
|
+
shape="bars"
|
|
203
|
+
:label="loadingLabel ?? 'Loading'"
|
|
204
|
+
/>
|
|
205
|
+
<ChartContainer v-else width="100%" :height="height">
|
|
151
206
|
<ScatterChart>
|
|
152
207
|
<CartesianGrid
|
|
153
208
|
v-if="(xGridLine ?? true) || (yGridLine ?? true)"
|
|
209
|
+
:horizontal="yGridLine ?? true"
|
|
210
|
+
:vertical="xGridLine ?? true"
|
|
154
211
|
stroke="var(--vc-grid-color)"
|
|
155
212
|
stroke-dasharray="var(--vc-grid-dash)"
|
|
156
213
|
:stroke-width="'var(--vc-grid-width)'"
|
|
157
214
|
/>
|
|
215
|
+
<!-- Axes stay mounted when hidden: their `dataKey` is what maps rows to x/y. -->
|
|
158
216
|
<XAxis
|
|
159
|
-
v-if="!hideXAxis"
|
|
160
217
|
type="number"
|
|
161
218
|
:data-key="xKey"
|
|
162
|
-
:
|
|
219
|
+
:hide="hideXAxis"
|
|
220
|
+
:name="xName"
|
|
163
221
|
:height="xAxisHeight"
|
|
164
222
|
:tick="xTickProp"
|
|
165
223
|
:tick-margin="AXIS_TICK_MARGIN.x"
|
|
@@ -171,10 +229,10 @@ const groups = computed(() => {
|
|
|
171
229
|
:interval="xAxis.interval"
|
|
172
230
|
/>
|
|
173
231
|
<YAxis
|
|
174
|
-
v-if="!hideYAxis"
|
|
175
232
|
type="number"
|
|
176
233
|
:data-key="yKey"
|
|
177
|
-
:
|
|
234
|
+
:hide="hideYAxis"
|
|
235
|
+
:name="yName"
|
|
178
236
|
:width="yAxisWidth"
|
|
179
237
|
:tick="yTickProp"
|
|
180
238
|
:tick-margin="AXIS_TICK_MARGIN.y"
|
|
@@ -184,18 +242,21 @@ const groups = computed(() => {
|
|
|
184
242
|
:domain="yAxisDomain"
|
|
185
243
|
:interval="yAxis.interval"
|
|
186
244
|
/>
|
|
187
|
-
<ZAxis v-if="zKey" type="number" :data-key="zKey" :range="sizeRange" />
|
|
245
|
+
<ZAxis v-if="zKey" type="number" :data-key="zKey" :name="zName" :range="sizeRange" />
|
|
188
246
|
|
|
247
|
+
<!-- Distinct `dataKey` per series: `vccs` filters the item tooltip and the -->
|
|
248
|
+
<!-- active-symbol highlight by it, so shared keys show every series at once. -->
|
|
189
249
|
<Scatter
|
|
190
250
|
v-for="g in groups"
|
|
191
251
|
:key="g.name"
|
|
252
|
+
:data-key="g.name"
|
|
192
253
|
:name="g.name"
|
|
193
254
|
:data="g.data"
|
|
194
255
|
:fill="g.color"
|
|
195
256
|
:fill-opacity="opacity ?? 0.7"
|
|
196
257
|
/>
|
|
197
258
|
|
|
198
|
-
<Tooltip v-if="!hideTooltip" :content="
|
|
259
|
+
<Tooltip v-if="!hideTooltip" :content="tooltipContent" :cursor="cursor" :is-animation-active="false" />
|
|
199
260
|
<Legend
|
|
200
261
|
v-if="!hideLegend"
|
|
201
262
|
:align="legend.align"
|
|
@@ -204,7 +265,7 @@ const groups = computed(() => {
|
|
|
204
265
|
:wrapper-style="legendWrapperStyle"
|
|
205
266
|
>
|
|
206
267
|
<template #content="slotProps">
|
|
207
|
-
<ChartLegend v-bind="slotProps" />
|
|
268
|
+
<ChartLegend v-bind="slotProps" :variant="legendVariant" />
|
|
208
269
|
</template>
|
|
209
270
|
</Legend>
|
|
210
271
|
</ScatterChart>
|
|
@@ -5,7 +5,11 @@ declare const __VLS_export: <T extends Record<string, unknown>>(__VLS_props: Non
|
|
|
5
5
|
} ? P : {});
|
|
6
6
|
expose: (exposed: {}) => void;
|
|
7
7
|
attrs: any;
|
|
8
|
-
slots: {
|
|
8
|
+
slots: {
|
|
9
|
+
tooltip?: (props: {
|
|
10
|
+
values: T | undefined;
|
|
11
|
+
}) => unknown;
|
|
12
|
+
};
|
|
9
13
|
emit: {};
|
|
10
14
|
}>) => import("vue").VNode & {
|
|
11
15
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed } from "vue";
|
|
2
|
+
import { computed, useId } from "vue";
|
|
3
3
|
import { Legend, Pie, PieChart, Tooltip } from "vccs";
|
|
4
4
|
import ChartContainer from "./internal/ChartContainer";
|
|
5
|
-
import
|
|
5
|
+
import ChartSkeleton from "./internal/ChartSkeleton.vue";
|
|
6
6
|
import ChartLegend from "./internal/ChartLegend.vue";
|
|
7
|
+
import SegmentVariantDefs, { segmentGradientId } from "./internal/SegmentVariantDefs";
|
|
8
|
+
import { tooltipContentFor } from "./internal/tooltipContent";
|
|
9
|
+
import { variantId } from "../utils/variants";
|
|
7
10
|
import { DonutType } from "../enums";
|
|
8
11
|
import { categoriesToSeries } from "../utils/categories";
|
|
9
12
|
import { legendPositionToLegendProps, resolveLegendWrapperStyle } from "../utils/legend";
|
|
@@ -13,10 +16,15 @@ const props = defineProps({
|
|
|
13
16
|
data: { type: Array, required: true },
|
|
14
17
|
categories: { type: Object, required: true },
|
|
15
18
|
type: { type: String, required: false },
|
|
19
|
+
variant: { type: String, required: false },
|
|
20
|
+
glow: { type: Boolean, required: false },
|
|
16
21
|
height: { type: Number, required: false },
|
|
17
22
|
radius: { type: Number, required: false },
|
|
18
23
|
arcWidth: { type: Number, required: false },
|
|
19
24
|
padAngle: { type: Number, required: false },
|
|
25
|
+
cornerRadius: { type: Number, required: false },
|
|
26
|
+
startAngle: { type: Number, required: false },
|
|
27
|
+
endAngle: { type: Number, required: false },
|
|
20
28
|
stroke: { type: String, required: false },
|
|
21
29
|
hideLegend: { type: Boolean, required: false },
|
|
22
30
|
legendPosition: { type: String, required: false },
|
|
@@ -25,18 +33,40 @@ const props = defineProps({
|
|
|
25
33
|
tooltipTitleFormatter: { type: Function, required: false },
|
|
26
34
|
tooltip: { type: Object, required: false },
|
|
27
35
|
theme: { type: Object, required: false },
|
|
28
|
-
duration: { type: Number, required: false }
|
|
36
|
+
duration: { type: Number, required: false },
|
|
37
|
+
legendVariant: { type: String, required: false },
|
|
38
|
+
tooltipVariant: { type: String, required: false },
|
|
39
|
+
tooltipRoundness: { type: String, required: false },
|
|
40
|
+
loading: { type: Boolean, required: false },
|
|
41
|
+
loadingLabel: { type: String, required: false }
|
|
29
42
|
});
|
|
30
43
|
const themeVars = computed(() => themeToVars(props.theme));
|
|
44
|
+
const variantScope = useId();
|
|
45
|
+
const useGradient = computed(() => props.variant === "gradient");
|
|
31
46
|
const segments = computed(() => {
|
|
32
47
|
const cats = categoriesToSeries(props.categories);
|
|
33
|
-
return props.data.map((value, i) =>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
48
|
+
return props.data.map((value, i) => {
|
|
49
|
+
const color = cats[i]?.color ?? `var(--chart-color-${i})`;
|
|
50
|
+
return {
|
|
51
|
+
name: cats[i]?.name ?? String(i),
|
|
52
|
+
value,
|
|
53
|
+
dataKey: String(i),
|
|
54
|
+
color,
|
|
55
|
+
// vccs reads `fill` directly from the data entry to color each sector
|
|
56
|
+
fill: useGradient.value ? `url(#${segmentGradientId(String(i), variantScope)})` : color,
|
|
57
|
+
filter: props.glow ? `url(#${variantId("glow", String(i), variantScope)})` : void 0
|
|
58
|
+
};
|
|
59
|
+
});
|
|
39
60
|
});
|
|
61
|
+
const paintedSegments = computed(
|
|
62
|
+
() => segments.value.map((s) => ({ dataKey: s.dataKey, color: s.color }))
|
|
63
|
+
);
|
|
64
|
+
const legendColors = computed(
|
|
65
|
+
() => Object.fromEntries(segments.value.map((s) => [s.name, s.color]))
|
|
66
|
+
);
|
|
67
|
+
const tooltipContent = computed(
|
|
68
|
+
() => tooltipContentFor(props.tooltipVariant, props.tooltipRoundness)
|
|
69
|
+
);
|
|
40
70
|
const DEFAULT_ARC_WIDTH = 40;
|
|
41
71
|
const AUTO_OUTER_FRACTION = 0.9;
|
|
42
72
|
const radii = computed(() => {
|
|
@@ -59,9 +89,13 @@ const radii = computed(() => {
|
|
|
59
89
|
});
|
|
60
90
|
const outerRadius = computed(() => radii.value.outerRadius);
|
|
61
91
|
const innerRadius = computed(() => radii.value.innerRadius);
|
|
62
|
-
const angles = computed(
|
|
63
|
-
|
|
64
|
-
|
|
92
|
+
const angles = computed(() => {
|
|
93
|
+
const defaults = props.type === DonutType.Half ? { startAngle: 180, endAngle: 0 } : { startAngle: 0, endAngle: 360 };
|
|
94
|
+
return {
|
|
95
|
+
startAngle: props.startAngle ?? defaults.startAngle,
|
|
96
|
+
endAngle: props.endAngle ?? defaults.endAngle
|
|
97
|
+
};
|
|
98
|
+
});
|
|
65
99
|
const legend = computed(() => legendPositionToLegendProps(props.legendPosition));
|
|
66
100
|
const legendWrapperStyle = computed(
|
|
67
101
|
() => resolveLegendWrapperStyle(props.legendPosition, toCssProperties(props.legendStyle))
|
|
@@ -70,8 +104,25 @@ const legendWrapperStyle = computed(
|
|
|
70
104
|
|
|
71
105
|
<template>
|
|
72
106
|
<div class="donut-chart vue-chrts" :style="{ position: 'relative', width: '100%', height: `${height ?? (radius ? radius * 2 : 200)}px`, ...themeVars }">
|
|
73
|
-
<
|
|
107
|
+
<ChartSkeleton
|
|
108
|
+
v-if="loading"
|
|
109
|
+
:height="height ?? 200"
|
|
110
|
+
shape="ring"
|
|
111
|
+
:label="loadingLabel ?? 'Loading'"
|
|
112
|
+
/>
|
|
113
|
+
<ChartContainer v-else width="100%" height="100%">
|
|
74
114
|
<PieChart>
|
|
115
|
+
<!--
|
|
116
|
+
Built with `h()` so the paints land in the SVG namespace and each
|
|
117
|
+
segment's `fill="url(#…)"` resolves.
|
|
118
|
+
-->
|
|
119
|
+
<SegmentVariantDefs
|
|
120
|
+
v-if="useGradient || glow"
|
|
121
|
+
:segments="paintedSegments"
|
|
122
|
+
:scope="variantScope"
|
|
123
|
+
:gradient="useGradient"
|
|
124
|
+
:glow="glow === true"
|
|
125
|
+
/>
|
|
75
126
|
<Pie
|
|
76
127
|
:data="segments"
|
|
77
128
|
data-key="value"
|
|
@@ -81,10 +132,11 @@ const legendWrapperStyle = computed(
|
|
|
81
132
|
:start-angle="angles.startAngle"
|
|
82
133
|
:end-angle="angles.endAngle"
|
|
83
134
|
:padding-angle="padAngle ?? 0"
|
|
135
|
+
:corner-radius="cornerRadius ?? 0"
|
|
84
136
|
:stroke="stroke ?? 'none'"
|
|
85
137
|
:is-animation-active="duration !== void 0 && duration !== 0"
|
|
86
138
|
/>
|
|
87
|
-
<Tooltip v-if="!hideTooltip" :content="
|
|
139
|
+
<Tooltip v-if="!hideTooltip" :content="tooltipContent" :is-animation-active="false" />
|
|
88
140
|
<Legend
|
|
89
141
|
v-if="!hideLegend"
|
|
90
142
|
:align="legend.align"
|
|
@@ -93,7 +145,7 @@ const legendWrapperStyle = computed(
|
|
|
93
145
|
:wrapper-style="legendWrapperStyle"
|
|
94
146
|
>
|
|
95
147
|
<template #content="slotProps">
|
|
96
|
-
<ChartLegend v-bind="slotProps" />
|
|
148
|
+
<ChartLegend v-bind="slotProps" :variant="legendVariant" :colors="legendColors" />
|
|
97
149
|
</template>
|
|
98
150
|
</Legend>
|
|
99
151
|
</PieChart>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FunnelLayeredDatum } from "./internal/FunnelLayeredShape.vue.js";
|
|
1
2
|
import type { FunnelChartProps } from "../types/charts.js";
|
|
2
3
|
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
4
|
props: import("vue").PublicProps & __VLS_PrettifyLocal<FunnelChartProps<T>> & (typeof globalThis extends {
|
|
@@ -5,7 +6,13 @@ declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
|
|
|
5
6
|
} ? P : {});
|
|
6
7
|
expose: (exposed: {}) => void;
|
|
7
8
|
attrs: any;
|
|
8
|
-
slots: {
|
|
9
|
+
slots: {
|
|
10
|
+
shape(props: Record<string, unknown> & {
|
|
11
|
+
onMouseenter: (event: MouseEvent, datum: FunnelLayeredDatum) => void;
|
|
12
|
+
onMousemove: (event: MouseEvent) => void;
|
|
13
|
+
onMouseleave: () => void;
|
|
14
|
+
}): unknown;
|
|
15
|
+
};
|
|
9
16
|
emit: {};
|
|
10
17
|
}>) => import("vue").VNode & {
|
|
11
18
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
@@ -3,13 +3,13 @@ import { computed, ref, useTemplateRef } from "vue";
|
|
|
3
3
|
import {
|
|
4
4
|
Funnel,
|
|
5
5
|
FunnelChart as VccsFunnelChart,
|
|
6
|
-
|
|
7
|
-
Legend,
|
|
8
|
-
Tooltip
|
|
6
|
+
Legend
|
|
9
7
|
} from "vccs";
|
|
10
8
|
import ChartContainer from "./internal/ChartContainer";
|
|
11
|
-
import
|
|
9
|
+
import ChartSkeleton from "./internal/ChartSkeleton.vue";
|
|
12
10
|
import ChartLegend from "./internal/ChartLegend.vue";
|
|
11
|
+
import FunnelClassicShape from "./internal/FunnelClassicShape.vue";
|
|
12
|
+
import FunnelTooltip from "./internal/FunnelTooltip.vue";
|
|
13
13
|
import FunnelLayeredShape, {
|
|
14
14
|
} from "./internal/FunnelLayeredShape.vue";
|
|
15
15
|
import { categoriesToSeries } from "../utils/categories";
|
|
@@ -22,7 +22,12 @@ const props = defineProps({
|
|
|
22
22
|
height: { type: Number, required: true },
|
|
23
23
|
variant: { type: String, required: false },
|
|
24
24
|
lastShapeType: { type: String, required: false },
|
|
25
|
-
showValueLabel: { type: Boolean, required: false },
|
|
25
|
+
showValueLabel: { type: Boolean, required: false, default: true },
|
|
26
|
+
showStageLabel: { type: Boolean, required: false, default: true },
|
|
27
|
+
stageGap: { type: Number, required: false, default: 4 },
|
|
28
|
+
valueFormatter: { type: Function, required: false },
|
|
29
|
+
showPercentage: { type: Boolean, required: false, default: true },
|
|
30
|
+
labelSize: { type: [String, Number, Object], required: false },
|
|
26
31
|
hideLegend: { type: Boolean, required: false },
|
|
27
32
|
legendPosition: { type: String, required: false },
|
|
28
33
|
legendStyle: { type: [String, Object], required: false },
|
|
@@ -30,19 +35,42 @@ const props = defineProps({
|
|
|
30
35
|
tooltipTitleFormatter: { type: Function, required: false },
|
|
31
36
|
tooltip: { type: Object, required: false },
|
|
32
37
|
theme: { type: Object, required: false },
|
|
33
|
-
duration: { type: Number, required: false }
|
|
38
|
+
duration: { type: Number, required: false },
|
|
39
|
+
legendVariant: { type: String, required: false },
|
|
40
|
+
tooltipVariant: { type: String, required: false },
|
|
41
|
+
tooltipRoundness: { type: String, required: false },
|
|
42
|
+
loading: { type: Boolean, required: false },
|
|
43
|
+
loadingLabel: { type: String, required: false }
|
|
34
44
|
});
|
|
45
|
+
defineSlots();
|
|
35
46
|
const themeVars = computed(() => themeToVars(props.theme));
|
|
36
47
|
const stages = computed(() => {
|
|
37
48
|
const cats = categoriesToSeries(props.categories);
|
|
38
49
|
return props.data.map((value, i) => ({
|
|
39
50
|
name: cats[i]?.name ?? String(i),
|
|
40
51
|
value,
|
|
52
|
+
formattedValue: props.valueFormatter?.(value, i),
|
|
41
53
|
fill: cats[i]?.color ?? `var(--chart-color-${i})`,
|
|
42
54
|
color: cats[i]?.color ?? `var(--chart-color-${i})`
|
|
43
55
|
}));
|
|
44
56
|
});
|
|
45
57
|
const isLayered = computed(() => props.variant === "layered");
|
|
58
|
+
const showValueLabel = computed(() => props.showValueLabel ?? true);
|
|
59
|
+
const NAMED_LABEL_SIZES = { sm: 10, md: 12, lg: 14 };
|
|
60
|
+
function toPixels(size) {
|
|
61
|
+
if (size === void 0 || size === "auto") return void 0;
|
|
62
|
+
return typeof size === "number" ? size : NAMED_LABEL_SIZES[size];
|
|
63
|
+
}
|
|
64
|
+
const labelSizes = computed(() => {
|
|
65
|
+
const size = props.labelSize;
|
|
66
|
+
const perRole = typeof size === "object" ? size : void 0;
|
|
67
|
+
return {
|
|
68
|
+
base: toPixels(typeof size === "object" ? size.base : size),
|
|
69
|
+
value: perRole?.value,
|
|
70
|
+
percentage: perRole?.percentage,
|
|
71
|
+
name: perRole?.name
|
|
72
|
+
};
|
|
73
|
+
});
|
|
46
74
|
const layeredData = computed(() => {
|
|
47
75
|
const maxValue = stages.value[0]?.value ?? 0;
|
|
48
76
|
return stages.value.map((stage, stageIndex) => ({
|
|
@@ -50,10 +78,24 @@ const layeredData = computed(() => {
|
|
|
50
78
|
stageIndex,
|
|
51
79
|
stageCount: stages.value.length,
|
|
52
80
|
maxValue,
|
|
53
|
-
nextValue: stages.value[stageIndex + 1]?.value ?? stage.value
|
|
81
|
+
nextValue: stages.value[stageIndex + 1]?.value ?? stage.value,
|
|
82
|
+
labelSizes: labelSizes.value,
|
|
83
|
+
showValue: showValueLabel.value,
|
|
84
|
+
showPercentage: props.showPercentage ?? true,
|
|
85
|
+
showStageLabel: props.showStageLabel ?? true
|
|
86
|
+
}));
|
|
87
|
+
});
|
|
88
|
+
const classicData = computed(() => {
|
|
89
|
+
const maxValue = stages.value[0]?.value ?? 0;
|
|
90
|
+
return stages.value.map((stage) => ({
|
|
91
|
+
...stage,
|
|
92
|
+
maxValue,
|
|
93
|
+
labelSizes: labelSizes.value,
|
|
94
|
+
showValue: showValueLabel.value,
|
|
95
|
+
showStageLabel: props.showStageLabel ?? true,
|
|
96
|
+
stageGap: props.stageGap
|
|
54
97
|
}));
|
|
55
98
|
});
|
|
56
|
-
const showValueLabel = computed(() => props.showValueLabel ?? true);
|
|
57
99
|
const legend = computed(() => legendPositionToLegendProps(props.legendPosition));
|
|
58
100
|
const legendWrapperStyle = computed(
|
|
59
101
|
() => resolveLegendWrapperStyle(props.legendPosition, toCssProperties(props.legendStyle))
|
|
@@ -73,7 +115,7 @@ function showTooltip(event, datum) {
|
|
|
73
115
|
activeDatum.value = datum;
|
|
74
116
|
updateTooltipPosition(event);
|
|
75
117
|
}
|
|
76
|
-
function
|
|
118
|
+
function clearTooltip() {
|
|
77
119
|
activeDatum.value = null;
|
|
78
120
|
}
|
|
79
121
|
</script>
|
|
@@ -85,56 +127,98 @@ function hideTooltip() {
|
|
|
85
127
|
class="vc-funnel-layered vue-chrts"
|
|
86
128
|
:style="{ height: `${height}px`, ...themeVars }"
|
|
87
129
|
>
|
|
88
|
-
<
|
|
130
|
+
<ChartSkeleton
|
|
131
|
+
v-if="loading"
|
|
132
|
+
:height="height"
|
|
133
|
+
shape="bars"
|
|
134
|
+
:label="loadingLabel ?? 'Loading'"
|
|
135
|
+
/>
|
|
136
|
+
<ChartContainer v-else width="100%" height="100%">
|
|
89
137
|
<VccsFunnelChart :margin="{ top: 16, right: 0, bottom: 8, left: 0 }">
|
|
90
138
|
<Funnel
|
|
91
139
|
:data="layeredData"
|
|
92
140
|
data-key="value"
|
|
93
141
|
name-key="name"
|
|
94
142
|
last-shape-type="rectangle"
|
|
143
|
+
stroke="none"
|
|
95
144
|
:is-animation-active="duration !== void 0 && duration !== 0"
|
|
96
145
|
>
|
|
97
146
|
<template #shape="shapeProps">
|
|
98
|
-
|
|
147
|
+
<!--
|
|
148
|
+
Bind only what the shape reads. vccs also passes fill/stroke
|
|
149
|
+
(`stroke` defaults to `#fff`); those fall through onto the root
|
|
150
|
+
`<g>` and SVG-inherit onto every label, stroking the glyphs so
|
|
151
|
+
they read as extra-bold. CSS `font-weight` cannot undo that.
|
|
152
|
+
-->
|
|
153
|
+
<slot
|
|
154
|
+
name="shape"
|
|
99
155
|
v-bind="shapeProps"
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
156
|
+
:on-mouseenter="showTooltip"
|
|
157
|
+
:on-mousemove="updateTooltipPosition"
|
|
158
|
+
:on-mouseleave="clearTooltip"
|
|
159
|
+
>
|
|
160
|
+
<FunnelLayeredShape
|
|
161
|
+
:payload="shapeProps.payload"
|
|
162
|
+
:parent-view-box="shapeProps.parentViewBox"
|
|
163
|
+
@mouseenter="showTooltip"
|
|
164
|
+
@mousemove="updateTooltipPosition"
|
|
165
|
+
@mouseleave="clearTooltip"
|
|
166
|
+
/>
|
|
167
|
+
</slot>
|
|
104
168
|
</template>
|
|
105
169
|
</Funnel>
|
|
106
170
|
</VccsFunnelChart>
|
|
107
171
|
</ChartContainer>
|
|
108
172
|
|
|
109
|
-
<
|
|
110
|
-
v-if="!hideTooltip && activeDatum"
|
|
111
|
-
|
|
112
|
-
:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
/>
|
|
118
|
-
<span class="vc-funnel-layered__name">{{ activeDatum.name }}</span>
|
|
119
|
-
<strong class="vc-funnel-layered__value">{{
|
|
120
|
-
activeDatum.value.toLocaleString()
|
|
121
|
-
}}</strong>
|
|
122
|
-
</div>
|
|
173
|
+
<FunnelTooltip
|
|
174
|
+
v-if="!props.hideTooltip && activeDatum"
|
|
175
|
+
:datum="activeDatum"
|
|
176
|
+
:x="tooltipPosition.x"
|
|
177
|
+
:y="tooltipPosition.y"
|
|
178
|
+
:variant="tooltipVariant"
|
|
179
|
+
:roundness="tooltipRoundness"
|
|
180
|
+
/>
|
|
123
181
|
</div>
|
|
124
182
|
|
|
125
|
-
<div v-else class="vue-chrts" :style="themeVars">
|
|
126
|
-
|
|
183
|
+
<div v-else ref="chartContainer" class="vc-funnel vue-chrts" :style="themeVars">
|
|
184
|
+
<ChartSkeleton
|
|
185
|
+
v-if="loading"
|
|
186
|
+
:height="height"
|
|
187
|
+
shape="bars"
|
|
188
|
+
:label="loadingLabel ?? 'Loading'"
|
|
189
|
+
/>
|
|
190
|
+
<ChartContainer v-else width="100%" :height="height">
|
|
127
191
|
<VccsFunnelChart>
|
|
128
192
|
<Funnel
|
|
129
|
-
:data="
|
|
193
|
+
:data="classicData"
|
|
130
194
|
data-key="value"
|
|
131
195
|
name-key="name"
|
|
132
196
|
:last-shape-type="lastShapeType ?? 'triangle'"
|
|
197
|
+
stroke="none"
|
|
133
198
|
:is-animation-active="duration !== void 0 && duration !== 0"
|
|
134
199
|
>
|
|
135
|
-
<
|
|
200
|
+
<template #shape="shapeProps">
|
|
201
|
+
<slot
|
|
202
|
+
name="shape"
|
|
203
|
+
v-bind="shapeProps"
|
|
204
|
+
:on-mouseenter="showTooltip"
|
|
205
|
+
:on-mousemove="updateTooltipPosition"
|
|
206
|
+
:on-mouseleave="clearTooltip"
|
|
207
|
+
>
|
|
208
|
+
<FunnelClassicShape
|
|
209
|
+
:x="shapeProps.x"
|
|
210
|
+
:y="shapeProps.y"
|
|
211
|
+
:upper-width="shapeProps.upperWidth"
|
|
212
|
+
:lower-width="shapeProps.lowerWidth"
|
|
213
|
+
:height="shapeProps.height"
|
|
214
|
+
:payload="shapeProps.payload"
|
|
215
|
+
@mouseenter="showTooltip"
|
|
216
|
+
@mousemove="updateTooltipPosition"
|
|
217
|
+
@mouseleave="clearTooltip"
|
|
218
|
+
/>
|
|
219
|
+
</slot>
|
|
220
|
+
</template>
|
|
136
221
|
</Funnel>
|
|
137
|
-
<Tooltip v-if="!hideTooltip" :content="ChartTooltip" :is-animation-active="false" />
|
|
138
222
|
<Legend
|
|
139
223
|
v-if="!hideLegend"
|
|
140
224
|
:align="legend.align"
|
|
@@ -143,10 +227,18 @@ function hideTooltip() {
|
|
|
143
227
|
:wrapper-style="legendWrapperStyle"
|
|
144
228
|
>
|
|
145
229
|
<template #content="slotProps">
|
|
146
|
-
<ChartLegend v-bind="slotProps" />
|
|
230
|
+
<ChartLegend v-bind="slotProps" :variant="legendVariant" />
|
|
147
231
|
</template>
|
|
148
232
|
</Legend>
|
|
149
233
|
</VccsFunnelChart>
|
|
150
234
|
</ChartContainer>
|
|
235
|
+
<FunnelTooltip
|
|
236
|
+
v-if="!props.hideTooltip && activeDatum"
|
|
237
|
+
:datum="activeDatum"
|
|
238
|
+
:x="tooltipPosition.x"
|
|
239
|
+
:y="tooltipPosition.y"
|
|
240
|
+
:variant="tooltipVariant"
|
|
241
|
+
:roundness="tooltipRoundness"
|
|
242
|
+
/>
|
|
151
243
|
</div>
|
|
152
244
|
</template>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FunnelLayeredDatum } from "./internal/FunnelLayeredShape.vue.js";
|
|
1
2
|
import type { FunnelChartProps } from "../types/charts.js";
|
|
2
3
|
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
4
|
props: import("vue").PublicProps & __VLS_PrettifyLocal<FunnelChartProps<T>> & (typeof globalThis extends {
|
|
@@ -5,7 +6,13 @@ declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
|
|
|
5
6
|
} ? P : {});
|
|
6
7
|
expose: (exposed: {}) => void;
|
|
7
8
|
attrs: any;
|
|
8
|
-
slots: {
|
|
9
|
+
slots: {
|
|
10
|
+
shape(props: Record<string, unknown> & {
|
|
11
|
+
onMouseenter: (event: MouseEvent, datum: FunnelLayeredDatum) => void;
|
|
12
|
+
onMousemove: (event: MouseEvent) => void;
|
|
13
|
+
onMouseleave: () => void;
|
|
14
|
+
}): unknown;
|
|
15
|
+
};
|
|
9
16
|
emit: {};
|
|
10
17
|
}>) => import("vue").VNode & {
|
|
11
18
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
@@ -5,7 +5,11 @@ declare const __VLS_export: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_se
|
|
|
5
5
|
} ? P : {});
|
|
6
6
|
expose: (exposed: {}) => void;
|
|
7
7
|
attrs: any;
|
|
8
|
-
slots: {
|
|
8
|
+
slots: {
|
|
9
|
+
tooltip?: (props: {
|
|
10
|
+
values: T | undefined;
|
|
11
|
+
}) => unknown;
|
|
12
|
+
};
|
|
9
13
|
emit: {};
|
|
10
14
|
}>) => import("vue").VNode & {
|
|
11
15
|
__ctx?: Awaited<typeof __VLS_setup>;
|