@leavepulse/ui 0.24.1 → 0.25.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.
Files changed (91) hide show
  1. package/dist/assets/LpNumberFlow-rZkwQwlU.css +116 -0
  2. package/dist/assets/LpRollingText-CR1mb0Vt.css +74 -0
  3. package/dist/assets/LpSparkline-0mKTq7pb.css +51 -0
  4. package/dist/assets/LpTooltip-B61SYd4K.css +17 -0
  5. package/dist/assets/LpUptimeBar-B98o9krI.css +18 -0
  6. package/dist/chunks/LpChart-tNXs1ttr.js +671 -0
  7. package/dist/chunks/{LpConfirmDialog-CObRah9c.js → LpConfirmDialog-CjOGcj1e.js} +32 -4
  8. package/dist/chunks/{LpFileTree-H7Ho8uoq.js → LpFileTree-BaV7f0O7.js} +1 -1
  9. package/dist/chunks/{LpFileTreeNode-BEZIvxHi.js → LpFileTreeNode-BzpBa7R7.js} +5 -7
  10. package/dist/chunks/{LpLogViewer-B1aLUVdz.js → LpLogViewer-CcQ3s3Fp.js} +2 -1
  11. package/dist/chunks/{LpMapPicker-DFKrd1Ne.js → LpMapPicker-CXvoTrAe.js} +2 -4
  12. package/dist/chunks/{LpNotificationBell-X1M30e0X.js → LpNotificationBell-Dvu-b6Vn.js} +56 -27
  13. package/dist/chunks/LpNumberFlow-amWYaeuA.js +221 -0
  14. package/dist/chunks/LpPie-BkjWlfSw.js +525 -0
  15. package/dist/chunks/LpProgress-DoIP4jQR.js +198 -0
  16. package/dist/chunks/LpRollingText-CM5gkSeF.js +50 -0
  17. package/dist/chunks/LpSparkline-C6xQ2dwv.js +175 -0
  18. package/dist/chunks/{LpStat-CdbEFOvn.js → LpStat-CDanpzU0.js} +43 -5
  19. package/dist/chunks/{LpThemeSwitcher-BgpJgrnP.js → LpThemeSwitcher-7mAK6bI7.js} +2 -4
  20. package/dist/chunks/LpTooltip-hCLPAqMG.js +131 -0
  21. package/dist/chunks/LpUptimeBar-DcQ8HikG.js +152 -0
  22. package/dist/component-names.d.ts +1 -1
  23. package/dist/components/LpChart.vue.d.ts +88 -0
  24. package/dist/components/LpChart.vue.js +4 -0
  25. package/dist/components/LpConfirmDialog.vue.d.ts +18 -2
  26. package/dist/components/LpConfirmDialog.vue.js +1 -1
  27. package/dist/components/LpFileTree.vue.d.ts +1 -1
  28. package/dist/components/LpFileTree.vue.js +1 -1
  29. package/dist/components/LpFileTreeNode.vue.js +1 -1
  30. package/dist/components/LpLightbox.vue.d.ts +1 -1
  31. package/dist/components/LpLogViewer.vue.d.ts +1 -1
  32. package/dist/components/LpLogViewer.vue.js +1 -1
  33. package/dist/components/LpMapPicker.vue.js +1 -1
  34. package/dist/components/LpNotificationBell.vue.d.ts +12 -0
  35. package/dist/components/LpNotificationBell.vue.js +1 -1
  36. package/dist/components/LpNumberFlow.vue.d.ts +35 -0
  37. package/dist/components/LpNumberFlow.vue.js +4 -0
  38. package/dist/components/LpPie.vue.d.ts +134 -0
  39. package/dist/components/LpPie.vue.js +4 -0
  40. package/dist/components/LpProgress.vue.d.ts +56 -2
  41. package/dist/components/LpProgress.vue.js +1 -1
  42. package/dist/components/LpRollingText.vue.d.ts +33 -0
  43. package/dist/components/LpRollingText.vue.js +4 -0
  44. package/dist/components/LpSparkline.vue.d.ts +48 -0
  45. package/dist/components/LpSparkline.vue.js +4 -0
  46. package/dist/components/LpStat.vue.d.ts +25 -2
  47. package/dist/components/LpStat.vue.js +1 -1
  48. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  49. package/dist/components/LpTooltip.vue.d.ts +24 -3
  50. package/dist/components/LpTooltip.vue.js +1 -1
  51. package/dist/components/LpUptimeBar.vue.js +1 -1
  52. package/dist/components/chart.d.ts +180 -0
  53. package/dist/components/chart.js +353 -0
  54. package/dist/composables/easing.d.ts +46 -0
  55. package/dist/composables/easing.js +23 -0
  56. package/dist/composables/usePillTransition.d.ts +13 -1
  57. package/dist/composables/usePillTransition.js +22 -2
  58. package/dist/composables/useReveal.d.ts +26 -0
  59. package/dist/composables/useReveal.js +45 -0
  60. package/dist/composables/useSettled.js +2 -2
  61. package/dist/composables/useShift.js +2 -4
  62. package/dist/index.d.ts +14 -0
  63. package/dist/index.js +125 -102
  64. package/package.json +2 -1
  65. package/src/component-names.ts +5 -0
  66. package/src/components/LpChart.vue +846 -0
  67. package/src/components/LpConfirmDialog.vue +49 -0
  68. package/src/components/LpFileTreeNode.vue +5 -9
  69. package/src/components/LpLogViewer.vue +2 -3
  70. package/src/components/LpMapPicker.vue +2 -5
  71. package/src/components/LpNotificationBell.vue +76 -15
  72. package/src/components/LpNumberFlow.vue +524 -0
  73. package/src/components/LpPie.vue +778 -0
  74. package/src/components/LpProgress.vue +283 -6
  75. package/src/components/LpRollingText.vue +157 -0
  76. package/src/components/LpSparkline.vue +312 -0
  77. package/src/components/LpStat.vue +52 -4
  78. package/src/components/LpTooltip.vue +185 -3
  79. package/src/components/LpUptimeBar.vue +150 -53
  80. package/src/components/chart.ts +686 -0
  81. package/src/composables/easing.ts +88 -0
  82. package/src/composables/usePillTransition.ts +71 -6
  83. package/src/composables/useReveal.ts +113 -0
  84. package/src/composables/useSettled.ts +2 -4
  85. package/src/composables/useShift.ts +2 -8
  86. package/src/index.ts +19 -0
  87. package/src/theme/useTheme.ts +2 -8
  88. package/src/tokens/tokens.css +36 -0
  89. package/dist/chunks/LpProgress-BhI0IbjC.js +0 -37
  90. package/dist/chunks/LpTooltip-D1hbjQWG.js +0 -63
  91. package/dist/chunks/LpUptimeBar-CB366k0O.js +0 -127
@@ -0,0 +1,525 @@
1
+ import { defineComponent, computed, ref, watch, onBeforeUnmount, openBlock, createElementBlock, toDisplayString, createCommentVNode, normalizeStyle, createVNode, createElementVNode, createBlock, withCtx, withModifiers, Fragment, renderList, normalizeClass, renderSlot } from "vue";
2
+ import { prefersReducedMotion, easeTravel } from "../composables/easing.js";
3
+ import { useReveal } from "../composables/useReveal.js";
4
+ import { resolveSlices, arcPath, calloutLabels, formatTick } from "../components/chart.js";
5
+ import { _ as _sfc_main$2 } from "./LpContextMenu-D0mzXqA7.js";
6
+ import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
7
+ import { _ as _sfc_main$4 } from "./LpNumberFlow-amWYaeuA.js";
8
+ import { _ as _sfc_main$5 } from "./LpRollingText-CM5gkSeF.js";
9
+ import { _ as _sfc_main$6 } from "./LpShift-DXNsauQZ.js";
10
+ import { _ as _sfc_main$3 } from "./LpTooltip-hCLPAqMG.js";
11
+ const _hoisted_1 = {
12
+ key: 0,
13
+ class: "flex flex-col gap-0.5"
14
+ };
15
+ const _hoisted_2 = {
16
+ key: 0,
17
+ class: "text-sm font-medium text-ink"
18
+ };
19
+ const _hoisted_3 = {
20
+ key: 1,
21
+ class: "text-xs text-muted"
22
+ };
23
+ const _hoisted_4 = { class: "text-xs" };
24
+ const _hoisted_5 = { class: "flex flex-wrap items-start gap-x-6 gap-y-3" };
25
+ const _hoisted_6 = { class: "flex items-center gap-1.5" };
26
+ const _hoisted_7 = ["width", "height"];
27
+ const _hoisted_8 = { key: 0 };
28
+ const _hoisted_9 = ["id"];
29
+ const _hoisted_10 = ["d"];
30
+ const _hoisted_11 = ["clip-path"];
31
+ const _hoisted_12 = ["d", "fill", "transform", "opacity"];
32
+ const _hoisted_13 = ["d", "onPointerenter", "onPointerleave", "onPointerdown", "onContextmenu"];
33
+ const _hoisted_14 = { key: 1 };
34
+ const _hoisted_15 = ["opacity", "onPointerenter", "onPointerleave", "onPointerdown", "onContextmenu"];
35
+ const _hoisted_16 = ["points", "stroke"];
36
+ const _hoisted_17 = ["x", "y", "text-anchor"];
37
+ const _hoisted_18 = {
38
+ key: 0,
39
+ class: "pointer-events-none absolute inset-0 flex flex-col items-center justify-center text-center"
40
+ };
41
+ const _hoisted_19 = ["aria-pressed", "onPointerenter", "onPointerleave", "onPointerdown", "onContextmenu"];
42
+ const _hoisted_20 = { class: "truncate text-muted-strong group-hover:text-ink" };
43
+ const _hoisted_21 = {
44
+ key: 0,
45
+ class: "shrink-0 text-muted"
46
+ };
47
+ const _hoisted_22 = { class: "ml-auto shrink-0 pl-2 font-mono tabular-nums text-muted" };
48
+ const ELBOW = 16;
49
+ const RUN = 14;
50
+ const LABEL_LINE = 17;
51
+ const _sfc_main = /* @__PURE__ */ defineComponent({
52
+ __name: "LpPie",
53
+ props: {
54
+ slices: {},
55
+ type: { default: "donut" },
56
+ size: { default: 180 },
57
+ innerRatio: { default: 0.62 },
58
+ title: {},
59
+ subtitle: {},
60
+ maxSlices: { default: 6 },
61
+ otherLabel: { default: "Other" },
62
+ sortBySize: { type: Boolean },
63
+ unit: {},
64
+ formatValue: {},
65
+ labels: { type: Boolean },
66
+ labelWidth: { default: 140 },
67
+ showLegend: { type: Boolean },
68
+ showPercent: { type: Boolean, default: true },
69
+ showTotal: { type: Boolean, default: true },
70
+ padAngle: { default: 1.5 },
71
+ cornerRadius: { default: 4 },
72
+ hoverOffset: { default: 8 },
73
+ animate: { type: Boolean, default: true },
74
+ duration: { default: 900 },
75
+ menuItems: {},
76
+ emptyLabel: { default: "No data" }
77
+ },
78
+ emits: ["sliceClick", "selectionChange", "sliceContextMenu"],
79
+ setup(__props, { emit: __emit }) {
80
+ const props = __props;
81
+ const emit = __emit;
82
+ const resolved = computed(
83
+ () => resolveSlices(
84
+ props.slices,
85
+ props.maxSlices || void 0,
86
+ props.otherLabel,
87
+ !props.sortBySize
88
+ )
89
+ );
90
+ const hasData = computed(() => resolved.value.slices.length > 0);
91
+ const legendShown = computed(() => props.showLegend ?? !props.labels);
92
+ const fmt = (v) => `${props.formatValue ? props.formatValue(v) : formatTick(v)}${props.unit ?? ""}`;
93
+ const pct = (fraction) => {
94
+ const p = fraction * 100;
95
+ if (p > 0 && p < 0.1) return "<0.1%";
96
+ return `${Number(p.toFixed(p < 10 ? 1 : 0))}%`;
97
+ };
98
+ const arcAngles = computed(() => {
99
+ const list = resolved.value.slices;
100
+ const pad = list.length > 1 ? props.padAngle * Math.PI / 180 : 0;
101
+ let angle = -Math.PI / 2;
102
+ return list.map((s, index) => {
103
+ const sweep2 = s.fraction * Math.PI * 2;
104
+ const start = angle;
105
+ angle += sweep2;
106
+ const gap = Math.min(pad, sweep2 * 0.5);
107
+ return { index, slice: s, start: start + gap / 2, end: angle - gap / 2, mid: start + sweep2 / 2 };
108
+ });
109
+ });
110
+ const canvasWidth = computed(
111
+ () => props.labels ? props.size + (props.labelWidth + ELBOW + RUN) * 2 : props.size
112
+ );
113
+ const canvasHeight = computed(() => {
114
+ if (!props.labels) return props.size;
115
+ const right = arcAngles.value.filter((a) => Math.cos(a.mid) >= 0).length;
116
+ const perSide = Math.max(right, arcAngles.value.length - right) + 2;
117
+ return Math.max(props.size, perSide * LABEL_LINE + 16);
118
+ });
119
+ const cx = computed(() => canvasWidth.value / 2);
120
+ const cy = computed(() => canvasHeight.value / 2);
121
+ const outer = computed(() => props.size / 2 - 2 - props.hoverOffset);
122
+ const inner = computed(
123
+ () => props.type === "donut" ? outer.value * Math.min(0.95, Math.max(0, props.innerRatio)) : 0
124
+ );
125
+ const arcs = computed(
126
+ () => arcAngles.value.map(({ slice, start, end, mid }) => ({
127
+ ...slice,
128
+ d: arcPath(cx.value, cy.value, outer.value, inner.value, start, end, props.cornerRadius),
129
+ /*
130
+ * Push along the slice's own bisector, so it moves straight out of the
131
+ * circle rather than sideways.
132
+ *
133
+ * As an SVG `transform` attribute, not a CSS `translate`: Vue binds the
134
+ * latter as an inline style, and on an SVG <path> that produced no style
135
+ * attribute at all — the wedge never moved, though the dimming beside it
136
+ * worked and made it look as if it had.
137
+ */
138
+ lift: `translate(${(Math.cos(mid) * props.hoverOffset).toFixed(2)} ${(Math.sin(mid) * props.hoverOffset).toFixed(2)})`
139
+ }))
140
+ );
141
+ const { el: revealAnchor, revealed } = useReveal({ immediate: () => !props.animate });
142
+ const sweep = ref(props.animate ? 0 : 1);
143
+ let sweepFrame = 0;
144
+ const stopSweep = () => {
145
+ if (sweepFrame) cancelAnimationFrame(sweepFrame);
146
+ sweepFrame = 0;
147
+ };
148
+ const runSweep = () => {
149
+ stopSweep();
150
+ if (prefersReducedMotion() || props.duration <= 0) {
151
+ sweep.value = 1;
152
+ return;
153
+ }
154
+ const started = performance.now();
155
+ const step = (now) => {
156
+ const t = Math.min(1, (now - started) / props.duration);
157
+ sweep.value = easeTravel(t);
158
+ sweepFrame = t < 1 ? requestAnimationFrame(step) : 0;
159
+ };
160
+ sweepFrame = requestAnimationFrame(step);
161
+ };
162
+ watch(
163
+ revealed,
164
+ (visible) => {
165
+ if (visible && props.animate) runSweep();
166
+ },
167
+ { immediate: true }
168
+ );
169
+ onBeforeUnmount(stopSweep);
170
+ const sweepId = `lp-pie-${Math.random().toString(36).slice(2, 9)}`;
171
+ const sweepPath = computed(() => {
172
+ const start = -Math.PI / 2;
173
+ const radius = outer.value + props.hoverOffset + 4;
174
+ return arcPath(cx.value, cy.value, radius, 0, start, start + Math.PI * 2 * sweep.value);
175
+ });
176
+ const callouts = computed(
177
+ () => props.labels ? calloutLabels(
178
+ arcAngles.value.map((a) => ({ index: a.index, mid: a.mid })),
179
+ cx.value,
180
+ cy.value,
181
+ outer.value,
182
+ ELBOW,
183
+ RUN,
184
+ LABEL_LINE,
185
+ canvasHeight.value
186
+ ) : []
187
+ );
188
+ const active = ref(-1);
189
+ const selected = ref(/* @__PURE__ */ new Set());
190
+ const anchor = ref(-1);
191
+ const dragging = ref(false);
192
+ const isSelected = (i) => selected.value.has(i);
193
+ const payloadFor = (i) => {
194
+ const a = arcs.value[i];
195
+ return { index: i, label: a.label, value: a.value, isOther: a.isOther };
196
+ };
197
+ const emitSelection = () => {
198
+ const indices = [...selected.value].sort((x, y) => x - y);
199
+ emit("selectionChange", indices.map(payloadFor));
200
+ };
201
+ const rangeTo = (i) => {
202
+ const from = anchor.value < 0 ? i : anchor.value;
203
+ const [lo, hi] = from <= i ? [from, i] : [i, from];
204
+ const next = new Set(selected.value);
205
+ for (let k = lo; k <= hi; k++) next.add(k);
206
+ return next;
207
+ };
208
+ const onSlicePointerDown = (i, e) => {
209
+ if (e.button === 2) return;
210
+ if (e.shiftKey) {
211
+ selected.value = rangeTo(i);
212
+ } else if (e.ctrlKey || e.metaKey) {
213
+ const next = new Set(selected.value);
214
+ if (next.has(i)) next.delete(i);
215
+ else next.add(i);
216
+ selected.value = next;
217
+ anchor.value = i;
218
+ } else {
219
+ const only = selected.value.size === 1 && selected.value.has(i);
220
+ selected.value = only ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([i]);
221
+ anchor.value = only ? -1 : i;
222
+ if (!only) beginDrag();
223
+ }
224
+ emitSelection();
225
+ emit("sliceClick", payloadFor(i));
226
+ };
227
+ const onSlicePointerEnter = (i) => {
228
+ clearTimeout(leaveTimer);
229
+ active.value = i;
230
+ if (!dragging.value || selected.value.has(i)) return;
231
+ const next = new Set(selected.value);
232
+ next.add(i);
233
+ selected.value = next;
234
+ emitSelection();
235
+ };
236
+ let leaveTimer;
237
+ const onSlicePointerLeave = (i) => {
238
+ if (active.value !== i) return;
239
+ clearTimeout(leaveTimer);
240
+ leaveTimer = setTimeout(() => {
241
+ if (active.value === i) active.value = -1;
242
+ }, 60);
243
+ };
244
+ const leaveChart = (e) => {
245
+ const to = e.relatedTarget;
246
+ if (to instanceof Element && (to.closest("[data-lp-follow]") || to.querySelector("[data-lp-follow]")))
247
+ return;
248
+ clearTimeout(leaveTimer);
249
+ active.value = -1;
250
+ endDrag();
251
+ };
252
+ const endDrag = () => {
253
+ dragging.value = false;
254
+ window.removeEventListener("pointerup", endDrag);
255
+ window.removeEventListener("pointercancel", endDrag);
256
+ };
257
+ const beginDrag = () => {
258
+ dragging.value = true;
259
+ window.addEventListener("pointerup", endDrag);
260
+ window.addEventListener("pointercancel", endDrag);
261
+ };
262
+ onBeforeUnmount(() => {
263
+ endDrag();
264
+ clearTimeout(leaveTimer);
265
+ window.removeEventListener("keydown", onKeydown);
266
+ });
267
+ const clearSelection = () => {
268
+ if (!selected.value.size) return;
269
+ selected.value = /* @__PURE__ */ new Set();
270
+ anchor.value = -1;
271
+ emitSelection();
272
+ };
273
+ const onKeydown = (e) => {
274
+ if (e.key === "Escape") clearSelection();
275
+ };
276
+ watch(
277
+ () => selected.value.size > 0,
278
+ (hasSelection) => {
279
+ if (hasSelection) window.addEventListener("keydown", onKeydown);
280
+ else window.removeEventListener("keydown", onKeydown);
281
+ }
282
+ );
283
+ const menuPoint = ref(
284
+ null
285
+ );
286
+ const menuEntries = computed(
287
+ () => props.menuItems && menuPoint.value ? props.menuItems(menuPoint.value, [...selected.value].sort((x, y) => x - y).map(payloadFor)) : []
288
+ );
289
+ const onSliceContextMenu = (i) => {
290
+ if (!selected.value.has(i)) {
291
+ selected.value = /* @__PURE__ */ new Set([i]);
292
+ anchor.value = i;
293
+ emitSelection();
294
+ }
295
+ menuPoint.value = payloadFor(i);
296
+ emit("sliceContextMenu", menuPoint.value);
297
+ };
298
+ const focused = computed(() => {
299
+ if (active.value >= 0) return active.value;
300
+ return selected.value.size === 1 ? [...selected.value][0] : -1;
301
+ });
302
+ const dimmed = (i) => selected.value.size > 0 && !selected.value.has(i);
303
+ const total = computed(() => resolved.value.total);
304
+ const selectedArcs = computed(
305
+ () => [...selected.value].sort((x, y) => x - y).map((i) => arcs.value[i])
306
+ );
307
+ const hovered = computed(() => active.value >= 0 ? arcs.value[active.value] : null);
308
+ const tipSlice = ref(null);
309
+ watch(hovered, (v) => {
310
+ if (v) tipSlice.value = v;
311
+ });
312
+ const centreValue = computed(() => {
313
+ if (focused.value >= 0) return arcs.value[focused.value].value;
314
+ if (selected.value.size > 1)
315
+ return selectedArcs.value.reduce((sum, a) => sum + a.value, 0);
316
+ return total.value;
317
+ });
318
+ const centreLabel = computed(() => {
319
+ if (focused.value >= 0) return arcs.value[focused.value].label;
320
+ if (selected.value.size > 1) return `${selected.value.size} selected`;
321
+ return "total";
322
+ });
323
+ return (_ctx, _cache) => {
324
+ return openBlock(), createElementBlock("div", {
325
+ ref_key: "revealAnchor",
326
+ ref: revealAnchor,
327
+ class: "flex flex-col gap-3"
328
+ }, [
329
+ __props.title || __props.subtitle ? (openBlock(), createElementBlock("div", _hoisted_1, [
330
+ __props.title ? (openBlock(), createElementBlock("span", _hoisted_2, toDisplayString(__props.title), 1)) : createCommentVNode("", true),
331
+ __props.subtitle ? (openBlock(), createElementBlock("span", _hoisted_3, toDisplayString(__props.subtitle), 1)) : createCommentVNode("", true)
332
+ ])) : createCommentVNode("", true),
333
+ !hasData.value ? (openBlock(), createElementBlock("div", {
334
+ key: 1,
335
+ class: "flex flex-col items-center justify-center gap-2 rounded-control border border-dashed border-line text-muted",
336
+ style: normalizeStyle({ width: `${__props.size}px`, height: `${__props.size}px` })
337
+ }, [
338
+ createVNode(_sfc_main$1, {
339
+ name: "lucide:chart-pie",
340
+ size: 18
341
+ }),
342
+ createElementVNode("span", _hoisted_4, toDisplayString(__props.emptyLabel), 1)
343
+ ], 4)) : (openBlock(), createBlock(_sfc_main$2, {
344
+ key: 2,
345
+ items: menuEntries.value,
346
+ always: !!__props.menuItems
347
+ }, {
348
+ default: withCtx(() => [
349
+ createElementVNode("div", _hoisted_5, [
350
+ createVNode(_sfc_main$3, {
351
+ delay: 80,
352
+ "follow-cursor": "",
353
+ side: "top",
354
+ disabled: !hovered.value
355
+ }, {
356
+ content: withCtx(() => [
357
+ tipSlice.value ? (openBlock(), createBlock(_sfc_main$6, {
358
+ key: 0,
359
+ axis: "width"
360
+ }, {
361
+ default: withCtx(() => [
362
+ createElementVNode("span", _hoisted_6, [
363
+ createElementVNode("span", {
364
+ class: "size-1.5 shrink-0 rounded-full transition-colors duration-fast",
365
+ style: normalizeStyle({ backgroundColor: tipSlice.value.color })
366
+ }, null, 4),
367
+ createVNode(_sfc_main$5, {
368
+ class: "text-muted-strong",
369
+ value: tipSlice.value.label,
370
+ duration: 200,
371
+ align: "start"
372
+ }, null, 8, ["value"]),
373
+ createVNode(_sfc_main$4, {
374
+ class: "font-mono text-ink",
375
+ value: tipSlice.value.value,
376
+ duration: 240,
377
+ format: fmt,
378
+ "animate-on-mount": false
379
+ }, null, 8, ["value"]),
380
+ createVNode(_sfc_main$5, {
381
+ class: "text-muted",
382
+ value: pct(tipSlice.value.fraction),
383
+ duration: 200,
384
+ align: "end"
385
+ }, null, 8, ["value"])
386
+ ])
387
+ ]),
388
+ _: 1
389
+ })) : createCommentVNode("", true)
390
+ ]),
391
+ default: withCtx(() => [
392
+ createElementVNode("div", {
393
+ class: "relative shrink-0",
394
+ style: normalizeStyle({ width: `${canvasWidth.value}px`, height: `${canvasHeight.value}px` })
395
+ }, [
396
+ (openBlock(), createElementBlock("svg", {
397
+ width: canvasWidth.value,
398
+ height: canvasHeight.value,
399
+ class: "touch-none select-none",
400
+ onPointerleave: leaveChart,
401
+ onPointerup: endDrag,
402
+ onClick: withModifiers(clearSelection, ["self"])
403
+ }, [
404
+ __props.animate ? (openBlock(), createElementBlock("defs", _hoisted_8, [
405
+ createElementVNode("clipPath", {
406
+ id: `${sweepId}-sweep`
407
+ }, [
408
+ createElementVNode("path", { d: sweepPath.value }, null, 8, _hoisted_10)
409
+ ], 8, _hoisted_9)
410
+ ])) : createCommentVNode("", true),
411
+ createElementVNode("g", {
412
+ "clip-path": __props.animate ? `url(#${sweepId}-sweep)` : void 0
413
+ }, [
414
+ (openBlock(true), createElementBlock(Fragment, null, renderList(arcs.value, (a, i) => {
415
+ return openBlock(), createElementBlock("path", {
416
+ key: a.label,
417
+ d: a.d,
418
+ fill: a.color,
419
+ class: "pointer-events-none transition-[transform,opacity] duration-medium ease-[var(--ease-emphasized)] motion-reduce:transition-none",
420
+ transform: active.value === i ? a.lift : void 0,
421
+ opacity: dimmed(i) ? 0.28 : 1
422
+ }, null, 8, _hoisted_12);
423
+ }), 128))
424
+ ], 8, _hoisted_11),
425
+ (openBlock(true), createElementBlock(Fragment, null, renderList(arcs.value, (a, i) => {
426
+ return openBlock(), createElementBlock("path", {
427
+ key: `hit-${a.label}`,
428
+ d: a.d,
429
+ fill: "transparent",
430
+ class: "cursor-pointer",
431
+ onPointerenter: ($event) => onSlicePointerEnter(i),
432
+ onPointerleave: ($event) => onSlicePointerLeave(i),
433
+ onPointerdown: ($event) => onSlicePointerDown(i, $event),
434
+ onContextmenu: ($event) => onSliceContextMenu(i)
435
+ }, null, 40, _hoisted_13);
436
+ }), 128)),
437
+ __props.labels ? (openBlock(), createElementBlock("g", _hoisted_14, [
438
+ (openBlock(true), createElementBlock(Fragment, null, renderList(callouts.value, (c) => {
439
+ return openBlock(), createElementBlock("g", {
440
+ key: `c-${c.index}`,
441
+ class: "cursor-pointer transition-opacity duration-fast",
442
+ opacity: dimmed(c.index) ? 0.35 : 1,
443
+ onPointerenter: ($event) => onSlicePointerEnter(c.index),
444
+ onPointerleave: ($event) => onSlicePointerLeave(c.index),
445
+ onPointerdown: ($event) => onSlicePointerDown(c.index, $event),
446
+ onContextmenu: ($event) => onSliceContextMenu(c.index)
447
+ }, [
448
+ createElementVNode("polyline", {
449
+ points: c.points,
450
+ fill: "none",
451
+ stroke: arcs.value[c.index].color,
452
+ "stroke-width": "1"
453
+ }, null, 8, _hoisted_16),
454
+ createElementVNode("text", {
455
+ x: c.x,
456
+ y: c.y,
457
+ "text-anchor": c.side === "right" ? "start" : "end",
458
+ "dominant-baseline": "middle",
459
+ class: normalizeClass(["text-[11px]", active.value === c.index ? "fill-[var(--color-ink)]" : "fill-[var(--color-muted-strong)]"])
460
+ }, toDisplayString(arcs.value[c.index].label) + ": " + toDisplayString(pct(arcs.value[c.index].fraction)), 11, _hoisted_17)
461
+ ], 40, _hoisted_15);
462
+ }), 128))
463
+ ])) : createCommentVNode("", true)
464
+ ], 40, _hoisted_7)),
465
+ __props.type === "donut" && (__props.showTotal || _ctx.$slots.center) ? (openBlock(), createElementBlock("div", _hoisted_18, [
466
+ renderSlot(_ctx.$slots, "center", {
467
+ total: total.value,
468
+ active: focused.value >= 0 ? arcs.value[focused.value] : null,
469
+ selection: selectedArcs.value
470
+ }, () => [
471
+ createVNode(_sfc_main$4, {
472
+ class: "font-mono text-xl font-medium text-ink",
473
+ value: centreValue.value,
474
+ duration: 360,
475
+ format: fmt,
476
+ "animate-on-mount": false
477
+ }, null, 8, ["value"]),
478
+ createVNode(_sfc_main$5, {
479
+ class: "max-w-[80%] text-[11px] text-muted",
480
+ value: centreLabel.value,
481
+ duration: 260
482
+ }, null, 8, ["value"])
483
+ ])
484
+ ])) : createCommentVNode("", true)
485
+ ], 4)
486
+ ]),
487
+ _: 3
488
+ }, 8, ["disabled"]),
489
+ legendShown.value ? (openBlock(), createElementBlock("div", {
490
+ key: 0,
491
+ class: "flex min-w-0 flex-col gap-1.5",
492
+ onPointerup: endDrag
493
+ }, [
494
+ (openBlock(true), createElementBlock(Fragment, null, renderList(arcs.value, (a, i) => {
495
+ return openBlock(), createElementBlock("button", {
496
+ key: a.label,
497
+ type: "button",
498
+ class: normalizeClass(["group flex cursor-pointer items-center gap-2 rounded-md px-1 py-0.5 text-xs outline-none transition-[opacity,background-color] duration-fast focus-visible:ring-2 focus-visible:ring-ring", [dimmed(i) ? "opacity-45" : "", isSelected(i) ? "bg-surface-soft" : ""]]),
499
+ "aria-pressed": isSelected(i),
500
+ onPointerenter: ($event) => onSlicePointerEnter(i),
501
+ onPointerleave: ($event) => onSlicePointerLeave(i),
502
+ onPointerdown: ($event) => onSlicePointerDown(i, $event),
503
+ onContextmenu: ($event) => onSliceContextMenu(i)
504
+ }, [
505
+ createElementVNode("span", {
506
+ class: "size-2 shrink-0 rounded-full transition-transform duration-fast group-hover:scale-125",
507
+ style: normalizeStyle({ backgroundColor: a.color })
508
+ }, null, 4),
509
+ createElementVNode("span", _hoisted_20, toDisplayString(a.label), 1),
510
+ a.isOther ? (openBlock(), createElementBlock("span", _hoisted_21, "(" + toDisplayString(a.count) + ")", 1)) : createCommentVNode("", true),
511
+ createElementVNode("span", _hoisted_22, toDisplayString(__props.showPercent ? pct(a.fraction) : fmt(a.value)), 1)
512
+ ], 42, _hoisted_19);
513
+ }), 128))
514
+ ], 32)) : createCommentVNode("", true)
515
+ ])
516
+ ]),
517
+ _: 3
518
+ }, 8, ["items", "always"]))
519
+ ], 512);
520
+ };
521
+ }
522
+ });
523
+ export {
524
+ _sfc_main as _
525
+ };