@leavepulse/ui 0.29.4 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/LpChart-CzgCGaAA.css +8 -0
- package/dist/chunks/LpActionBar-CxmBZpGu.js +173 -0
- package/dist/chunks/{LpAppShell-BJk3b-u4.js → LpAppShell-DnocEJAu.js} +1 -1
- package/dist/chunks/{LpCodeBlock-rS_6mO3D.js → LpCodeBlock-BWX1KZwk.js} +7 -13
- package/dist/chunks/LpColorPicker-CFC6iaNK.js +491 -0
- package/dist/chunks/{LpDisclosure-BnwP4Q-b.js → LpDisclosure-C1_YkMrd.js} +8 -2
- package/dist/chunks/{LpLogViewer-CVaGfTF_.js → LpLogViewer-TSRyt3t9.js} +3 -6
- package/dist/chunks/{LpSidebar-C_Mr-JPI.js → LpSidebar-CWSbczWi.js} +11 -2
- package/dist/component-names.d.ts +1 -1
- package/dist/components/LpActionBar.vue.d.ts +84 -0
- package/dist/components/LpActionBar.vue.js +4 -0
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpChart.vue.js +814 -2
- package/dist/components/LpCodeBlock.vue.js +1 -1
- package/dist/components/LpColorPicker.vue.d.ts +124 -0
- package/dist/components/LpColorPicker.vue.js +4 -0
- package/dist/components/LpDisclosure.vue.js +1 -1
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.d.ts +2 -2
- package/dist/components/LpSidebar.vue.d.ts +21 -0
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/actionBar.d.ts +19 -0
- package/dist/components/actionBar.js +31 -0
- package/dist/components/color.d.ts +65 -0
- package/dist/components/color.js +158 -0
- package/dist/composables/useClipboard.d.ts +46 -0
- package/dist/composables/useClipboard.js +28 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +154 -135
- package/package.json +1 -1
- package/src/component-names.ts +2 -0
- package/src/components/LpActionBar.vue +268 -0
- package/src/components/LpChart.vue +337 -63
- package/src/components/LpCodeBlock.vue +5 -10
- package/src/components/LpColorPicker.vue +784 -0
- package/src/components/LpDisclosure.vue +17 -2
- package/src/components/LpLogViewer.vue +5 -8
- package/src/components/LpSidebar.vue +42 -6
- package/src/components/actionBar.ts +73 -0
- package/src/components/color.ts +257 -0
- package/src/composables/useClipboard.ts +107 -0
- package/src/index.ts +21 -0
- package/dist/chunks/LpChart-Bmw7y34Z.js +0 -672
|
@@ -1,4 +1,816 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineComponent, ref, computed, onMounted, onBeforeUnmount, watch, shallowRef, openBlock, createElementBlock, createElementVNode, toDisplayString, createCommentVNode, renderSlot, Fragment, renderList, normalizeClass, normalizeStyle, createVNode, withCtx, unref, withDirectives, vShow, createBlock, createTextVNode } from "vue";
|
|
2
|
+
import { Motion } from "motion-v";
|
|
3
|
+
import { prefersReducedMotion, easeTravel } from "../composables/easing.js";
|
|
4
|
+
import { usePillTransition } from "../composables/usePillTransition.js";
|
|
5
|
+
import { useReveal } from "../composables/useReveal.js";
|
|
6
|
+
import { normalise, pointCount, resolveLabels, extent, axisTicks, niceScale, plotHeight, plotWidth, xAt, yAt, areaPath, linePath, barPath, nearestIndex, formatTick } from "./chart.js";
|
|
7
|
+
import { _ as _sfc_main$1 } from "../chunks/LpContextMenu-B6WBLleS.js";
|
|
8
|
+
import { _ as _sfc_main$2 } from "../chunks/LpIcon-CCnX5_2j.js";
|
|
9
|
+
import { _ as _sfc_main$3 } from "../chunks/LpShift-DXNsauQZ.js";
|
|
10
|
+
import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
|
|
11
|
+
import '../assets/LpChart-CzgCGaAA.css';const _hoisted_1 = {
|
|
12
|
+
key: 0,
|
|
13
|
+
class: "flex items-start justify-between gap-3"
|
|
14
|
+
};
|
|
15
|
+
const _hoisted_2 = { class: "flex flex-col gap-0.5" };
|
|
16
|
+
const _hoisted_3 = {
|
|
17
|
+
key: 0,
|
|
18
|
+
class: "text-sm font-medium text-ink"
|
|
19
|
+
};
|
|
20
|
+
const _hoisted_4 = {
|
|
21
|
+
key: 1,
|
|
22
|
+
class: "text-xs text-muted"
|
|
23
|
+
};
|
|
24
|
+
const _hoisted_5 = {
|
|
25
|
+
key: 1,
|
|
26
|
+
class: "flex flex-wrap items-center gap-x-4 gap-y-1.5"
|
|
27
|
+
};
|
|
28
|
+
const _hoisted_6 = ["onClick"];
|
|
29
|
+
const _hoisted_7 = { class: "text-muted-strong group-hover:text-ink" };
|
|
30
|
+
const _hoisted_8 = {
|
|
31
|
+
key: 0,
|
|
32
|
+
class: "text-[10px] text-muted",
|
|
33
|
+
title: "Measured on the right axis"
|
|
34
|
+
};
|
|
35
|
+
const _hoisted_9 = {
|
|
36
|
+
key: 0,
|
|
37
|
+
class: "flex size-full flex-col items-center justify-center gap-2 rounded-control border border-dashed border-line text-muted"
|
|
38
|
+
};
|
|
39
|
+
const _hoisted_10 = { class: "text-xs" };
|
|
40
|
+
const _hoisted_11 = ["width", "height"];
|
|
41
|
+
const _hoisted_12 = ["id"];
|
|
42
|
+
const _hoisted_13 = ["stop-color"];
|
|
43
|
+
const _hoisted_14 = ["stop-color"];
|
|
44
|
+
const _hoisted_15 = ["id"];
|
|
45
|
+
const _hoisted_16 = ["x", "height", "width"];
|
|
46
|
+
const _hoisted_17 = ["to"];
|
|
47
|
+
const _hoisted_18 = { key: 0 };
|
|
48
|
+
const _hoisted_19 = ["x1", "x2", "y1", "y2"];
|
|
49
|
+
const _hoisted_20 = ["x", "y"];
|
|
50
|
+
const _hoisted_21 = ["x", "y"];
|
|
51
|
+
const _hoisted_22 = ["x1", "x2", "y1", "y2"];
|
|
52
|
+
const _hoisted_23 = ["x", "y", "width", "height"];
|
|
53
|
+
const _hoisted_24 = ["clip-path"];
|
|
54
|
+
const _hoisted_25 = ["d", "fill", "opacity"];
|
|
55
|
+
const _hoisted_26 = ["d", "fill"];
|
|
56
|
+
const _hoisted_27 = ["d", "stroke", "stroke-dasharray"];
|
|
57
|
+
const _hoisted_28 = ["cx", "cy", "fill"];
|
|
58
|
+
const _hoisted_29 = { key: 3 };
|
|
59
|
+
const _hoisted_30 = ["x1", "x2", "y1", "y2"];
|
|
60
|
+
const _hoisted_31 = ["cx", "cy", "fill"];
|
|
61
|
+
const _hoisted_32 = ["x", "y"];
|
|
62
|
+
const _hoisted_33 = { class: "mb-1 text-[11px] font-medium text-muted-strong" };
|
|
63
|
+
const _hoisted_34 = { class: "flex flex-col gap-1" };
|
|
64
|
+
const _hoisted_35 = { class: "flex items-center gap-1.5 text-muted" };
|
|
65
|
+
const _hoisted_36 = { class: "font-mono tabular-nums text-ink" };
|
|
66
|
+
const SETTLE_MS = 260;
|
|
67
|
+
const CARD_W = 150;
|
|
68
|
+
const CARD_GAP = 14;
|
|
69
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
70
|
+
__name: "LpChart",
|
|
71
|
+
props: {
|
|
72
|
+
series: {},
|
|
73
|
+
labels: {},
|
|
74
|
+
type: { default: "line" },
|
|
75
|
+
height: { default: 240 },
|
|
76
|
+
title: {},
|
|
77
|
+
subtitle: {},
|
|
78
|
+
smooth: { default: 0.9 },
|
|
79
|
+
showPoints: { type: Boolean },
|
|
80
|
+
showGrid: { type: Boolean, default: true },
|
|
81
|
+
showXAxis: { type: Boolean, default: true },
|
|
82
|
+
showYAxis: { type: Boolean, default: true },
|
|
83
|
+
showLegend: { type: Boolean, default: true },
|
|
84
|
+
yTicks: { default: 4 },
|
|
85
|
+
includeZero: { type: Boolean },
|
|
86
|
+
unit: {},
|
|
87
|
+
rightUnit: {},
|
|
88
|
+
formatValue: {},
|
|
89
|
+
formatRightValue: {},
|
|
90
|
+
stacked: { type: Boolean },
|
|
91
|
+
animate: { type: Boolean, default: true },
|
|
92
|
+
emptyLabel: { default: "No data" },
|
|
93
|
+
menuItems: {}
|
|
94
|
+
},
|
|
95
|
+
emits: ["pointClick", "pointContextMenu"],
|
|
96
|
+
setup(__props, { emit: __emit }) {
|
|
97
|
+
const props = __props;
|
|
98
|
+
const emit = __emit;
|
|
99
|
+
const root = ref(null);
|
|
100
|
+
const width = ref(0);
|
|
101
|
+
let ro = null;
|
|
102
|
+
const { el: revealAnchor, revealed } = useReveal({ immediate: () => !props.animate });
|
|
103
|
+
const drawing = computed(() => props.animate && revealed.value);
|
|
104
|
+
onMounted(() => {
|
|
105
|
+
revealAnchor.value = root.value;
|
|
106
|
+
if (!root.value || typeof ResizeObserver === "undefined") return;
|
|
107
|
+
ro = new ResizeObserver(([entry]) => {
|
|
108
|
+
width.value = entry.contentRect.width;
|
|
109
|
+
});
|
|
110
|
+
ro.observe(root.value);
|
|
111
|
+
width.value = root.value.clientWidth;
|
|
112
|
+
});
|
|
113
|
+
onBeforeUnmount(() => {
|
|
114
|
+
ro?.disconnect();
|
|
115
|
+
ro = null;
|
|
116
|
+
if (presenceFrame) cancelAnimationFrame(presenceFrame);
|
|
117
|
+
if (scaleFrame) cancelAnimationFrame(scaleFrame);
|
|
118
|
+
if (rightFrame) cancelAnimationFrame(rightFrame);
|
|
119
|
+
if (gutterFrame) cancelAnimationFrame(gutterFrame);
|
|
120
|
+
});
|
|
121
|
+
const hidden = ref(/* @__PURE__ */ new Set());
|
|
122
|
+
const toggleSeries = (name) => {
|
|
123
|
+
const next = new Set(hidden.value);
|
|
124
|
+
if (next.has(name)) next.delete(name);
|
|
125
|
+
else next.add(name);
|
|
126
|
+
if (next.size < props.series.length) hidden.value = next;
|
|
127
|
+
};
|
|
128
|
+
const allSeries = computed(() => normalise(props.series));
|
|
129
|
+
const visible = computed(() => allSeries.value.filter((s) => !hidden.value.has(s.name)));
|
|
130
|
+
const presence = ref(/* @__PURE__ */ new Map());
|
|
131
|
+
let presenceFrame;
|
|
132
|
+
const presenceOf = (name) => presence.value.get(name) ?? 1;
|
|
133
|
+
watch(
|
|
134
|
+
// A string, not the Set: `hidden` is replaced wholesale on every toggle, and a
|
|
135
|
+
// watcher handed the object itself compares the new reference to the old one
|
|
136
|
+
// and sees a change it cannot describe — or, with a stale copy in the array,
|
|
137
|
+
// no change at all. The names that are currently off say it exactly.
|
|
138
|
+
() => allSeries.value.map((s) => `${s.name}:${hidden.value.has(s.name) ? 0 : 1}`).join(","),
|
|
139
|
+
() => {
|
|
140
|
+
const targets = new Map(allSeries.value.map((s) => [s.name, hidden.value.has(s.name) ? 0 : 1]));
|
|
141
|
+
if (!props.animate || prefersReducedMotion()) {
|
|
142
|
+
presence.value = targets;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const from = new Map(allSeries.value.map((s) => [s.name, presenceOf(s.name)]));
|
|
146
|
+
if ([...targets].every(([name, to]) => from.get(name) === to)) return;
|
|
147
|
+
const start = performance.now();
|
|
148
|
+
if (presenceFrame) cancelAnimationFrame(presenceFrame);
|
|
149
|
+
const step = (now) => {
|
|
150
|
+
const t = Math.min(1, (now - start) / SETTLE_MS);
|
|
151
|
+
const eased = easeTravel(t);
|
|
152
|
+
const next = /* @__PURE__ */ new Map();
|
|
153
|
+
for (const [name, to] of targets) {
|
|
154
|
+
const a = from.get(name) ?? to;
|
|
155
|
+
next.set(name, a + (to - a) * eased);
|
|
156
|
+
}
|
|
157
|
+
presence.value = next;
|
|
158
|
+
presenceFrame = t < 1 ? requestAnimationFrame(step) : void 0;
|
|
159
|
+
};
|
|
160
|
+
presenceFrame = requestAnimationFrame(step);
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
const count = computed(() => pointCount(allSeries.value));
|
|
164
|
+
const labels = computed(() => resolveLabels(props.series, props.labels, count.value));
|
|
165
|
+
const banded = computed(() => props.type === "bar");
|
|
166
|
+
const hasData = computed(
|
|
167
|
+
() => allSeries.value.some((s) => s.values.some((v) => v != null))
|
|
168
|
+
);
|
|
169
|
+
const zeroBased = computed(() => props.includeZero || props.type === "bar");
|
|
170
|
+
const stackTotals = computed(() => {
|
|
171
|
+
if (!(props.type === "bar" && props.stacked)) return null;
|
|
172
|
+
const pos = [];
|
|
173
|
+
const neg = [];
|
|
174
|
+
for (let i = 0; i < count.value; i++) {
|
|
175
|
+
let p = 0;
|
|
176
|
+
let n = 0;
|
|
177
|
+
for (const s of visible.value) {
|
|
178
|
+
const v = s.values[i];
|
|
179
|
+
if (v == null) continue;
|
|
180
|
+
if (v >= 0) p += v;
|
|
181
|
+
else n += v;
|
|
182
|
+
}
|
|
183
|
+
pos.push(p);
|
|
184
|
+
neg.push(n);
|
|
185
|
+
}
|
|
186
|
+
return { pos, neg };
|
|
187
|
+
});
|
|
188
|
+
const declaredRight = computed(() => allSeries.value.filter((s) => s.axis === "right"));
|
|
189
|
+
const declaredLeft = computed(() => allSeries.value.filter((s) => s.axis === "left"));
|
|
190
|
+
const onRight = computed(() => visible.value.filter((s) => s.axis === "right"));
|
|
191
|
+
const onLeft = computed(() => {
|
|
192
|
+
const left = visible.value.filter((s) => s.axis === "left");
|
|
193
|
+
return left.length || !declaredRight.value.length ? left : visible.value;
|
|
194
|
+
});
|
|
195
|
+
const hasRightAxis = computed(
|
|
196
|
+
() => declaredRight.value.length > 0 && declaredLeft.value.length > 0
|
|
197
|
+
);
|
|
198
|
+
const scaleFor = (members) => {
|
|
199
|
+
const totals = stackTotals.value;
|
|
200
|
+
const raw = totals ? { min: Math.min(0, ...totals.neg), max: Math.max(0, ...totals.pos) } : extent(members, zeroBased.value);
|
|
201
|
+
return niceScale(raw.min, raw.max, props.yTicks);
|
|
202
|
+
};
|
|
203
|
+
const targetScale = computed(
|
|
204
|
+
() => scaleFor(onLeft.value.length ? onLeft.value : declaredLeft.value)
|
|
205
|
+
);
|
|
206
|
+
const scale = ref(targetScale.value);
|
|
207
|
+
let scaleFrame;
|
|
208
|
+
watch(targetScale, (to, from) => {
|
|
209
|
+
if (!props.animate || prefersReducedMotion() || !from) {
|
|
210
|
+
scale.value = to;
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (from.min === to.min && from.max === to.max && from.step === to.step) return;
|
|
214
|
+
const start = performance.now();
|
|
215
|
+
if (scaleFrame) cancelAnimationFrame(scaleFrame);
|
|
216
|
+
const tick = (now) => {
|
|
217
|
+
const t = Math.min(1, (now - start) / SETTLE_MS);
|
|
218
|
+
const eased = easeTravel(t);
|
|
219
|
+
scale.value = t < 1 ? {
|
|
220
|
+
min: from.min + (to.min - from.min) * eased,
|
|
221
|
+
max: from.max + (to.max - from.max) * eased,
|
|
222
|
+
/* The step stays the target's: it decides how many ticks there are,
|
|
223
|
+
and a travelling one would add and drop labels mid-move. */
|
|
224
|
+
step: to.step
|
|
225
|
+
} : to;
|
|
226
|
+
scaleFrame = t < 1 ? requestAnimationFrame(tick) : void 0;
|
|
227
|
+
};
|
|
228
|
+
scaleFrame = requestAnimationFrame(tick);
|
|
229
|
+
});
|
|
230
|
+
const targetRightScale = computed(() => {
|
|
231
|
+
if (!hasRightAxis.value) return null;
|
|
232
|
+
const members = onRight.value.length ? onRight.value : declaredRight.value;
|
|
233
|
+
const raw = extent(members, zeroBased.value);
|
|
234
|
+
const steps = Math.max(
|
|
235
|
+
1,
|
|
236
|
+
axisTicks(targetScale.value.min, targetScale.value.max, targetScale.value.step).length - 1
|
|
237
|
+
);
|
|
238
|
+
const nice = niceScale(raw.min, raw.max, steps);
|
|
239
|
+
return { min: nice.min, max: nice.min + nice.step * steps, step: nice.step };
|
|
240
|
+
});
|
|
241
|
+
const rightScale = ref(targetRightScale.value);
|
|
242
|
+
let rightFrame;
|
|
243
|
+
watch(targetRightScale, (to, from) => {
|
|
244
|
+
if (!to || !from || !props.animate || prefersReducedMotion()) {
|
|
245
|
+
rightScale.value = to;
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (from.min === to.min && from.max === to.max && from.step === to.step) return;
|
|
249
|
+
const start = performance.now();
|
|
250
|
+
if (rightFrame) cancelAnimationFrame(rightFrame);
|
|
251
|
+
const tick = (now) => {
|
|
252
|
+
const t = Math.min(1, (now - start) / SETTLE_MS);
|
|
253
|
+
const eased = easeTravel(t);
|
|
254
|
+
rightScale.value = t < 1 ? {
|
|
255
|
+
min: from.min + (to.min - from.min) * eased,
|
|
256
|
+
max: from.max + (to.max - from.max) * eased,
|
|
257
|
+
step: to.step
|
|
258
|
+
} : to;
|
|
259
|
+
rightFrame = t < 1 ? requestAnimationFrame(tick) : void 0;
|
|
260
|
+
};
|
|
261
|
+
rightFrame = requestAnimationFrame(tick);
|
|
262
|
+
});
|
|
263
|
+
const fmtWith = (v, unit, f) => `${f ? f(v) : formatTick(v)}${unit ?? ""}`;
|
|
264
|
+
const fmt = (v) => fmtWith(v, props.unit, props.formatValue);
|
|
265
|
+
const fmtRight = (v) => fmtWith(v, props.rightUnit ?? props.unit, props.formatRightValue ?? props.formatValue);
|
|
266
|
+
const fmtSeries = (s, v) => s.axis === "right" && hasRightAxis.value ? fmtRight(v) : fmt(v);
|
|
267
|
+
const scaleOf = (s) => s.axis === "right" && rightScale.value ? rightScale.value : scale.value;
|
|
268
|
+
const yLabels = computed(
|
|
269
|
+
() => axisTicks(targetScale.value.min, targetScale.value.max, targetScale.value.step).map(fmt)
|
|
270
|
+
);
|
|
271
|
+
const rightLabels = computed(
|
|
272
|
+
() => targetRightScale.value ? axisTicks(
|
|
273
|
+
targetRightScale.value.min,
|
|
274
|
+
targetRightScale.value.max,
|
|
275
|
+
targetRightScale.value.step
|
|
276
|
+
).map(fmtRight) : []
|
|
277
|
+
);
|
|
278
|
+
const gutter = (labels2) => 12 + Math.max(...labels2.map((l) => l.length), 1) * 7;
|
|
279
|
+
const targetGutters = computed(() => ({
|
|
280
|
+
left: props.showYAxis ? gutter(yLabels.value) : 8,
|
|
281
|
+
right: props.showYAxis && rightLabels.value.length ? gutter(rightLabels.value) : 12
|
|
282
|
+
}));
|
|
283
|
+
const gutters = ref(targetGutters.value);
|
|
284
|
+
let gutterFrame;
|
|
285
|
+
watch(targetGutters, (to, from) => {
|
|
286
|
+
if (!props.animate || prefersReducedMotion() || !from) {
|
|
287
|
+
gutters.value = to;
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (from.left === to.left && from.right === to.right) return;
|
|
291
|
+
const start = performance.now();
|
|
292
|
+
if (gutterFrame) cancelAnimationFrame(gutterFrame);
|
|
293
|
+
const tick = (now) => {
|
|
294
|
+
const t = Math.min(1, (now - start) / SETTLE_MS);
|
|
295
|
+
const eased = easeTravel(t);
|
|
296
|
+
gutters.value = t < 1 ? {
|
|
297
|
+
left: from.left + (to.left - from.left) * eased,
|
|
298
|
+
right: from.right + (to.right - from.right) * eased
|
|
299
|
+
} : to;
|
|
300
|
+
gutterFrame = t < 1 ? requestAnimationFrame(tick) : void 0;
|
|
301
|
+
};
|
|
302
|
+
gutterFrame = requestAnimationFrame(tick);
|
|
303
|
+
});
|
|
304
|
+
const box = computed(() => ({
|
|
305
|
+
width: width.value,
|
|
306
|
+
height: props.height,
|
|
307
|
+
left: gutters.value.left,
|
|
308
|
+
right: gutters.value.right,
|
|
309
|
+
top: 10,
|
|
310
|
+
bottom: props.showXAxis ? 26 : 8
|
|
311
|
+
}));
|
|
312
|
+
const geo = computed(() => ({
|
|
313
|
+
min: scale.value.min,
|
|
314
|
+
max: scale.value.max,
|
|
315
|
+
box: box.value,
|
|
316
|
+
count: count.value,
|
|
317
|
+
banded: banded.value
|
|
318
|
+
}));
|
|
319
|
+
const geoOf = (s) => ({ ...geo.value, ...scaleOf(s) });
|
|
320
|
+
const gridLines = computed(() => {
|
|
321
|
+
const left = axisTicks(targetScale.value.min, targetScale.value.max, targetScale.value.step);
|
|
322
|
+
const right = targetRightScale.value ? axisTicks(
|
|
323
|
+
targetRightScale.value.min,
|
|
324
|
+
targetRightScale.value.max,
|
|
325
|
+
targetRightScale.value.step
|
|
326
|
+
) : [];
|
|
327
|
+
const span = targetScale.value.max - targetScale.value.min || 1;
|
|
328
|
+
const plot = plotHeight(box.value);
|
|
329
|
+
return left.map((v, i) => {
|
|
330
|
+
const fraction = (v - targetScale.value.min) / span;
|
|
331
|
+
return {
|
|
332
|
+
v,
|
|
333
|
+
/*
|
|
334
|
+
* Keyed by POSITION, not by value. A tick keyed by its value is a
|
|
335
|
+
* different element every time the range moves, so hiding a series
|
|
336
|
+
* replaced the whole axis at once — the numbers jumped to their new
|
|
337
|
+
* places instead of moving there.
|
|
338
|
+
*/
|
|
339
|
+
key: i,
|
|
340
|
+
y: box.value.top + plot - fraction * plot,
|
|
341
|
+
label: fmt(v),
|
|
342
|
+
rightLabel: right[i] != null ? fmtRight(right[i]) : ""
|
|
343
|
+
};
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
const xLabelStride = computed(() => {
|
|
347
|
+
const w = plotWidth(box.value);
|
|
348
|
+
if (w <= 0 || count.value === 0) return 1;
|
|
349
|
+
const longest = Math.max(...labels.value.map((l) => l.length), 1);
|
|
350
|
+
return Math.max(1, Math.ceil(count.value * (longest * 6.5 + 16) / w));
|
|
351
|
+
});
|
|
352
|
+
const xLabels = computed(
|
|
353
|
+
() => labels.value.map((label, i) => ({ label, i, x: xAt(i, count.value, box.value, banded.value) })).filter(({ i }) => i % xLabelStride.value === 0 || i === count.value - 1)
|
|
354
|
+
);
|
|
355
|
+
const baseY = computed(
|
|
356
|
+
() => yAt(
|
|
357
|
+
Math.max(scale.value.min, Math.min(scale.value.max, 0)),
|
|
358
|
+
scale.value.min,
|
|
359
|
+
scale.value.max,
|
|
360
|
+
box.value
|
|
361
|
+
)
|
|
362
|
+
);
|
|
363
|
+
const lines = computed(
|
|
364
|
+
() => allSeries.value.map((s) => {
|
|
365
|
+
const g = geoOf(s);
|
|
366
|
+
const p = presenceOf(s.name);
|
|
367
|
+
const base = Math.min(Math.max(0, g.min), g.max);
|
|
368
|
+
const values = p === 1 ? s.values : s.values.map((v) => v == null ? null : base + (v - base) * p);
|
|
369
|
+
return {
|
|
370
|
+
...s,
|
|
371
|
+
values,
|
|
372
|
+
d: linePath(values, g, props.smooth),
|
|
373
|
+
fill: props.type === "area" ? areaPath(values, g, props.smooth) : "",
|
|
374
|
+
/* A dashed series is a reference level (a budget, a target), not a
|
|
375
|
+
measurement — dotting it implies readings that aren't there. */
|
|
376
|
+
points: props.showPoints && !s.dashed,
|
|
377
|
+
scale: scaleOf(s),
|
|
378
|
+
/* Fades only at the end of the collapse: going transparent while it is
|
|
379
|
+
still visibly moving loses the travel that explains what happened. */
|
|
380
|
+
alpha: Math.min(1, p * 3)
|
|
381
|
+
};
|
|
382
|
+
})
|
|
383
|
+
);
|
|
384
|
+
const bars = computed(() => {
|
|
385
|
+
if (props.type !== "bar" || count.value === 0 || plotWidth(box.value) <= 0) return [];
|
|
386
|
+
const band = plotWidth(box.value) / count.value;
|
|
387
|
+
const inner = Math.min(band * 0.6, props.stacked ? 46 : 30 * (visible.value.length || 1));
|
|
388
|
+
const n = visible.value.length || 1;
|
|
389
|
+
const slot = props.stacked ? inner : inner / n;
|
|
390
|
+
const out = [];
|
|
391
|
+
const posTop = new Array(count.value).fill(0);
|
|
392
|
+
const negTop = new Array(count.value).fill(0);
|
|
393
|
+
visible.value.forEach((s, si) => {
|
|
394
|
+
for (let i = 0; i < count.value; i++) {
|
|
395
|
+
const v = s.values[i];
|
|
396
|
+
if (v == null) continue;
|
|
397
|
+
const centre = xAt(i, count.value, box.value, true);
|
|
398
|
+
const x = props.stacked ? centre - inner / 2 : centre - inner / 2 + si * slot;
|
|
399
|
+
let y;
|
|
400
|
+
let h;
|
|
401
|
+
if (props.stacked) {
|
|
402
|
+
const prev = v >= 0 ? posTop[i] : negTop[i];
|
|
403
|
+
const from = yAt(prev, scale.value.min, scale.value.max, box.value);
|
|
404
|
+
const to = yAt(prev + v, scale.value.min, scale.value.max, box.value);
|
|
405
|
+
y = Math.min(from, to);
|
|
406
|
+
h = Math.abs(to - from);
|
|
407
|
+
if (v >= 0) posTop[i] += v;
|
|
408
|
+
else negTop[i] += v;
|
|
409
|
+
} else {
|
|
410
|
+
const sc = scaleOf(s);
|
|
411
|
+
const top = yAt(v, sc.min, sc.max, box.value);
|
|
412
|
+
const base = yAt(Math.max(sc.min, Math.min(sc.max, 0)), sc.min, sc.max, box.value);
|
|
413
|
+
y = Math.min(top, base);
|
|
414
|
+
h = Math.abs(top - base);
|
|
415
|
+
}
|
|
416
|
+
out.push({
|
|
417
|
+
key: `${s.name}-${i}`,
|
|
418
|
+
x,
|
|
419
|
+
y,
|
|
420
|
+
w: Math.max(1, slot - (props.stacked ? 0 : 2)),
|
|
421
|
+
h: Math.max(v === 0 ? 0 : 1, h),
|
|
422
|
+
color: s.color,
|
|
423
|
+
index: i,
|
|
424
|
+
/* A standalone bar rounds only the end it grows toward: a negative one
|
|
425
|
+
hangs off the baseline, so its square end is the top. */
|
|
426
|
+
roundTop: props.stacked || v >= 0,
|
|
427
|
+
roundBottom: props.stacked || v < 0,
|
|
428
|
+
d: ""
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
if (props.stacked) {
|
|
433
|
+
const columns = /* @__PURE__ */ new Map();
|
|
434
|
+
for (const b of out) {
|
|
435
|
+
const col = columns.get(b.index) ?? [];
|
|
436
|
+
col.push(b);
|
|
437
|
+
columns.set(b.index, col);
|
|
438
|
+
}
|
|
439
|
+
for (const col of columns.values()) {
|
|
440
|
+
const top = Math.min(...col.map((b) => b.y));
|
|
441
|
+
const bottom = Math.max(...col.map((b) => b.y + b.h));
|
|
442
|
+
for (const b of col) {
|
|
443
|
+
b.roundTop = b.y === top;
|
|
444
|
+
b.roundBottom = b.y + b.h === bottom;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
for (const b of out) {
|
|
449
|
+
b.d = barPath(b.x, b.y, b.w, b.h, 4, { top: b.roundTop, bottom: b.roundBottom });
|
|
450
|
+
}
|
|
451
|
+
return out;
|
|
452
|
+
});
|
|
453
|
+
const active = ref(-1);
|
|
454
|
+
const pointer = ref({ x: 0, y: 0 });
|
|
455
|
+
const onMove = (e) => {
|
|
456
|
+
const el = e.currentTarget;
|
|
457
|
+
const rect = el.getBoundingClientRect();
|
|
458
|
+
const x = e.clientX - rect.left;
|
|
459
|
+
pointer.value = { x, y: e.clientY - rect.top };
|
|
460
|
+
active.value = nearestIndex(x, geo.value);
|
|
461
|
+
};
|
|
462
|
+
const onLeave = () => {
|
|
463
|
+
active.value = -1;
|
|
464
|
+
};
|
|
465
|
+
const pointEvent = (originalEvent) => ({
|
|
466
|
+
index: active.value,
|
|
467
|
+
label: labels.value[active.value] ?? "",
|
|
468
|
+
values: allSeries.value.map((s) => s.values[active.value] ?? null),
|
|
469
|
+
originalEvent
|
|
470
|
+
});
|
|
471
|
+
const onClick = (e) => {
|
|
472
|
+
if (active.value < 0) return;
|
|
473
|
+
emit("pointClick", pointEvent(e));
|
|
474
|
+
};
|
|
475
|
+
const menuPoint = ref(null);
|
|
476
|
+
const menuEntries = computed(
|
|
477
|
+
() => props.menuItems && menuPoint.value ? props.menuItems(menuPoint.value) : []
|
|
478
|
+
);
|
|
479
|
+
const onContextMenu = (e) => {
|
|
480
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
481
|
+
active.value = nearestIndex(e.clientX - rect.left, geo.value);
|
|
482
|
+
if (active.value < 0) return;
|
|
483
|
+
menuPoint.value = pointEvent(e);
|
|
484
|
+
emit("pointContextMenu", menuPoint.value);
|
|
485
|
+
};
|
|
486
|
+
const activeX = computed(
|
|
487
|
+
() => active.value < 0 ? 0 : xAt(active.value, count.value, box.value, banded.value)
|
|
488
|
+
);
|
|
489
|
+
const activePoints = computed(() => {
|
|
490
|
+
if (active.value < 0) return [];
|
|
491
|
+
return visible.value.map((s) => ({ s, v: s.values[active.value] })).filter((r) => r.v != null).map(({ s, v }) => {
|
|
492
|
+
const sc = scaleOf(s);
|
|
493
|
+
return {
|
|
494
|
+
color: s.color,
|
|
495
|
+
name: s.name,
|
|
496
|
+
value: v,
|
|
497
|
+
y: yAt(v, sc.min, sc.max, box.value)
|
|
498
|
+
};
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
const tooltipRows = computed(
|
|
502
|
+
() => active.value < 0 ? [] : visible.value.map((s) => ({
|
|
503
|
+
name: s.name,
|
|
504
|
+
color: s.color,
|
|
505
|
+
text: s.values[active.value] == null ? "—" : fmtSeries(s, s.values[active.value])
|
|
506
|
+
}))
|
|
507
|
+
);
|
|
508
|
+
const tooltipTarget = computed(() => {
|
|
509
|
+
const flip = activeX.value > box.value.width - CARD_W;
|
|
510
|
+
const y = Math.min(
|
|
511
|
+
Math.max(pointer.value.y, box.value.top + 8),
|
|
512
|
+
box.value.top + plotHeight(box.value) - 8
|
|
513
|
+
);
|
|
514
|
+
return {
|
|
515
|
+
x: flip ? activeX.value - CARD_GAP : activeX.value + CARD_GAP,
|
|
516
|
+
y,
|
|
517
|
+
flip
|
|
518
|
+
};
|
|
519
|
+
});
|
|
520
|
+
const floatTransition = usePillTransition("float");
|
|
521
|
+
const revealKey = shallowRef(0);
|
|
522
|
+
watch(
|
|
523
|
+
() => [props.series, props.type],
|
|
524
|
+
() => {
|
|
525
|
+
revealKey.value++;
|
|
526
|
+
},
|
|
527
|
+
{ deep: true }
|
|
528
|
+
);
|
|
529
|
+
const clipId = `lp-chart-${Math.random().toString(36).slice(2, 9)}`;
|
|
530
|
+
const gradientId = (name, index) => `${clipId}-g-${index}-${name.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
|
|
531
|
+
return (_ctx, _cache) => {
|
|
532
|
+
return openBlock(), createElementBlock("div", {
|
|
533
|
+
ref_key: "root",
|
|
534
|
+
ref: root,
|
|
535
|
+
class: "flex w-full flex-col gap-3"
|
|
536
|
+
}, [
|
|
537
|
+
__props.title || __props.subtitle || _ctx.$slots.actions ? (openBlock(), createElementBlock("div", _hoisted_1, [
|
|
538
|
+
createElementVNode("div", _hoisted_2, [
|
|
539
|
+
__props.title ? (openBlock(), createElementBlock("span", _hoisted_3, toDisplayString(__props.title), 1)) : createCommentVNode("", true),
|
|
540
|
+
__props.subtitle ? (openBlock(), createElementBlock("span", _hoisted_4, toDisplayString(__props.subtitle), 1)) : createCommentVNode("", true)
|
|
541
|
+
]),
|
|
542
|
+
renderSlot(_ctx.$slots, "actions", {}, void 0, true)
|
|
543
|
+
])) : createCommentVNode("", true),
|
|
544
|
+
__props.showLegend && allSeries.value.length > 1 ? (openBlock(), createElementBlock("div", _hoisted_5, [
|
|
545
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(allSeries.value, (s) => {
|
|
546
|
+
return openBlock(), createElementBlock("button", {
|
|
547
|
+
key: s.name,
|
|
548
|
+
type: "button",
|
|
549
|
+
class: normalizeClass(["group flex cursor-pointer items-center gap-1.5 rounded-md text-xs outline-none transition-opacity duration-fast focus-visible:ring-2 focus-visible:ring-ring", hidden.value.has(s.name) ? "opacity-40" : ""]),
|
|
550
|
+
onClick: ($event) => toggleSeries(s.name)
|
|
551
|
+
}, [
|
|
552
|
+
createElementVNode("span", {
|
|
553
|
+
class: "size-2 shrink-0 rounded-full transition-transform duration-fast group-hover:scale-125",
|
|
554
|
+
style: normalizeStyle({ backgroundColor: s.color })
|
|
555
|
+
}, null, 4),
|
|
556
|
+
createElementVNode("span", _hoisted_7, toDisplayString(s.name), 1),
|
|
557
|
+
hasRightAxis.value && s.axis === "right" ? (openBlock(), createElementBlock("span", _hoisted_8, " ↗ ")) : createCommentVNode("", true)
|
|
558
|
+
], 10, _hoisted_6);
|
|
559
|
+
}), 128))
|
|
560
|
+
])) : createCommentVNode("", true),
|
|
561
|
+
createVNode(_sfc_main$1, {
|
|
562
|
+
items: menuEntries.value,
|
|
563
|
+
always: !!__props.menuItems
|
|
564
|
+
}, {
|
|
565
|
+
default: withCtx(() => [
|
|
566
|
+
createElementVNode("div", {
|
|
567
|
+
class: "group/plot relative",
|
|
568
|
+
style: normalizeStyle({ height: `${__props.height}px` })
|
|
569
|
+
}, [
|
|
570
|
+
!hasData.value ? (openBlock(), createElementBlock("div", _hoisted_9, [
|
|
571
|
+
createVNode(_sfc_main$2, {
|
|
572
|
+
name: "lucide:chart-no-axes-column",
|
|
573
|
+
size: 18
|
|
574
|
+
}),
|
|
575
|
+
createElementVNode("span", _hoisted_10, toDisplayString(__props.emptyLabel), 1)
|
|
576
|
+
])) : width.value > 0 ? (openBlock(), createElementBlock("svg", {
|
|
577
|
+
key: 1,
|
|
578
|
+
width: width.value,
|
|
579
|
+
height: __props.height,
|
|
580
|
+
class: "overflow-visible touch-none select-none",
|
|
581
|
+
onPointermove: onMove,
|
|
582
|
+
onPointerleave: onLeave,
|
|
583
|
+
onClick,
|
|
584
|
+
onContextmenu: onContextMenu
|
|
585
|
+
}, [
|
|
586
|
+
createElementVNode("defs", null, [
|
|
587
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(lines.value, (s, i) => {
|
|
588
|
+
return openBlock(), createElementBlock("linearGradient", {
|
|
589
|
+
id: gradientId(s.name, i),
|
|
590
|
+
key: s.name,
|
|
591
|
+
x1: "0",
|
|
592
|
+
y1: "0",
|
|
593
|
+
x2: "0",
|
|
594
|
+
y2: "1"
|
|
595
|
+
}, [
|
|
596
|
+
createElementVNode("stop", {
|
|
597
|
+
offset: "0%",
|
|
598
|
+
"stop-color": s.color,
|
|
599
|
+
"stop-opacity": "0.28"
|
|
600
|
+
}, null, 8, _hoisted_13),
|
|
601
|
+
createElementVNode("stop", {
|
|
602
|
+
offset: "100%",
|
|
603
|
+
"stop-color": s.color,
|
|
604
|
+
"stop-opacity": "0.02"
|
|
605
|
+
}, null, 8, _hoisted_14)
|
|
606
|
+
], 8, _hoisted_12);
|
|
607
|
+
}), 128)),
|
|
608
|
+
createElementVNode("clipPath", {
|
|
609
|
+
id: `${clipId}-reveal`
|
|
610
|
+
}, [
|
|
611
|
+
(openBlock(), createElementBlock("rect", {
|
|
612
|
+
key: revealKey.value,
|
|
613
|
+
x: box.value.left,
|
|
614
|
+
y: 0,
|
|
615
|
+
height: __props.height,
|
|
616
|
+
width: __props.animate ? 0 : unref(plotWidth)(box.value)
|
|
617
|
+
}, [
|
|
618
|
+
drawing.value ? (openBlock(), createElementBlock("animate", {
|
|
619
|
+
key: 0,
|
|
620
|
+
attributeName: "width",
|
|
621
|
+
from: 0,
|
|
622
|
+
to: unref(plotWidth)(box.value),
|
|
623
|
+
dur: "0.95s",
|
|
624
|
+
fill: "freeze",
|
|
625
|
+
calcMode: "spline",
|
|
626
|
+
keySplines: "0.2 0.25 0.5 0.85",
|
|
627
|
+
keyTimes: "0;1"
|
|
628
|
+
}, null, 8, _hoisted_17)) : createCommentVNode("", true)
|
|
629
|
+
], 8, _hoisted_16))
|
|
630
|
+
], 8, _hoisted_15)
|
|
631
|
+
]),
|
|
632
|
+
__props.showGrid || __props.showYAxis ? (openBlock(), createElementBlock("g", _hoisted_18, [
|
|
633
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(gridLines.value, (g) => {
|
|
634
|
+
return openBlock(), createElementBlock(Fragment, {
|
|
635
|
+
key: g.key
|
|
636
|
+
}, [
|
|
637
|
+
__props.showGrid ? (openBlock(), createElementBlock("line", {
|
|
638
|
+
key: 0,
|
|
639
|
+
x1: box.value.left,
|
|
640
|
+
x2: box.value.width - box.value.right,
|
|
641
|
+
y1: g.y,
|
|
642
|
+
y2: g.y,
|
|
643
|
+
stroke: "var(--color-line)",
|
|
644
|
+
"stroke-width": "1"
|
|
645
|
+
}, null, 8, _hoisted_19)) : createCommentVNode("", true),
|
|
646
|
+
__props.showYAxis ? (openBlock(), createElementBlock("text", {
|
|
647
|
+
key: 1,
|
|
648
|
+
x: box.value.left - 8,
|
|
649
|
+
y: g.y,
|
|
650
|
+
"text-anchor": "end",
|
|
651
|
+
"dominant-baseline": "middle",
|
|
652
|
+
class: "fill-[var(--color-muted)] text-[10px] tabular-nums"
|
|
653
|
+
}, toDisplayString(g.label), 9, _hoisted_20)) : createCommentVNode("", true),
|
|
654
|
+
__props.showYAxis && g.rightLabel ? (openBlock(), createElementBlock("text", {
|
|
655
|
+
key: 2,
|
|
656
|
+
x: box.value.width - box.value.right + 8,
|
|
657
|
+
y: g.y,
|
|
658
|
+
"text-anchor": "start",
|
|
659
|
+
"dominant-baseline": "middle",
|
|
660
|
+
class: "fill-[var(--color-muted)] text-[10px] tabular-nums"
|
|
661
|
+
}, toDisplayString(g.rightLabel), 9, _hoisted_21)) : createCommentVNode("", true)
|
|
662
|
+
], 64);
|
|
663
|
+
}), 128))
|
|
664
|
+
])) : createCommentVNode("", true),
|
|
665
|
+
scale.value.min < 0 && scale.value.max > 0 ? (openBlock(), createElementBlock("line", {
|
|
666
|
+
key: 1,
|
|
667
|
+
x1: box.value.left,
|
|
668
|
+
x2: box.value.width - box.value.right,
|
|
669
|
+
y1: baseY.value,
|
|
670
|
+
y2: baseY.value,
|
|
671
|
+
stroke: "var(--color-line-strong)",
|
|
672
|
+
"stroke-width": "1"
|
|
673
|
+
}, null, 8, _hoisted_22)) : createCommentVNode("", true),
|
|
674
|
+
active.value >= 0 && banded.value ? (openBlock(), createElementBlock("rect", {
|
|
675
|
+
key: 2,
|
|
676
|
+
x: activeX.value - unref(plotWidth)(box.value) / count.value / 2,
|
|
677
|
+
y: box.value.top,
|
|
678
|
+
width: unref(plotWidth)(box.value) / count.value,
|
|
679
|
+
height: unref(plotHeight)(box.value),
|
|
680
|
+
fill: "var(--color-surface-soft)",
|
|
681
|
+
opacity: "0.5"
|
|
682
|
+
}, null, 8, _hoisted_23)) : createCommentVNode("", true),
|
|
683
|
+
createElementVNode("g", {
|
|
684
|
+
"clip-path": `url(#${clipId}-reveal)`
|
|
685
|
+
}, [
|
|
686
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(bars.value, (b) => {
|
|
687
|
+
return openBlock(), createElementBlock("path", {
|
|
688
|
+
key: b.key,
|
|
689
|
+
d: b.d,
|
|
690
|
+
fill: b.color,
|
|
691
|
+
class: "transition-opacity duration-fast",
|
|
692
|
+
opacity: active.value < 0 || active.value === b.index ? 1 : 0.45
|
|
693
|
+
}, null, 8, _hoisted_25);
|
|
694
|
+
}), 128)),
|
|
695
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(lines.value, (s, i) => {
|
|
696
|
+
return openBlock(), createElementBlock("g", {
|
|
697
|
+
key: `s-${s.name}`,
|
|
698
|
+
style: normalizeStyle({ opacity: s.alpha })
|
|
699
|
+
}, [
|
|
700
|
+
withDirectives(createElementVNode("path", {
|
|
701
|
+
d: s.fill,
|
|
702
|
+
fill: `url(#${gradientId(s.name, i)})`
|
|
703
|
+
}, null, 8, _hoisted_26), [
|
|
704
|
+
[vShow, __props.type === "area"]
|
|
705
|
+
]),
|
|
706
|
+
withDirectives(createElementVNode("path", {
|
|
707
|
+
d: s.d,
|
|
708
|
+
fill: "none",
|
|
709
|
+
stroke: s.color,
|
|
710
|
+
"stroke-width": "2",
|
|
711
|
+
"stroke-linecap": "round",
|
|
712
|
+
"stroke-linejoin": "round",
|
|
713
|
+
"stroke-dasharray": s.dashed ? "5 4" : void 0
|
|
714
|
+
}, null, 8, _hoisted_27), [
|
|
715
|
+
[vShow, __props.type !== "bar"]
|
|
716
|
+
]),
|
|
717
|
+
__props.type !== "bar" && s.points ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(s.values, (v, j) => {
|
|
718
|
+
return withDirectives((openBlock(), createElementBlock("circle", {
|
|
719
|
+
key: j,
|
|
720
|
+
cx: unref(xAt)(j, count.value, box.value, banded.value),
|
|
721
|
+
cy: v == null ? 0 : unref(yAt)(v, s.scale.min, s.scale.max, box.value),
|
|
722
|
+
r: "2.5",
|
|
723
|
+
fill: s.color
|
|
724
|
+
}, null, 8, _hoisted_28)), [
|
|
725
|
+
[vShow, v != null]
|
|
726
|
+
]);
|
|
727
|
+
}), 128)) : createCommentVNode("", true)
|
|
728
|
+
], 4);
|
|
729
|
+
}), 128))
|
|
730
|
+
], 8, _hoisted_24),
|
|
731
|
+
active.value >= 0 && __props.type !== "bar" ? (openBlock(), createElementBlock("g", _hoisted_29, [
|
|
732
|
+
createElementVNode("line", {
|
|
733
|
+
x1: activeX.value,
|
|
734
|
+
x2: activeX.value,
|
|
735
|
+
y1: box.value.top,
|
|
736
|
+
y2: box.value.top + unref(plotHeight)(box.value),
|
|
737
|
+
stroke: "var(--color-line-strong)",
|
|
738
|
+
"stroke-width": "1",
|
|
739
|
+
"stroke-dasharray": "3 3"
|
|
740
|
+
}, null, 8, _hoisted_30),
|
|
741
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(activePoints.value, (p) => {
|
|
742
|
+
return openBlock(), createElementBlock("circle", {
|
|
743
|
+
key: p.name,
|
|
744
|
+
cx: activeX.value,
|
|
745
|
+
cy: p.y,
|
|
746
|
+
r: "4",
|
|
747
|
+
fill: p.color,
|
|
748
|
+
stroke: "var(--color-surface)",
|
|
749
|
+
"stroke-width": "2"
|
|
750
|
+
}, null, 8, _hoisted_31);
|
|
751
|
+
}), 128))
|
|
752
|
+
])) : createCommentVNode("", true),
|
|
753
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(xLabels.value, (l) => {
|
|
754
|
+
return withDirectives((openBlock(), createElementBlock("text", {
|
|
755
|
+
key: l.i,
|
|
756
|
+
x: l.x,
|
|
757
|
+
y: __props.height - 8,
|
|
758
|
+
"text-anchor": "middle",
|
|
759
|
+
class: normalizeClass(["fill-[var(--color-muted)] text-[10px]", active.value === l.i ? "fill-[var(--color-ink)]" : ""])
|
|
760
|
+
}, toDisplayString(l.label), 11, _hoisted_32)), [
|
|
761
|
+
[vShow, __props.showXAxis]
|
|
762
|
+
]);
|
|
763
|
+
}), 128))
|
|
764
|
+
], 40, _hoisted_11)) : createCommentVNode("", true),
|
|
765
|
+
active.value >= 0 && hasData.value ? (openBlock(), createBlock(unref(Motion), {
|
|
766
|
+
key: 2,
|
|
767
|
+
as: "div",
|
|
768
|
+
class: "pointer-events-none absolute left-0 top-0 z-(--z-tooltip) min-w-32 rounded-control border border-line bg-surface-overlay px-2.5 py-2 shadow-panel backdrop-blur group-data-[state=open]/plot:hidden",
|
|
769
|
+
initial: { opacity: 0, scale: 0.96 },
|
|
770
|
+
animate: {
|
|
771
|
+
opacity: 1,
|
|
772
|
+
scale: 1,
|
|
773
|
+
x: tooltipTarget.value.flip ? `calc(${tooltipTarget.value.x}px - 100%)` : `${tooltipTarget.value.x}px`,
|
|
774
|
+
y: `calc(${tooltipTarget.value.y}px - 50%)`
|
|
775
|
+
},
|
|
776
|
+
transition: unref(floatTransition)
|
|
777
|
+
}, {
|
|
778
|
+
default: withCtx(() => [
|
|
779
|
+
createVNode(_sfc_main$3, { axis: "both" }, {
|
|
780
|
+
default: withCtx(() => [
|
|
781
|
+
createElementVNode("div", _hoisted_33, toDisplayString(labels.value[active.value]), 1),
|
|
782
|
+
createElementVNode("div", _hoisted_34, [
|
|
783
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(tooltipRows.value, (r) => {
|
|
784
|
+
return openBlock(), createElementBlock("div", {
|
|
785
|
+
key: r.name,
|
|
786
|
+
class: "flex items-center justify-between gap-3 text-xs"
|
|
787
|
+
}, [
|
|
788
|
+
createElementVNode("span", _hoisted_35, [
|
|
789
|
+
createElementVNode("span", {
|
|
790
|
+
class: "size-1.5 shrink-0 rounded-full",
|
|
791
|
+
style: normalizeStyle({ backgroundColor: r.color })
|
|
792
|
+
}, null, 4),
|
|
793
|
+
createTextVNode(" " + toDisplayString(r.name), 1)
|
|
794
|
+
]),
|
|
795
|
+
createElementVNode("span", _hoisted_36, toDisplayString(r.text), 1)
|
|
796
|
+
]);
|
|
797
|
+
}), 128))
|
|
798
|
+
])
|
|
799
|
+
]),
|
|
800
|
+
_: 1
|
|
801
|
+
})
|
|
802
|
+
]),
|
|
803
|
+
_: 1
|
|
804
|
+
}, 8, ["animate", "transition"])) : createCommentVNode("", true)
|
|
805
|
+
], 4)
|
|
806
|
+
]),
|
|
807
|
+
_: 1
|
|
808
|
+
}, 8, ["items", "always"])
|
|
809
|
+
], 512);
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
const LpChart = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-e07e1e7d"]]);
|
|
2
814
|
export {
|
|
3
|
-
|
|
815
|
+
LpChart as default
|
|
4
816
|
};
|