@leavepulse/ui 0.24.0 → 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.
- package/dist/assets/LpNumberFlow-rZkwQwlU.css +116 -0
- package/dist/assets/LpRollingText-CR1mb0Vt.css +74 -0
- package/dist/assets/LpSparkline-0mKTq7pb.css +51 -0
- package/dist/assets/LpTooltip-B61SYd4K.css +17 -0
- package/dist/assets/LpUptimeBar-B98o9krI.css +18 -0
- package/dist/chunks/{LpAppShell-CaQYQKZV.js → LpAppShell-B5k0J3Cf.js} +1 -1
- package/dist/chunks/LpChart-tNXs1ttr.js +671 -0
- package/dist/chunks/{LpCodeBlock-CSiOSPs8.js → LpCodeBlock-rS_6mO3D.js} +1 -1
- package/dist/chunks/{LpConfirmDialog-CObRah9c.js → LpConfirmDialog-CjOGcj1e.js} +32 -4
- package/dist/chunks/{LpDisclosure-DP6yfgfM.js → LpDisclosure-BnwP4Q-b.js} +1 -1
- package/dist/chunks/{LpFileTree-nU1wRRxZ.js → LpFileTree-BaV7f0O7.js} +1 -1
- package/dist/chunks/{LpFileTreeNode-D7WgUPcJ.js → LpFileTreeNode-BzpBa7R7.js} +6 -8
- package/dist/chunks/{LpLogViewer-B1aLUVdz.js → LpLogViewer-CcQ3s3Fp.js} +2 -1
- package/dist/chunks/{LpMapPicker-DFKrd1Ne.js → LpMapPicker-CXvoTrAe.js} +2 -4
- package/dist/chunks/{LpNotificationBell-X1M30e0X.js → LpNotificationBell-Dvu-b6Vn.js} +56 -27
- package/dist/chunks/LpNumberFlow-amWYaeuA.js +221 -0
- package/dist/chunks/LpPie-BkjWlfSw.js +525 -0
- package/dist/chunks/LpProgress-DoIP4jQR.js +198 -0
- package/dist/chunks/LpRollingText-CM5gkSeF.js +50 -0
- package/dist/chunks/{LpSidebar-D3GGLSE7.js → LpSidebar-7Vpy2IJc.js} +1 -1
- package/dist/chunks/{LpSidebarNav-X14xMdPa.js → LpSidebarNav-CFX5NxGs.js} +1 -1
- package/dist/chunks/LpSparkline-C6xQ2dwv.js +175 -0
- package/dist/chunks/{LpStat-CdbEFOvn.js → LpStat-CDanpzU0.js} +43 -5
- package/dist/chunks/{LpThemeSwitcher-BgpJgrnP.js → LpThemeSwitcher-7mAK6bI7.js} +2 -4
- package/dist/chunks/LpTooltip-hCLPAqMG.js +131 -0
- package/dist/chunks/LpUptimeBar-DcQ8HikG.js +152 -0
- package/dist/component-names.d.ts +1 -1
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpChart.vue.d.ts +88 -0
- package/dist/components/LpChart.vue.js +4 -0
- package/dist/components/LpCodeBlock.vue.js +1 -1
- package/dist/components/LpConfirmDialog.vue.d.ts +18 -2
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpDisclosure.vue.js +1 -1
- package/dist/components/LpFileTree.vue.d.ts +1 -1
- package/dist/components/LpFileTree.vue.js +1 -1
- package/dist/components/LpFileTreeNode.vue.js +1 -1
- package/dist/components/LpLightbox.vue.d.ts +1 -1
- package/dist/components/LpLogViewer.vue.d.ts +1 -1
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpMapPicker.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.d.ts +12 -0
- package/dist/components/LpNotificationBell.vue.js +1 -1
- package/dist/components/LpNumberFlow.vue.d.ts +35 -0
- package/dist/components/LpNumberFlow.vue.js +4 -0
- package/dist/components/LpPie.vue.d.ts +134 -0
- package/dist/components/LpPie.vue.js +4 -0
- package/dist/components/LpProgress.vue.d.ts +56 -2
- package/dist/components/LpProgress.vue.js +1 -1
- package/dist/components/LpRollingText.vue.d.ts +33 -0
- package/dist/components/LpRollingText.vue.js +4 -0
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.vue.js +1 -1
- package/dist/components/LpSparkline.vue.d.ts +48 -0
- package/dist/components/LpSparkline.vue.js +4 -0
- package/dist/components/LpStat.vue.d.ts +25 -2
- package/dist/components/LpStat.vue.js +1 -1
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpTooltip.vue.d.ts +24 -3
- package/dist/components/LpTooltip.vue.js +1 -1
- package/dist/components/LpUptimeBar.vue.js +1 -1
- package/dist/components/chart.d.ts +180 -0
- package/dist/components/chart.js +353 -0
- package/dist/composables/easing.d.ts +46 -0
- package/dist/composables/easing.js +23 -0
- package/dist/composables/usePillTransition.d.ts +13 -1
- package/dist/composables/usePillTransition.js +22 -2
- package/dist/composables/useReveal.d.ts +26 -0
- package/dist/composables/useReveal.js +45 -0
- package/dist/composables/useSettled.js +2 -2
- package/dist/composables/useShift.js +2 -4
- package/dist/index.d.ts +14 -0
- package/dist/index.js +126 -103
- package/package.json +2 -1
- package/src/component-names.ts +5 -0
- package/src/components/LpChart.vue +846 -0
- package/src/components/LpCodeBlock.vue +1 -1
- package/src/components/LpConfirmDialog.vue +49 -0
- package/src/components/LpDisclosure.vue +1 -1
- package/src/components/LpFileTreeNode.vue +9 -10
- package/src/components/LpLogViewer.vue +2 -3
- package/src/components/LpMapPicker.vue +2 -5
- package/src/components/LpNotificationBell.vue +76 -15
- package/src/components/LpNumberFlow.vue +524 -0
- package/src/components/LpPie.vue +778 -0
- package/src/components/LpProgress.vue +283 -6
- package/src/components/LpRollingText.vue +157 -0
- package/src/components/LpSidebarNav.vue +1 -1
- package/src/components/LpSparkline.vue +312 -0
- package/src/components/LpStat.vue +52 -4
- package/src/components/LpTooltip.vue +185 -3
- package/src/components/LpUptimeBar.vue +150 -53
- package/src/components/chart.ts +686 -0
- package/src/composables/easing.ts +88 -0
- package/src/composables/usePillTransition.ts +71 -6
- package/src/composables/useReveal.ts +113 -0
- package/src/composables/useSettled.ts +2 -4
- package/src/composables/useShift.ts +2 -8
- package/src/index.ts +19 -0
- package/src/theme/useTheme.ts +2 -8
- package/src/tokens/tokens.css +36 -0
- package/dist/chunks/LpProgress-BhI0IbjC.js +0 -37
- package/dist/chunks/LpTooltip-D1hbjQWG.js +0 -63
- package/dist/chunks/LpUptimeBar-CB366k0O.js +0 -127
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { defineComponent, ref, onBeforeUnmount, watch, computed, openBlock, createBlock, unref, normalizeStyle, withCtx, createElementBlock, createElementVNode, Fragment, createTextVNode, toDisplayString, createCommentVNode, createVNode, normalizeClass } from "vue";
|
|
2
|
+
import { ProgressRoot, ProgressIndicator } from "reka-ui";
|
|
3
|
+
import { prefersReducedMotion, easeOut } from "../composables/easing.js";
|
|
4
|
+
import { useReveal } from "../composables/useReveal.js";
|
|
5
|
+
import { _ as _sfc_main$1 } from "./LpNumberFlow-amWYaeuA.js";
|
|
6
|
+
const _hoisted_1 = ["width", "height"];
|
|
7
|
+
const _hoisted_2 = ["cx", "cy", "r", "stroke-width"];
|
|
8
|
+
const _hoisted_3 = ["cx", "cy", "r", "stroke", "stroke-width", "stroke-dasharray"];
|
|
9
|
+
const _hoisted_4 = {
|
|
10
|
+
key: 0,
|
|
11
|
+
class: "absolute font-mono text-[11px] font-medium text-ink"
|
|
12
|
+
};
|
|
13
|
+
const _hoisted_5 = {
|
|
14
|
+
key: 1,
|
|
15
|
+
class: "flex w-full flex-col gap-1"
|
|
16
|
+
};
|
|
17
|
+
const _hoisted_6 = { class: "flex items-baseline justify-between gap-2 text-xs" };
|
|
18
|
+
const _hoisted_7 = { class: "text-muted" };
|
|
19
|
+
const _hoisted_8 = { class: "font-mono tabular-nums text-muted-strong" };
|
|
20
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
21
|
+
__name: "LpProgress",
|
|
22
|
+
props: {
|
|
23
|
+
value: { default: 0 },
|
|
24
|
+
max: { default: 100 },
|
|
25
|
+
tone: { default: "brand" },
|
|
26
|
+
warnAt: { default: 0.8 },
|
|
27
|
+
dangerAt: { default: 0.9 },
|
|
28
|
+
title: {},
|
|
29
|
+
caption: { type: [Boolean, String] },
|
|
30
|
+
variant: { default: "bar" },
|
|
31
|
+
size: { default: 44 },
|
|
32
|
+
thickness: { default: 4 },
|
|
33
|
+
label: { type: [Boolean, String] },
|
|
34
|
+
animateOnMount: { type: Boolean, default: true },
|
|
35
|
+
duration: { default: 900 }
|
|
36
|
+
},
|
|
37
|
+
setup(__props) {
|
|
38
|
+
const props = __props;
|
|
39
|
+
const eased = ref(props.animateOnMount ? 0 : 1);
|
|
40
|
+
let raf = 0;
|
|
41
|
+
let from = props.animateOnMount ? 0 : props.value;
|
|
42
|
+
const runSweep = (ms) => {
|
|
43
|
+
if (raf) cancelAnimationFrame(raf);
|
|
44
|
+
if (prefersReducedMotion() || ms <= 0) {
|
|
45
|
+
eased.value = 1;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const t0 = performance.now();
|
|
49
|
+
const step = (now) => {
|
|
50
|
+
const t = Math.min(1, (now - t0) / ms);
|
|
51
|
+
eased.value = easeOut(t);
|
|
52
|
+
raf = t < 1 ? requestAnimationFrame(step) : 0;
|
|
53
|
+
};
|
|
54
|
+
raf = requestAnimationFrame(step);
|
|
55
|
+
};
|
|
56
|
+
onBeforeUnmount(() => {
|
|
57
|
+
if (raf) cancelAnimationFrame(raf);
|
|
58
|
+
});
|
|
59
|
+
const { el: revealAnchor, revealed } = useReveal({ immediate: () => !props.animateOnMount });
|
|
60
|
+
const bindReveal = (instance) => {
|
|
61
|
+
revealAnchor.value = instance?.$el ?? null;
|
|
62
|
+
};
|
|
63
|
+
watch(revealed, (visible) => {
|
|
64
|
+
if (visible && props.animateOnMount) runSweep(props.duration);
|
|
65
|
+
}, { immediate: true });
|
|
66
|
+
watch(
|
|
67
|
+
() => [props.value, props.max],
|
|
68
|
+
() => {
|
|
69
|
+
from = shown.value;
|
|
70
|
+
runSweep(props.duration);
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
const shown = computed(() => from + (props.value - from) * eased.value);
|
|
74
|
+
const pct = computed(
|
|
75
|
+
() => Math.max(0, Math.min(100, shown.value / props.max * 100))
|
|
76
|
+
);
|
|
77
|
+
const paintedTone = computed(() => {
|
|
78
|
+
if (props.tone !== "auto") return props.tone;
|
|
79
|
+
const fraction = props.max > 0 ? props.value / props.max : 0;
|
|
80
|
+
if (fraction >= props.dangerAt) return "danger";
|
|
81
|
+
if (fraction >= props.warnAt) return "accent";
|
|
82
|
+
return "action";
|
|
83
|
+
});
|
|
84
|
+
const captionText = computed(() => {
|
|
85
|
+
if (props.caption === void 0 || props.caption === false) return "";
|
|
86
|
+
return typeof props.caption === "string" ? props.caption : `${Math.round(pct.value)}%`;
|
|
87
|
+
});
|
|
88
|
+
const bar = {
|
|
89
|
+
brand: "bg-brand",
|
|
90
|
+
action: "bg-action",
|
|
91
|
+
danger: "bg-danger",
|
|
92
|
+
accent: "bg-accent"
|
|
93
|
+
};
|
|
94
|
+
const stroke = {
|
|
95
|
+
brand: "var(--color-brand)",
|
|
96
|
+
action: "var(--color-action)",
|
|
97
|
+
danger: "var(--color-danger)",
|
|
98
|
+
accent: "var(--color-accent)"
|
|
99
|
+
};
|
|
100
|
+
const radius = computed(() => (props.size - props.thickness) / 2);
|
|
101
|
+
const circumference = computed(() => 2 * Math.PI * radius.value);
|
|
102
|
+
const dash = computed(() => {
|
|
103
|
+
const full = circumference.value;
|
|
104
|
+
const raw = pct.value / 100 * full;
|
|
105
|
+
if (pct.value >= 100) return full;
|
|
106
|
+
return Math.min(raw, full - props.thickness * 1.5);
|
|
107
|
+
});
|
|
108
|
+
const staticCaption = computed(
|
|
109
|
+
() => typeof props.label === "string" ? props.label : null
|
|
110
|
+
);
|
|
111
|
+
return (_ctx, _cache) => {
|
|
112
|
+
return __props.variant === "ring" ? (openBlock(), createBlock(unref(ProgressRoot), {
|
|
113
|
+
key: 0,
|
|
114
|
+
ref: bindReveal,
|
|
115
|
+
"model-value": __props.value,
|
|
116
|
+
max: __props.max,
|
|
117
|
+
class: "relative inline-flex shrink-0 items-center justify-center",
|
|
118
|
+
style: normalizeStyle({ width: `${__props.size}px`, height: `${__props.size}px` })
|
|
119
|
+
}, {
|
|
120
|
+
default: withCtx(() => [
|
|
121
|
+
(openBlock(), createElementBlock("svg", {
|
|
122
|
+
width: __props.size,
|
|
123
|
+
height: __props.size,
|
|
124
|
+
class: "-rotate-90"
|
|
125
|
+
}, [
|
|
126
|
+
createElementVNode("circle", {
|
|
127
|
+
cx: __props.size / 2,
|
|
128
|
+
cy: __props.size / 2,
|
|
129
|
+
r: radius.value,
|
|
130
|
+
fill: "none",
|
|
131
|
+
stroke: "var(--color-surface-soft)",
|
|
132
|
+
"stroke-width": __props.thickness
|
|
133
|
+
}, null, 8, _hoisted_2),
|
|
134
|
+
createElementVNode("circle", {
|
|
135
|
+
cx: __props.size / 2,
|
|
136
|
+
cy: __props.size / 2,
|
|
137
|
+
r: radius.value,
|
|
138
|
+
fill: "none",
|
|
139
|
+
stroke: stroke[paintedTone.value],
|
|
140
|
+
"stroke-width": __props.thickness,
|
|
141
|
+
"stroke-linecap": "round",
|
|
142
|
+
"stroke-dasharray": `${dash.value} ${circumference.value - dash.value}`
|
|
143
|
+
}, null, 8, _hoisted_3)
|
|
144
|
+
], 8, _hoisted_1)),
|
|
145
|
+
__props.label !== false && __props.label !== void 0 ? (openBlock(), createElementBlock("span", _hoisted_4, [
|
|
146
|
+
staticCaption.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
147
|
+
createTextVNode(toDisplayString(staticCaption.value), 1)
|
|
148
|
+
], 64)) : (openBlock(), createBlock(_sfc_main$1, {
|
|
149
|
+
key: 1,
|
|
150
|
+
value: pct.value,
|
|
151
|
+
suffix: "%",
|
|
152
|
+
duration: 0,
|
|
153
|
+
"animate-on-mount": false,
|
|
154
|
+
style: { "--lp-nf-spin": "90ms" }
|
|
155
|
+
}, null, 8, ["value"]))
|
|
156
|
+
])) : createCommentVNode("", true)
|
|
157
|
+
]),
|
|
158
|
+
_: 1
|
|
159
|
+
}, 8, ["model-value", "max", "style"])) : __props.title || captionText.value ? (openBlock(), createElementBlock("div", _hoisted_5, [
|
|
160
|
+
createElementVNode("div", _hoisted_6, [
|
|
161
|
+
createElementVNode("span", _hoisted_7, toDisplayString(__props.title), 1),
|
|
162
|
+
createElementVNode("span", _hoisted_8, toDisplayString(captionText.value), 1)
|
|
163
|
+
]),
|
|
164
|
+
createVNode(unref(ProgressRoot), {
|
|
165
|
+
ref: bindReveal,
|
|
166
|
+
"model-value": __props.value,
|
|
167
|
+
max: __props.max,
|
|
168
|
+
class: "h-1.5 w-full overflow-hidden rounded-pill bg-surface-soft"
|
|
169
|
+
}, {
|
|
170
|
+
default: withCtx(() => [
|
|
171
|
+
createVNode(unref(ProgressIndicator), {
|
|
172
|
+
class: normalizeClass(["h-full rounded-pill transition-colors duration-medium", bar[paintedTone.value]]),
|
|
173
|
+
style: normalizeStyle({ width: `${pct.value}%` })
|
|
174
|
+
}, null, 8, ["class", "style"])
|
|
175
|
+
]),
|
|
176
|
+
_: 1
|
|
177
|
+
}, 8, ["model-value", "max"])
|
|
178
|
+
])) : (openBlock(), createBlock(unref(ProgressRoot), {
|
|
179
|
+
key: 2,
|
|
180
|
+
ref: bindReveal,
|
|
181
|
+
"model-value": __props.value,
|
|
182
|
+
max: __props.max,
|
|
183
|
+
class: "h-1.5 w-full overflow-hidden rounded-pill bg-surface-soft"
|
|
184
|
+
}, {
|
|
185
|
+
default: withCtx(() => [
|
|
186
|
+
createVNode(unref(ProgressIndicator), {
|
|
187
|
+
class: normalizeClass(["h-full rounded-pill transition-colors duration-medium", bar[paintedTone.value]]),
|
|
188
|
+
style: normalizeStyle({ width: `${pct.value}%` })
|
|
189
|
+
}, null, 8, ["class", "style"])
|
|
190
|
+
]),
|
|
191
|
+
_: 1
|
|
192
|
+
}, 8, ["model-value", "max"]));
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
export {
|
|
197
|
+
_sfc_main as _
|
|
198
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { defineComponent, computed, openBlock, createElementBlock, normalizeStyle, normalizeClass, createVNode, Transition, withCtx, renderSlot, createTextVNode, toDisplayString } from "vue";
|
|
2
|
+
import '../assets/LpRollingText-CR1mb0Vt.css';const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
3
|
+
__name: "LpRollingText",
|
|
4
|
+
props: {
|
|
5
|
+
value: {},
|
|
6
|
+
mode: { default: "slide" },
|
|
7
|
+
direction: { default: "up" },
|
|
8
|
+
duration: { default: 320 },
|
|
9
|
+
align: { default: "center" }
|
|
10
|
+
},
|
|
11
|
+
setup(__props) {
|
|
12
|
+
const props = __props;
|
|
13
|
+
const key = computed(() => String(props.value));
|
|
14
|
+
const name = computed(
|
|
15
|
+
() => props.mode === "flip" ? "lp-roll-flip" : `lp-roll-${props.direction}`
|
|
16
|
+
);
|
|
17
|
+
const justify = computed(
|
|
18
|
+
() => ({ start: "justify-start", center: "justify-center", end: "justify-end" })[props.align]
|
|
19
|
+
);
|
|
20
|
+
const style = computed(() => ({
|
|
21
|
+
"--lp-roll-duration": `${props.duration}ms`,
|
|
22
|
+
// The leaving copy is taken out of flow so both can occupy the same line;
|
|
23
|
+
// without this the box would grow to hold two lines mid-transition.
|
|
24
|
+
"--lp-roll-perspective": props.mode === "flip" ? "300px" : "none"
|
|
25
|
+
}));
|
|
26
|
+
return (_ctx, _cache) => {
|
|
27
|
+
return openBlock(), createElementBlock("span", {
|
|
28
|
+
class: normalizeClass(["lp-roll relative inline-flex overflow-hidden align-bottom", justify.value]),
|
|
29
|
+
style: normalizeStyle(style.value)
|
|
30
|
+
}, [
|
|
31
|
+
createVNode(Transition, { name: name.value }, {
|
|
32
|
+
default: withCtx(() => [
|
|
33
|
+
(openBlock(), createElementBlock("span", {
|
|
34
|
+
key: key.value,
|
|
35
|
+
class: "lp-roll__item"
|
|
36
|
+
}, [
|
|
37
|
+
renderSlot(_ctx.$slots, "default", { value: __props.value }, () => [
|
|
38
|
+
createTextVNode(toDisplayString(__props.value), 1)
|
|
39
|
+
])
|
|
40
|
+
]))
|
|
41
|
+
]),
|
|
42
|
+
_: 3
|
|
43
|
+
}, 8, ["name"])
|
|
44
|
+
], 6);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
export {
|
|
49
|
+
_sfc_main as _
|
|
50
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineComponent, computed, useId, openBlock, createElementBlock, Fragment, createElementVNode, mergeProps, normalizeClass, renderSlot, createCommentVNode, createVNode, createSlots, withCtx, normalizeProps, guardReactiveProps, createBlock } from "vue";
|
|
2
2
|
import { _ as _sfc_main$2 } from "./LpDrawer-DAJN5rGK.js";
|
|
3
|
-
import { _ as _sfc_main$1 } from "./LpSidebarNav-
|
|
3
|
+
import { _ as _sfc_main$1 } from "./LpSidebarNav-CFX5NxGs.js";
|
|
4
4
|
const _hoisted_1 = {
|
|
5
5
|
key: 1,
|
|
6
6
|
class: "mt-2 flex shrink-0 flex-col gap-2"
|
|
@@ -74,7 +74,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
74
74
|
disabled: item.href ? void 0 : item.disabled,
|
|
75
75
|
"aria-current": __props.itemActive(item) ? "page" : void 0,
|
|
76
76
|
"aria-disabled": item.disabled || void 0,
|
|
77
|
-
class: normalizeClass(["group/item relative flex items-center gap-3 rounded-control px-3 py-2 text-sm font-medium outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring", item.disabled ? "cursor-not-allowed text-muted/50" : __props.itemActive(item) ? "text-ink" : "text-muted hover:text-ink"]),
|
|
77
|
+
class: normalizeClass(["group/item relative flex items-center gap-3 rounded-control px-3 py-2 text-sm font-medium outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring", item.disabled ? "cursor-not-allowed text-muted/50" : __props.itemActive(item) ? "text-ink" : "text-muted hover:text-ink"]),
|
|
78
78
|
onClick: ($event) => emit("activate", item)
|
|
79
79
|
}, {
|
|
80
80
|
default: withCtx(() => [
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { defineComponent, computed, openBlock, createElementBlock, createElementVNode, createCommentVNode, normalizeStyle, normalizeClass } from "vue";
|
|
2
|
+
import { useReveal } from "../composables/useReveal.js";
|
|
3
|
+
import { normalise, extent, linePath, areaPath } from "../components/chart.js";
|
|
4
|
+
import '../assets/LpSparkline-0mKTq7pb.css';const _hoisted_1 = ["width", "height", "viewBox", "aria-label"];
|
|
5
|
+
const _hoisted_2 = { key: 0 };
|
|
6
|
+
const _hoisted_3 = ["stop-color"];
|
|
7
|
+
const _hoisted_4 = ["stop-color"];
|
|
8
|
+
const _hoisted_5 = ["d", "fill"];
|
|
9
|
+
const _hoisted_6 = ["d", "stroke"];
|
|
10
|
+
const _hoisted_7 = ["cx", "cy", "fill"];
|
|
11
|
+
const _hoisted_8 = ["aria-label"];
|
|
12
|
+
const PAD = 2;
|
|
13
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
14
|
+
__name: "LpSparkline",
|
|
15
|
+
props: {
|
|
16
|
+
data: {},
|
|
17
|
+
width: { default: 84 },
|
|
18
|
+
height: { default: 24 },
|
|
19
|
+
color: {},
|
|
20
|
+
tone: { default: "brand" },
|
|
21
|
+
area: { type: Boolean },
|
|
22
|
+
smooth: { default: 0.9 },
|
|
23
|
+
showLast: { type: Boolean },
|
|
24
|
+
min: {},
|
|
25
|
+
max: {},
|
|
26
|
+
ariaLabel: {},
|
|
27
|
+
animate: { type: Boolean, default: true },
|
|
28
|
+
duration: { default: 900 }
|
|
29
|
+
},
|
|
30
|
+
setup(__props) {
|
|
31
|
+
const props = __props;
|
|
32
|
+
const TONE = {
|
|
33
|
+
brand: "var(--color-brand)",
|
|
34
|
+
action: "var(--color-action)",
|
|
35
|
+
danger: "var(--color-danger)",
|
|
36
|
+
accent: "var(--color-accent)",
|
|
37
|
+
muted: "var(--color-muted)"
|
|
38
|
+
};
|
|
39
|
+
const stroke = computed(() => props.color ?? TONE[props.tone]);
|
|
40
|
+
const series = computed(() => normalise([{ name: "s", data: props.data }]));
|
|
41
|
+
const box = computed(() => ({
|
|
42
|
+
width: props.width,
|
|
43
|
+
height: props.height,
|
|
44
|
+
left: PAD,
|
|
45
|
+
right: PAD,
|
|
46
|
+
top: PAD,
|
|
47
|
+
bottom: PAD
|
|
48
|
+
}));
|
|
49
|
+
const bounds = computed(() => {
|
|
50
|
+
const auto = extent(series.value, false);
|
|
51
|
+
return { min: props.min ?? auto.min, max: props.max ?? auto.max };
|
|
52
|
+
});
|
|
53
|
+
const geo = computed(() => ({
|
|
54
|
+
min: bounds.value.min,
|
|
55
|
+
max: bounds.value.max,
|
|
56
|
+
box: box.value,
|
|
57
|
+
count: props.data.length,
|
|
58
|
+
banded: false
|
|
59
|
+
}));
|
|
60
|
+
const d = computed(() => linePath(series.value[0].values, geo.value, props.smooth));
|
|
61
|
+
const fill = computed(
|
|
62
|
+
() => props.area ? areaPath(series.value[0].values, geo.value, props.smooth) : ""
|
|
63
|
+
);
|
|
64
|
+
const lastPoint = computed(() => {
|
|
65
|
+
const values = series.value[0].values;
|
|
66
|
+
for (let i = values.length - 1; i >= 0; i--) {
|
|
67
|
+
const v = values[i];
|
|
68
|
+
if (v == null) continue;
|
|
69
|
+
const span = bounds.value.max - bounds.value.min || 1;
|
|
70
|
+
const w = props.width - PAD * 2;
|
|
71
|
+
const h = props.height - PAD * 2;
|
|
72
|
+
return {
|
|
73
|
+
x: props.data.length <= 1 ? PAD + w / 2 : PAD + w / (props.data.length - 1) * i,
|
|
74
|
+
y: PAD + h - (v - bounds.value.min) / span * h,
|
|
75
|
+
v
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
});
|
|
80
|
+
const hasData = computed(() => series.value[0].values.some((v) => v != null));
|
|
81
|
+
const label = computed(() => {
|
|
82
|
+
if (props.ariaLabel) return props.ariaLabel;
|
|
83
|
+
const values = series.value[0].values.filter((v) => v != null);
|
|
84
|
+
if (!values.length) return "No data";
|
|
85
|
+
const first = values[0];
|
|
86
|
+
const last = values[values.length - 1];
|
|
87
|
+
const dir = last > first ? "trending up" : last < first ? "trending down" : "flat";
|
|
88
|
+
return `${dir}, latest ${last}`;
|
|
89
|
+
});
|
|
90
|
+
const pathLength = computed(() => {
|
|
91
|
+
const n = Math.max(1, props.data.length - 1);
|
|
92
|
+
const segment = Math.hypot((props.width - PAD * 2) / n, props.height - PAD * 2);
|
|
93
|
+
return Math.ceil(segment * n + 4);
|
|
94
|
+
});
|
|
95
|
+
const { el, revealed } = useReveal({ immediate: () => !props.animate });
|
|
96
|
+
const revealStyle = computed(
|
|
97
|
+
() => props.animate ? {
|
|
98
|
+
"--lp-spark-len": `${pathLength.value}`,
|
|
99
|
+
"--lp-spark-dur": `${props.duration}ms`
|
|
100
|
+
} : void 0
|
|
101
|
+
);
|
|
102
|
+
const revealClass = computed(() => props.animate && !revealed.value ? "lp-spark--waiting" : "");
|
|
103
|
+
const gradientId = `lp-spark-${Math.random().toString(36).slice(2, 9)}`;
|
|
104
|
+
return (_ctx, _cache) => {
|
|
105
|
+
return hasData.value ? (openBlock(), createElementBlock("svg", {
|
|
106
|
+
key: 0,
|
|
107
|
+
ref_key: "el",
|
|
108
|
+
ref: el,
|
|
109
|
+
width: __props.width,
|
|
110
|
+
height: __props.height,
|
|
111
|
+
viewBox: `0 0 ${__props.width} ${__props.height}`,
|
|
112
|
+
role: "img",
|
|
113
|
+
"aria-label": label.value,
|
|
114
|
+
class: "shrink-0 overflow-visible"
|
|
115
|
+
}, [
|
|
116
|
+
__props.area ? (openBlock(), createElementBlock("defs", _hoisted_2, [
|
|
117
|
+
createElementVNode("linearGradient", {
|
|
118
|
+
id: gradientId,
|
|
119
|
+
x1: "0",
|
|
120
|
+
y1: "0",
|
|
121
|
+
x2: "0",
|
|
122
|
+
y2: "1"
|
|
123
|
+
}, [
|
|
124
|
+
createElementVNode("stop", {
|
|
125
|
+
offset: "0%",
|
|
126
|
+
"stop-color": stroke.value,
|
|
127
|
+
"stop-opacity": "0.3"
|
|
128
|
+
}, null, 8, _hoisted_3),
|
|
129
|
+
createElementVNode("stop", {
|
|
130
|
+
offset: "100%",
|
|
131
|
+
"stop-color": stroke.value,
|
|
132
|
+
"stop-opacity": "0.02"
|
|
133
|
+
}, null, 8, _hoisted_4)
|
|
134
|
+
])
|
|
135
|
+
])) : createCommentVNode("", true),
|
|
136
|
+
__props.area ? (openBlock(), createElementBlock("path", {
|
|
137
|
+
key: 1,
|
|
138
|
+
d: fill.value,
|
|
139
|
+
fill: `url(#${gradientId})`,
|
|
140
|
+
class: normalizeClass(["lp-spark__area", revealClass.value]),
|
|
141
|
+
style: normalizeStyle(revealStyle.value)
|
|
142
|
+
}, null, 14, _hoisted_5)) : createCommentVNode("", true),
|
|
143
|
+
createElementVNode("path", {
|
|
144
|
+
d: d.value,
|
|
145
|
+
fill: "none",
|
|
146
|
+
stroke: stroke.value,
|
|
147
|
+
"stroke-width": "1.5",
|
|
148
|
+
"stroke-linecap": "round",
|
|
149
|
+
"stroke-linejoin": "round",
|
|
150
|
+
"vector-effect": "non-scaling-stroke",
|
|
151
|
+
class: normalizeClass(["lp-spark__line", revealClass.value]),
|
|
152
|
+
style: normalizeStyle(revealStyle.value)
|
|
153
|
+
}, null, 14, _hoisted_6),
|
|
154
|
+
__props.showLast && lastPoint.value ? (openBlock(), createElementBlock("circle", {
|
|
155
|
+
key: 2,
|
|
156
|
+
cx: lastPoint.value.x,
|
|
157
|
+
cy: lastPoint.value.y,
|
|
158
|
+
r: "2",
|
|
159
|
+
fill: stroke.value,
|
|
160
|
+
class: normalizeClass(["lp-spark__dot", revealClass.value]),
|
|
161
|
+
style: normalizeStyle(revealStyle.value)
|
|
162
|
+
}, null, 14, _hoisted_7)) : createCommentVNode("", true)
|
|
163
|
+
], 8, _hoisted_1)) : (openBlock(), createElementBlock("span", {
|
|
164
|
+
key: 1,
|
|
165
|
+
class: "inline-block shrink-0 rounded-pill bg-surface-soft",
|
|
166
|
+
style: normalizeStyle({ width: `${__props.width}px`, height: "2px" }),
|
|
167
|
+
role: "img",
|
|
168
|
+
"aria-label": label.value
|
|
169
|
+
}, null, 12, _hoisted_8));
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
export {
|
|
174
|
+
_sfc_main as _
|
|
175
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { defineComponent, computed, openBlock, createElementBlock, createElementVNode, createCommentVNode, createBlock, toDisplayString, normalizeClass, createVNode,
|
|
1
|
+
import { defineComponent, computed, openBlock, createElementBlock, createElementVNode, createCommentVNode, createBlock, toDisplayString, Fragment, createTextVNode, normalizeClass, createVNode, renderSlot } from "vue";
|
|
2
2
|
import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
|
|
3
|
+
import { _ as _sfc_main$2 } from "./LpNumberFlow-amWYaeuA.js";
|
|
3
4
|
const _hoisted_1 = { class: "flex flex-col gap-2" };
|
|
4
5
|
const _hoisted_2 = { class: "flex items-center gap-2" };
|
|
5
6
|
const _hoisted_3 = {
|
|
@@ -10,6 +11,10 @@ const _hoisted_4 = { class: "text-[11px] font-semibold uppercase tracking-[0.08e
|
|
|
10
11
|
const _hoisted_5 = { class: "flex items-baseline gap-2" };
|
|
11
12
|
const _hoisted_6 = { class: "font-mono text-3xl font-medium tracking-tight text-ink" };
|
|
12
13
|
const _hoisted_7 = {
|
|
14
|
+
key: 1,
|
|
15
|
+
class: "ml-3 self-center"
|
|
16
|
+
};
|
|
17
|
+
const _hoisted_8 = {
|
|
13
18
|
key: 0,
|
|
14
19
|
class: "text-sm text-muted"
|
|
15
20
|
};
|
|
@@ -18,6 +23,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
18
23
|
props: {
|
|
19
24
|
label: {},
|
|
20
25
|
value: {},
|
|
26
|
+
compact: { type: Boolean },
|
|
27
|
+
grouped: { type: Boolean },
|
|
28
|
+
decimals: { default: 0 },
|
|
29
|
+
prefix: {},
|
|
30
|
+
valueSuffix: {},
|
|
21
31
|
hint: {},
|
|
22
32
|
icon: {},
|
|
23
33
|
online: { type: Boolean },
|
|
@@ -53,6 +63,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
53
63
|
return props.delta;
|
|
54
64
|
});
|
|
55
65
|
const showDelta = computed(() => props.delta != null);
|
|
66
|
+
const deltaDecimals = computed(() => {
|
|
67
|
+
if (typeof props.delta !== "number") return 0;
|
|
68
|
+
const [, fraction] = String(props.delta).split(".");
|
|
69
|
+
return Math.min(fraction?.length ?? 0, 4);
|
|
70
|
+
});
|
|
56
71
|
return (_ctx, _cache) => {
|
|
57
72
|
return openBlock(), createElementBlock("div", _hoisted_1, [
|
|
58
73
|
createElementVNode("div", _hoisted_2, [
|
|
@@ -66,7 +81,19 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
66
81
|
createElementVNode("span", _hoisted_4, toDisplayString(__props.label), 1)
|
|
67
82
|
]),
|
|
68
83
|
createElementVNode("div", _hoisted_5, [
|
|
69
|
-
createElementVNode("span", _hoisted_6,
|
|
84
|
+
createElementVNode("span", _hoisted_6, [
|
|
85
|
+
typeof __props.value === "number" ? (openBlock(), createBlock(_sfc_main$2, {
|
|
86
|
+
key: 0,
|
|
87
|
+
value: __props.value,
|
|
88
|
+
compact: __props.compact,
|
|
89
|
+
grouped: __props.grouped,
|
|
90
|
+
decimals: __props.decimals,
|
|
91
|
+
prefix: __props.prefix,
|
|
92
|
+
suffix: __props.valueSuffix
|
|
93
|
+
}, null, 8, ["value", "compact", "grouped", "decimals", "prefix", "suffix"])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
94
|
+
createTextVNode(toDisplayString(__props.value), 1)
|
|
95
|
+
], 64))
|
|
96
|
+
]),
|
|
70
97
|
showDelta.value ? (openBlock(), createElementBlock("span", {
|
|
71
98
|
key: 0,
|
|
72
99
|
class: normalizeClass(["inline-flex items-center gap-0.5 text-sm font-medium tabular-nums", trendColor.value])
|
|
@@ -75,10 +102,21 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
75
102
|
name: trendIcon.value,
|
|
76
103
|
size: 14
|
|
77
104
|
}, null, 8, ["name"]),
|
|
78
|
-
|
|
79
|
-
|
|
105
|
+
typeof __props.delta === "number" ? (openBlock(), createBlock(_sfc_main$2, {
|
|
106
|
+
key: 0,
|
|
107
|
+
value: __props.delta,
|
|
108
|
+
decimals: deltaDecimals.value,
|
|
109
|
+
prefix: __props.delta > 0 ? "+" : "",
|
|
110
|
+
suffix: __props.deltaSuffix
|
|
111
|
+
}, null, 8, ["value", "decimals", "prefix", "suffix"])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
112
|
+
createTextVNode(toDisplayString(deltaText.value), 1)
|
|
113
|
+
], 64))
|
|
114
|
+
], 2)) : createCommentVNode("", true),
|
|
115
|
+
_ctx.$slots.trend ? (openBlock(), createElementBlock("span", _hoisted_7, [
|
|
116
|
+
renderSlot(_ctx.$slots, "trend")
|
|
117
|
+
])) : createCommentVNode("", true)
|
|
80
118
|
]),
|
|
81
|
-
__props.hint ? (openBlock(), createElementBlock("span",
|
|
119
|
+
__props.hint ? (openBlock(), createElementBlock("span", _hoisted_8, toDisplayString(__props.hint), 1)) : createCommentVNode("", true)
|
|
82
120
|
]);
|
|
83
121
|
};
|
|
84
122
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { computed, readonly, shallowRef, defineComponent, onMounted, openBlock, createBlock, withCtx, createElementVNode, normalizeClass, createElementBlock, normalizeStyle, createVNode, toDisplayString, createCommentVNode } from "vue";
|
|
2
|
+
import { prefersReducedMotion, easingToken } from "../composables/easing.js";
|
|
2
3
|
import { _ as _sfc_main$1 } from "./LpContextMenu-D0mzXqA7.js";
|
|
3
4
|
import { _ as _sfc_main$2 } from "./LpIcon-CCnX5_2j.js";
|
|
4
5
|
const sharedShape = { radiusControl: 8, radiusCard: 14, radiusPill: 9999 };
|
|
@@ -383,9 +384,6 @@ function applyTheme(theme, root = document.documentElement) {
|
|
|
383
384
|
root.style.colorScheme = theme.mode;
|
|
384
385
|
detectBackdropFilter(root);
|
|
385
386
|
}
|
|
386
|
-
function prefersReducedMotion() {
|
|
387
|
-
return typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
|
|
388
|
-
}
|
|
389
387
|
function applyThemeWithTransition(theme, origin, root = document.documentElement, durationMs = 480) {
|
|
390
388
|
const doc = document;
|
|
391
389
|
if (typeof doc.startViewTransition !== "function" || prefersReducedMotion()) {
|
|
@@ -406,7 +404,7 @@ function applyThemeWithTransition(theme, origin, root = document.documentElement
|
|
|
406
404
|
},
|
|
407
405
|
{
|
|
408
406
|
duration: durationMs,
|
|
409
|
-
easing: "
|
|
407
|
+
easing: easingToken("ease-emphasized"),
|
|
410
408
|
// Hold the fully-shrunk circle after the keyframes end. Without it the
|
|
411
409
|
// old snapshot snaps back to its default (un-clipped) state for the one
|
|
412
410
|
// frame between animation-end and the browser tearing down the
|