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.
Files changed (82) hide show
  1. package/README.md +77 -7
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +20 -1
  4. package/dist/runtime/assets/components.css +1 -1
  5. package/dist/runtime/assets/theme.css +1 -1
  6. package/dist/runtime/components/AreaChart.d.vue.ts +5 -1
  7. package/dist/runtime/components/AreaChart.vue +84 -9
  8. package/dist/runtime/components/AreaChart.vue.d.ts +5 -1
  9. package/dist/runtime/components/BarChart.d.vue.ts +5 -1
  10. package/dist/runtime/components/BarChart.vue +125 -8
  11. package/dist/runtime/components/BarChart.vue.d.ts +5 -1
  12. package/dist/runtime/components/BubbleChart.d.vue.ts +5 -1
  13. package/dist/runtime/components/BubbleChart.vue +74 -13
  14. package/dist/runtime/components/BubbleChart.vue.d.ts +5 -1
  15. package/dist/runtime/components/DonutChart.vue +67 -15
  16. package/dist/runtime/components/FunnelChart.d.vue.ts +8 -1
  17. package/dist/runtime/components/FunnelChart.vue +127 -35
  18. package/dist/runtime/components/FunnelChart.vue.d.ts +8 -1
  19. package/dist/runtime/components/LineChart.d.vue.ts +5 -1
  20. package/dist/runtime/components/LineChart.vue +72 -7
  21. package/dist/runtime/components/LineChart.vue.d.ts +5 -1
  22. package/dist/runtime/components/RadarChart.vue +93 -12
  23. package/dist/runtime/components/RadialBarChart.vue +40 -8
  24. package/dist/runtime/components/SankeyChart.d.vue.ts +36 -0
  25. package/dist/runtime/components/SankeyChart.vue +146 -0
  26. package/dist/runtime/components/SankeyChart.vue.d.ts +36 -0
  27. package/dist/runtime/components/internal/AreaVariantDefs.d.ts +68 -0
  28. package/dist/runtime/components/internal/AreaVariantDefs.js +165 -0
  29. package/dist/runtime/components/internal/BarVariantDefs.d.ts +74 -0
  30. package/dist/runtime/components/internal/BarVariantDefs.js +200 -0
  31. package/dist/runtime/components/internal/CartesianFrame.d.vue.ts +18 -1
  32. package/dist/runtime/components/internal/CartesianFrame.vue +65 -14
  33. package/dist/runtime/components/internal/CartesianFrame.vue.d.ts +18 -1
  34. package/dist/runtime/components/internal/ChartBackground.d.ts +44 -0
  35. package/dist/runtime/components/internal/ChartBackground.js +53 -0
  36. package/dist/runtime/components/internal/ChartDot.d.ts +85 -0
  37. package/dist/runtime/components/internal/ChartDot.js +59 -0
  38. package/dist/runtime/components/internal/ChartLegend.d.vue.ts +24 -1
  39. package/dist/runtime/components/internal/ChartLegend.vue +11 -3
  40. package/dist/runtime/components/internal/ChartLegend.vue.d.ts +24 -1
  41. package/dist/runtime/components/internal/ChartSkeleton.d.vue.ts +18 -0
  42. package/dist/runtime/components/internal/ChartSkeleton.vue +64 -0
  43. package/dist/runtime/components/internal/ChartSkeleton.vue.d.ts +18 -0
  44. package/dist/runtime/components/internal/ChartTooltip.vue +8 -2
  45. package/dist/runtime/components/internal/CubeBarShape.d.vue.ts +9 -1
  46. package/dist/runtime/components/internal/CubeBarShape.vue +8 -8
  47. package/dist/runtime/components/internal/CubeBarShape.vue.d.ts +9 -1
  48. package/dist/runtime/components/internal/FunnelClassicShape.d.vue.ts +81 -0
  49. package/dist/runtime/components/internal/FunnelClassicShape.vue +97 -0
  50. package/dist/runtime/components/internal/FunnelClassicShape.vue.d.ts +81 -0
  51. package/dist/runtime/components/internal/FunnelLayeredShape.d.vue.ts +21 -0
  52. package/dist/runtime/components/internal/FunnelLayeredShape.vue +71 -37
  53. package/dist/runtime/components/internal/FunnelLayeredShape.vue.d.ts +21 -0
  54. package/dist/runtime/components/internal/FunnelTooltip.d.vue.ts +16 -0
  55. package/dist/runtime/components/internal/FunnelTooltip.vue +26 -0
  56. package/dist/runtime/components/internal/FunnelTooltip.vue.d.ts +16 -0
  57. package/dist/runtime/components/internal/PolarCenterSync.d.ts +38 -0
  58. package/dist/runtime/components/internal/PolarCenterSync.js +27 -0
  59. package/dist/runtime/components/internal/RadarGradientDefs.d.ts +46 -0
  60. package/dist/runtime/components/internal/RadarGradientDefs.js +46 -0
  61. package/dist/runtime/components/internal/SegmentVariantDefs.d.ts +67 -0
  62. package/dist/runtime/components/internal/SegmentVariantDefs.js +51 -0
  63. package/dist/runtime/components/internal/StrokeGradientDefs.d.ts +36 -0
  64. package/dist/runtime/components/internal/StrokeGradientDefs.js +35 -0
  65. package/dist/runtime/components/internal/VariantBarShape.d.vue.ts +36 -0
  66. package/dist/runtime/components/internal/VariantBarShape.vue +53 -0
  67. package/dist/runtime/components/internal/VariantBarShape.vue.d.ts +36 -0
  68. package/dist/runtime/components/internal/tooltipContent.d.ts +13 -0
  69. package/dist/runtime/components/internal/tooltipContent.js +5 -0
  70. package/dist/runtime/types/charts.d.ts +192 -12
  71. package/dist/runtime/types/shared.d.ts +37 -0
  72. package/dist/runtime/types.d.ts +3 -0
  73. package/dist/runtime/utils/axis.d.ts +12 -1
  74. package/dist/runtime/utils/axis.js +23 -0
  75. package/dist/runtime/utils/background.d.ts +36 -0
  76. package/dist/runtime/utils/background.js +140 -0
  77. package/dist/runtime/utils/gradient.d.ts +8 -0
  78. package/dist/runtime/utils/gradient.js +3 -0
  79. package/dist/runtime/utils/theme.js +14 -0
  80. package/dist/runtime/utils/variants.d.ts +101 -0
  81. package/dist/runtime/utils/variants.js +48 -0
  82. 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: colorByCat.get(name)
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
- <ChartContainer width="100%" :height="height">
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
- :name="xLabel"
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
- :name="yLabel"
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="ChartTooltip" :cursor="cursor" :is-animation-active="false" />
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 ChartTooltip from "./internal/ChartTooltip.vue";
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
- name: cats[i]?.name ?? String(i),
35
- value,
36
- // vccs reads `fill` directly from the data entry to color each sector
37
- fill: cats[i]?.color ?? `var(--chart-color-${i})`
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
- () => props.type === DonutType.Half ? { startAngle: 180, endAngle: 0 } : { startAngle: 0, endAngle: 360 }
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
- <ChartContainer width="100%" height="100%">
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="ChartTooltip" :is-animation-active="false" />
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
- LabelList,
7
- Legend,
8
- Tooltip
6
+ Legend
9
7
  } from "vccs";
10
8
  import ChartContainer from "./internal/ChartContainer";
11
- import ChartTooltip from "./internal/ChartTooltip.vue";
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 hideTooltip() {
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
- <ChartContainer width="100%" height="100%">
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
- <FunnelLayeredShape
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
- @mouseenter="showTooltip"
101
- @mousemove="updateTooltipPosition"
102
- @mouseleave="hideTooltip"
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
- <div
110
- v-if="!hideTooltip && activeDatum"
111
- class="vc-funnel-layered__tooltip"
112
- :style="{ left: `${tooltipPosition.x}px`, top: `${tooltipPosition.y}px` }"
113
- >
114
- <span
115
- class="vc-funnel-layered__dot"
116
- :style="{ background: activeDatum.color }"
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
- <ChartContainer width="100%" :height="height">
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="stages"
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
- <LabelList v-if="showValueLabel" data-key="value" position="right" />
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>;