@leavepulse/ui 0.24.1 → 0.26.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/LpSkeleton-D7xU8IPV.css +29 -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/LpChart-tNXs1ttr.js +671 -0
- package/dist/chunks/{LpConfirmDialog-CObRah9c.js → LpConfirmDialog-CjOGcj1e.js} +32 -4
- package/dist/chunks/{LpFileTree-H7Ho8uoq.js → LpFileTree-BaV7f0O7.js} +1 -1
- package/dist/chunks/{LpFileTreeNode-BEZIvxHi.js → LpFileTreeNode-BzpBa7R7.js} +5 -7
- 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/LpSkeleton-t5mMYl3a.js +29 -0
- 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/LpChart.vue.d.ts +88 -0
- package/dist/components/LpChart.vue.js +4 -0
- package/dist/components/LpConfirmDialog.vue.d.ts +18 -2
- package/dist/components/LpConfirmDialog.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/LpSkeleton.vue.d.ts +11 -1
- package/dist/components/LpSkeleton.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 +125 -102
- package/package.json +2 -1
- package/src/component-names.ts +5 -0
- package/src/components/LpChart.vue +846 -0
- package/src/components/LpConfirmDialog.vue +49 -0
- package/src/components/LpFileTreeNode.vue +5 -9
- 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/LpSkeleton.vue +81 -8
- 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/LpSkeleton-eSFIo4kD.js +0 -22
- package/dist/chunks/LpTooltip-D1hbjQWG.js +0 -63
- package/dist/chunks/LpUptimeBar-CB366k0O.js +0 -127
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
TooltipRoot,
|
|
7
|
-
TooltipTrigger,
|
|
8
|
-
} from "reka-ui"
|
|
9
|
-
import { computed } from "vue"
|
|
10
|
-
import { PANEL_SURFACE, TOOLTIP_ANIM } from "./dropdown"
|
|
2
|
+
import { computed, ref } from "vue"
|
|
3
|
+
import LpNumberFlow from "./LpNumberFlow.vue"
|
|
4
|
+
import LpRollingText from "./LpRollingText.vue"
|
|
5
|
+
import LpTooltip from "./LpTooltip.vue"
|
|
11
6
|
|
|
12
7
|
export type UptimeStatus = "operational" | "degraded" | "down" | "maintenance" | "empty"
|
|
13
8
|
|
|
@@ -40,14 +35,24 @@ const props = withDefaults(
|
|
|
40
35
|
{ showUptime: true },
|
|
41
36
|
)
|
|
42
37
|
|
|
38
|
+
/*
|
|
39
|
+
* Green for healthy, and at full strength — the convention every status page
|
|
40
|
+
* shares, so a wall of green reads instantly as "fine" and the eye only has to
|
|
41
|
+
* find the slices that are not green. (Dimming the healthy days was tried and
|
|
42
|
+
* made the whole row look faded rather than calm.)
|
|
43
|
+
*/
|
|
43
44
|
const defaultColor: Record<UptimeStatus, string> = {
|
|
44
|
-
operational: "var(--color-
|
|
45
|
+
operational: "var(--color-action)",
|
|
45
46
|
degraded: "var(--color-accent)",
|
|
46
47
|
down: "var(--color-danger)",
|
|
47
|
-
maintenance: "var(--color-
|
|
48
|
+
maintenance: "var(--color-brand)",
|
|
48
49
|
empty: "var(--color-surface-soft)",
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
/* Only "no data yet" is held back; a real reading is always full strength. */
|
|
53
|
+
const opacityFor = (status: UptimeStatus): number =>
|
|
54
|
+
status === "empty" ? 0.4 : 1
|
|
55
|
+
|
|
51
56
|
const colorFor = (status: UptimeStatus): string =>
|
|
52
57
|
props.colors?.[status] ?? defaultColor[status]
|
|
53
58
|
|
|
@@ -67,60 +72,152 @@ const uptimePct = computed(() => {
|
|
|
67
72
|
return (up / tracked.length) * 100
|
|
68
73
|
})
|
|
69
74
|
|
|
70
|
-
|
|
75
|
+
/*
|
|
76
|
+
* Places actually needed, so trailing zeros stay trimmed: 100 rather than
|
|
77
|
+
* 100.00, 99.9 rather than 99.90. The counter needs a fixed count per render —
|
|
78
|
+
* it cannot "trim" mid-roll without changing the row's shape — so it is derived
|
|
79
|
+
* from the value instead of hard-coded at two.
|
|
80
|
+
*/
|
|
81
|
+
/*
|
|
82
|
+
* The gap is drawn INSIDE each slice, not between slices.
|
|
83
|
+
*
|
|
84
|
+
* A flex `gap` looked right and measured wrong: the slices divide a fractional
|
|
85
|
+
* remainder, so each one's edges land on a different side of a device pixel and
|
|
86
|
+
* a single 2.59px gap was painted as a mix of 2px and 3px down the row — the
|
|
87
|
+
* bar kept losing its rhythm no matter what the gap was set to, including a
|
|
88
|
+
* whole number of pixels.
|
|
89
|
+
*
|
|
90
|
+
* Giving every slice the same full share and inset-ing its paint by a
|
|
91
|
+
* percentage of that share moves the rounding INTO a mark that is uniform by
|
|
92
|
+
* construction: whatever each slot rounds to, the visible bar and the space
|
|
93
|
+
* beside it round with it, together.
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
/** Slice width as a fraction of its slot; the remainder is the gap. Mirrored by
|
|
97
|
+
* `.lp-uptime__mark` in the stylesheet, which owns the resting state. */
|
|
98
|
+
const MARK_WIDTH = 0.64
|
|
99
|
+
|
|
100
|
+
const hovered = ref<number | null>(null)
|
|
101
|
+
|
|
102
|
+
/* Sticky-ish read of the hovered slice, so the panel has content to show while
|
|
103
|
+
it is closing rather than emptying first. */
|
|
104
|
+
const tip = computed(() => {
|
|
105
|
+
const i = hovered.value
|
|
106
|
+
if (i == null) return null
|
|
107
|
+
const seg = props.segments[i]
|
|
108
|
+
if (!seg) return null
|
|
109
|
+
return { status: seg.status, text: seg.label ?? statusLabel[seg.status] }
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
const uptimeDecimals = computed(() => {
|
|
71
113
|
const v = uptimePct.value
|
|
72
|
-
if (v == null) return
|
|
73
|
-
|
|
74
|
-
return
|
|
114
|
+
if (v == null) return 0
|
|
115
|
+
const trimmed = Number(v.toFixed(2))
|
|
116
|
+
return Number.isInteger(trimmed) ? 0 : String(trimmed).split(".")[1].length
|
|
75
117
|
})
|
|
76
118
|
</script>
|
|
77
119
|
|
|
78
120
|
<template>
|
|
79
121
|
<div class="flex flex-col gap-1.5">
|
|
80
|
-
<div v-if="
|
|
81
|
-
<span
|
|
82
|
-
<span v-else />
|
|
83
|
-
<span v-if="showUptime" class="tabular-nums text-muted-strong">
|
|
84
|
-
{{ uptimeText }}<span class="text-muted"> uptime</span>
|
|
85
|
-
</span>
|
|
122
|
+
<div v-if="title" class="text-xs">
|
|
123
|
+
<span class="font-medium text-ink">{{ title }}</span>
|
|
86
124
|
</div>
|
|
87
125
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
126
|
+
<!--
|
|
127
|
+
One tooltip for the whole bar, following the pointer, rather than one per
|
|
128
|
+
slice. At sixty slices a few pixels wide, a tip anchored to each would
|
|
129
|
+
jump from box to box as the pointer travelled; following it reads as a
|
|
130
|
+
single readout being scrubbed along the timeline. It also means the kit's
|
|
131
|
+
tooltip is used here rather than a second, hand-wired copy of reka.
|
|
132
|
+
-->
|
|
133
|
+
<LpTooltip :delay="80" follow-cursor side="top" :disabled="hovered === null">
|
|
134
|
+
<template #content>
|
|
135
|
+
<span v-if="tip" class="flex items-center gap-1.5">
|
|
136
|
+
<span
|
|
137
|
+
class="size-1.5 shrink-0 rounded-full"
|
|
138
|
+
:style="{ backgroundColor: colorFor(tip.status) }"
|
|
139
|
+
/>
|
|
140
|
+
<LpRollingText :value="tip.text" :duration="180" align="start" />
|
|
141
|
+
</span>
|
|
142
|
+
</template>
|
|
143
|
+
|
|
144
|
+
<!--
|
|
145
|
+
Mark-to-gap is a RATIO, not a pixel count. The reference bar is ~800px
|
|
146
|
+
wide and can spare 3.6px between 5.4px marks; the same 3px inside a
|
|
147
|
+
384px card left the marks thinner than the spaces between them. The gap
|
|
148
|
+
is therefore derived from how many slices are being drawn, and clamped
|
|
149
|
+
so a year's worth doesn't lose its gaps and a week's doesn't grow
|
|
150
|
+
canyons.
|
|
151
|
+
-->
|
|
152
|
+
<div class="lp-uptime flex h-[34px] items-stretch" @pointerleave="hovered = null">
|
|
153
|
+
<button
|
|
154
|
+
v-for="(seg, i) in segments"
|
|
155
|
+
:key="i"
|
|
156
|
+
type="button"
|
|
157
|
+
class="lp-uptime__slot min-w-0 flex-1 outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
158
|
+
:aria-label="seg.label ?? statusLabel[seg.status]"
|
|
159
|
+
@pointerenter="hovered = i"
|
|
160
|
+
>
|
|
161
|
+
<span
|
|
162
|
+
class="lp-uptime__mark block h-full rounded-[1px] transition-[opacity,scale] duration-[var(--duration-fast)] motion-reduce:transition-none"
|
|
163
|
+
:style="{
|
|
164
|
+
backgroundColor: colorFor(seg.status),
|
|
165
|
+
opacity: hovered === i ? 1 : opacityFor(seg.status),
|
|
166
|
+
// Both axes every time: the x factor is what holds the gap open
|
|
167
|
+
// (see the stylesheet), so a hover that set only y would widen
|
|
168
|
+
// the slice into its neighbours' spacing.
|
|
169
|
+
scale: hovered === i ? `${MARK_WIDTH} 1.12` : undefined,
|
|
170
|
+
}"
|
|
171
|
+
/>
|
|
172
|
+
</button>
|
|
115
173
|
</div>
|
|
116
|
-
</
|
|
174
|
+
</LpTooltip>
|
|
117
175
|
|
|
176
|
+
<!--
|
|
177
|
+
The figure sits BETWEEN the two dates rather than above the bar: it is a
|
|
178
|
+
summary of exactly the span they bracket, and the rules on either side say
|
|
179
|
+
so. This is the arrangement status pages settled on, and it also frees the
|
|
180
|
+
top line to be nothing but the component's name.
|
|
181
|
+
-->
|
|
118
182
|
<div
|
|
119
|
-
v-if="startLabel || endLabel"
|
|
120
|
-
class="flex items-center
|
|
183
|
+
v-if="startLabel || endLabel || showUptime"
|
|
184
|
+
class="flex items-center gap-2 text-xs text-muted"
|
|
121
185
|
>
|
|
122
|
-
<span>{{ startLabel }}</span>
|
|
123
|
-
<span
|
|
186
|
+
<span class="shrink-0">{{ startLabel }}</span>
|
|
187
|
+
<span class="h-px flex-1 bg-line" />
|
|
188
|
+
<span v-if="showUptime" class="shrink-0 text-muted-strong">
|
|
189
|
+
<LpNumberFlow
|
|
190
|
+
v-if="uptimePct != null"
|
|
191
|
+
:value="uptimePct"
|
|
192
|
+
:decimals="uptimeDecimals"
|
|
193
|
+
suffix="%"
|
|
194
|
+
/>
|
|
195
|
+
<template v-else>—</template>
|
|
196
|
+
<span class="text-muted"> uptime</span>
|
|
197
|
+
</span>
|
|
198
|
+
<span class="h-px flex-1 bg-line" />
|
|
199
|
+
<span class="shrink-0">{{ endLabel }}</span>
|
|
124
200
|
</div>
|
|
125
201
|
</div>
|
|
126
202
|
</template>
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
<style>
|
|
206
|
+
/*
|
|
207
|
+
* The slot is the full share of the row; the mark inside it is narrowed to a
|
|
208
|
+
* FRACTION of that share, which is what produces the space between days.
|
|
209
|
+
* Because the inset scales with the slot, the mark-to-gap ratio holds at any
|
|
210
|
+
* width and any number of slices, and both round to device pixels together
|
|
211
|
+
* instead of drifting apart.
|
|
212
|
+
*
|
|
213
|
+
* A horizontal scale, not padding. Percentage padding resolves against the
|
|
214
|
+
* PARENT's width, never the element's own — so `padding-inline: 18%` on a slot
|
|
215
|
+
* of a 384px bar asked for 69px of padding on each side of a 3.2px slice. The
|
|
216
|
+
* marks collapsed to zero width and the bar vanished, while flex stretched the
|
|
217
|
+
* slots to fit the padding. `scale` is one of the few things that is genuinely
|
|
218
|
+
* relative to the box itself.
|
|
219
|
+
*/
|
|
220
|
+
.lp-uptime__mark {
|
|
221
|
+
scale: 0.64 1;
|
|
222
|
+
}
|
|
223
|
+
</style>
|