@leavepulse/ui 0.18.2 → 0.19.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/{LpCommandPalette-D315B47-.css → LpCommandPalette-Cp6Y-j7g.css} +4 -4
- package/dist/chunks/{LpAddressInput-Dfyn88Dd.js → LpAddressInput-CWlfDyKz.js} +1 -1
- package/dist/chunks/{LpAppShell-CiHAD6PN.js → LpAppShell-D42la4ZD.js} +9 -3
- package/dist/chunks/{LpConfirmDialog-BxBU4Pj9.js → LpConfirmDialog-JNW4rpx3.js} +1 -1
- package/dist/chunks/{LpDrawer-DaCsNuW3.js → LpDrawer-CYm-nUIZ.js} +1 -1
- package/dist/chunks/{LpLightbox-CWdIsthW.js → LpLightbox-CzhFe9wZ.js} +2 -2
- package/dist/chunks/{LpModal-CYKAaSHO.js → LpModal-BJTiTSDJ.js} +1 -1
- package/dist/chunks/{LpSidebar-BBG9NhAn.js → LpSidebar-DlLZJk1u.js} +1 -1
- package/dist/chunks/{LpThemeSwitcher-Ct-lnXMa.js → LpThemeSwitcher-B_kj0T2b.js} +21 -4
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.js +3 -3
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +16 -13
- package/dist/theme/backdrop.d.ts +9 -0
- package/package.json +1 -1
- package/src/components/LpAppShell.vue +15 -2
- package/src/components/LpCommandPalette.vue +1 -1
- package/src/components/LpDrawer.vue +1 -1
- package/src/components/LpLightbox.vue +4 -2
- package/src/components/LpModal.vue +1 -1
- package/src/index.ts +1 -0
- package/src/theme/backdrop.ts +51 -0
- package/src/theme/useTheme.ts +4 -0
- package/src/tokens/tokens.css +48 -0
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
|
|
2
2
|
/* Each result eases in; honours reduced motion. */
|
|
3
|
-
.lp-cmd-item[data-v-
|
|
4
|
-
animation: lp-cmd-in-
|
|
3
|
+
.lp-cmd-item[data-v-7fba2dd8] {
|
|
4
|
+
animation: lp-cmd-in-7fba2dd8 140ms ease-out both;
|
|
5
5
|
}
|
|
6
|
-
@keyframes lp-cmd-in-
|
|
6
|
+
@keyframes lp-cmd-in-7fba2dd8 {
|
|
7
7
|
from {
|
|
8
8
|
opacity: 0;
|
|
9
9
|
transform: translateY(3px);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
@media (prefers-reduced-motion: reduce) {
|
|
13
|
-
.lp-cmd-item[data-v-
|
|
13
|
+
.lp-cmd-item[data-v-7fba2dd8] {
|
|
14
14
|
animation: none;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -6,7 +6,7 @@ import { _ as _sfc_main$3 } from "./LpButton-5VXXCjwF.js";
|
|
|
6
6
|
import { _ as _sfc_main$1 } from "./LpFormField-Bn-ZwC6z.js";
|
|
7
7
|
import { _ as _sfc_main$4 } from "./LpIcon-CCnX5_2j.js";
|
|
8
8
|
import { _ as _sfc_main$5 } from "./LpInput-BauZRyzm.js";
|
|
9
|
-
import { _ as _sfc_main$7 } from "./LpModal-
|
|
9
|
+
import { _ as _sfc_main$7 } from "./LpModal-BJTiTSDJ.js";
|
|
10
10
|
import { _ as _sfc_main$6 } from "./LpSelect-D5mU4cZO.js";
|
|
11
11
|
const _hoisted_1 = { class: "flex flex-col gap-3" };
|
|
12
12
|
const _hoisted_2 = { class: "flex items-stretch gap-2" };
|
|
@@ -2,7 +2,7 @@ import { defineComponent, computed, watch, openBlock, createElementBlock, create
|
|
|
2
2
|
import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
|
|
3
3
|
import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
|
|
4
4
|
import { _ as _sfc_main$4 } from "./LpScrollArea-DQ0Yudfu.js";
|
|
5
|
-
import { _ as _sfc_main$1 } from "./LpSidebar-
|
|
5
|
+
import { _ as _sfc_main$1 } from "./LpSidebar-DlLZJk1u.js";
|
|
6
6
|
const _hoisted_1 = { class: "flex h-dvh overflow-hidden bg-surface text-ink" };
|
|
7
7
|
const _hoisted_2 = { class: "flex min-w-0 flex-1 flex-col" };
|
|
8
8
|
const _hoisted_3 = { class: "flex h-16 shrink-0 items-center gap-3 border-b border-line bg-surface px-4 md:px-6" };
|
|
@@ -38,11 +38,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
38
38
|
const flatItems = computed(
|
|
39
39
|
() => props.sections ? props.sections.flatMap((s) => s.items) : props.items ?? []
|
|
40
40
|
);
|
|
41
|
-
function
|
|
42
|
-
if (props.path == null) return item.id === props.modelValue;
|
|
41
|
+
function matchesPath(item) {
|
|
43
42
|
if (item.id === "/") return props.path === "/";
|
|
44
43
|
return props.path === item.id || props.path.startsWith(`${item.id}/`);
|
|
45
44
|
}
|
|
45
|
+
function defaultActive(item) {
|
|
46
|
+
if (props.path == null) return item.id === props.modelValue;
|
|
47
|
+
if (!matchesPath(item)) return false;
|
|
48
|
+
return !flatItems.value.some(
|
|
49
|
+
(other) => other.id !== item.id && other.id.length > item.id.length && matchesPath(other)
|
|
50
|
+
);
|
|
51
|
+
}
|
|
46
52
|
const activeTest = computed(() => props.isActive ?? defaultActive);
|
|
47
53
|
const pageTitle = computed(() => {
|
|
48
54
|
if (props.title != null) return props.title;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineComponent, openBlock, createBlock, withCtx, renderSlot, createVNode, createTextVNode, toDisplayString } from "vue";
|
|
2
2
|
import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
|
|
3
|
-
import { _ as _sfc_main$1 } from "./LpModal-
|
|
3
|
+
import { _ as _sfc_main$1 } from "./LpModal-BJTiTSDJ.js";
|
|
4
4
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
5
5
|
__name: "LpConfirmDialog",
|
|
6
6
|
props: {
|
|
@@ -215,7 +215,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
215
215
|
default: withCtx(() => [
|
|
216
216
|
createVNode(unref(DrawerPortal), null, {
|
|
217
217
|
default: withCtx(() => [
|
|
218
|
-
createVNode(unref(DrawerOverlay), { class: "fixed inset-0 z-(--z-overlay)
|
|
218
|
+
createVNode(unref(DrawerOverlay), { class: "lp-scrim fixed inset-0 z-(--z-overlay) backdrop-blur-sm data-[state=open]:animate-[fade-in_150ms_ease] data-[state=closed]:animate-[fade-out_130ms_ease]" }),
|
|
219
219
|
createVNode(unref(DrawerContent), {
|
|
220
220
|
class: normalizeClass(contentClass.value),
|
|
221
221
|
style: normalizeStyle(sizeStyle.value),
|
|
@@ -210,9 +210,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
210
210
|
default: withCtx(() => [
|
|
211
211
|
createVNode(unref(DialogPortal), null, {
|
|
212
212
|
default: withCtx(() => [
|
|
213
|
-
createVNode(unref(DialogOverlay), { class: "fixed inset-0 z-(--z-overlay)
|
|
213
|
+
createVNode(unref(DialogOverlay), { class: "lp-scrim fixed inset-0 z-(--z-lightbox-overlay) backdrop-blur-sm data-[state=open]:animate-[fade-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[fade-out_120ms_ease]" }),
|
|
214
214
|
createVNode(unref(DialogContent), {
|
|
215
|
-
class: "fixed inset-0 z-(--z-
|
|
215
|
+
class: "fixed inset-0 z-(--z-lightbox) flex flex-col outline-none data-[state=open]:animate-[lightbox-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[lightbox-out_140ms_cubic-bezier(0.4,0,1,1)] motion-reduce:animate-none",
|
|
216
216
|
onKeydown
|
|
217
217
|
}, {
|
|
218
218
|
default: withCtx(() => [
|
|
@@ -57,7 +57,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
57
57
|
default: withCtx(() => [
|
|
58
58
|
createVNode(unref(DialogPortal), null, {
|
|
59
59
|
default: withCtx(() => [
|
|
60
|
-
createVNode(unref(DialogOverlay), { class: "fixed inset-0 z-(--z-overlay)
|
|
60
|
+
createVNode(unref(DialogOverlay), { class: "lp-scrim fixed inset-0 z-(--z-overlay) backdrop-blur-sm data-[state=open]:animate-[fade-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[fade-out_120ms_ease]" }),
|
|
61
61
|
createElementVNode("div", _hoisted_1, [
|
|
62
62
|
createVNode(unref(DialogContent), mergeProps({
|
|
63
63
|
class: ["pointer-events-auto flex max-h-[min(90vh,calc(100dvh-2rem))] min-h-0 flex-col rounded-card border border-line bg-surface-raised shadow-panel outline-none data-[state=open]:animate-[rise-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[rise-out_120ms_cubic-bezier(0.4,0,1,1)]", widthClass.value],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineComponent, computed, useId, openBlock, createElementBlock, Fragment, createElementVNode, mergeProps, normalizeClass, renderSlot, createCommentVNode, createVNode, createSlots, withCtx, normalizeProps, guardReactiveProps, createBlock } from "vue";
|
|
2
|
-
import { _ as _sfc_main$2 } from "./LpDrawer-
|
|
2
|
+
import { _ as _sfc_main$2 } from "./LpDrawer-CYm-nUIZ.js";
|
|
3
3
|
import { _ as _sfc_main$1 } from "./LpSidebarNav-DSDa5mAY.js";
|
|
4
4
|
const _hoisted_1 = {
|
|
5
5
|
key: 1,
|
|
@@ -249,6 +249,19 @@ const nord = {
|
|
|
249
249
|
font: { ...interFont }
|
|
250
250
|
};
|
|
251
251
|
const presets = { dark, light, lime, leavepulse, rose, amber, violet, nord };
|
|
252
|
+
const NO_BACKDROP_ATTR = "data-lp-no-backdrop";
|
|
253
|
+
function isBrokenBackdropEngine(nav = navigator) {
|
|
254
|
+
const ua = nav.userAgent;
|
|
255
|
+
if (!/AppleWebKit/.test(ua)) return false;
|
|
256
|
+
if (/Chrome|Chromium|Edg\//.test(ua)) return false;
|
|
257
|
+
return !/Mac|iPhone|iPad|iPod/.test(nav.platform || "");
|
|
258
|
+
}
|
|
259
|
+
function detectBackdropFilter(root = document.documentElement) {
|
|
260
|
+
if (typeof navigator === "undefined") return true;
|
|
261
|
+
const usable = !isBrokenBackdropEngine();
|
|
262
|
+
root.toggleAttribute(NO_BACKDROP_ATTR, !usable);
|
|
263
|
+
return usable;
|
|
264
|
+
}
|
|
252
265
|
const COLOR_VARS = {
|
|
253
266
|
surface: "--color-surface",
|
|
254
267
|
surfaceRaised: "--color-surface-raised",
|
|
@@ -341,6 +354,7 @@ function applyTheme(theme, root = document.documentElement) {
|
|
|
341
354
|
}
|
|
342
355
|
root.dataset.themeMode = theme.mode;
|
|
343
356
|
root.style.colorScheme = theme.mode;
|
|
357
|
+
detectBackdropFilter(root);
|
|
344
358
|
}
|
|
345
359
|
function prefersReducedMotion() {
|
|
346
360
|
return typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
|
|
@@ -600,6 +614,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
600
614
|
});
|
|
601
615
|
export {
|
|
602
616
|
DEFAULT_SURFACE as D,
|
|
617
|
+
NO_BACKDROP_ATTR as N,
|
|
603
618
|
THEME_COOKIE as T,
|
|
604
619
|
_sfc_main as _,
|
|
605
620
|
parseTheme as a,
|
|
@@ -608,11 +623,13 @@ export {
|
|
|
608
623
|
applyThemeWithTransition as d,
|
|
609
624
|
bootstrapTheme as e,
|
|
610
625
|
dark as f,
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
626
|
+
detectBackdropFilter as g,
|
|
627
|
+
light as h,
|
|
628
|
+
isBrokenBackdropEngine as i,
|
|
629
|
+
lime as j,
|
|
630
|
+
themeToCssRule as k,
|
|
615
631
|
leavepulse as l,
|
|
632
|
+
themeToCssVars as m,
|
|
616
633
|
nord as n,
|
|
617
634
|
presets as p,
|
|
618
635
|
rose as r,
|
|
@@ -2,7 +2,7 @@ import { defineComponent, ref, computed, watch, nextTick, onMounted, onBeforeUnm
|
|
|
2
2
|
import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription } from "reka-ui";
|
|
3
3
|
import { _ as _sfc_main$1 } from "../chunks/LpIcon-CCnX5_2j.js";
|
|
4
4
|
import { _ as _sfc_main$2 } from "../chunks/LpScrollArea-DQ0Yudfu.js";
|
|
5
|
-
import '../assets/LpCommandPalette-
|
|
5
|
+
import '../assets/LpCommandPalette-Cp6Y-j7g.css';const _hoisted_1 = { class: "flex shrink-0 items-center gap-2.5 border-b border-line px-4" };
|
|
6
6
|
const _hoisted_2 = ["placeholder", "onKeydown"];
|
|
7
7
|
const _hoisted_3 = {
|
|
8
8
|
key: 0,
|
|
@@ -167,7 +167,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
167
167
|
default: withCtx(() => [
|
|
168
168
|
createVNode(unref(DialogPortal), null, {
|
|
169
169
|
default: withCtx(() => [
|
|
170
|
-
createVNode(unref(DialogOverlay), { class: "fixed inset-0 z-(--z-overlay)
|
|
170
|
+
createVNode(unref(DialogOverlay), { class: "lp-scrim fixed inset-0 z-(--z-overlay) backdrop-blur-sm data-[state=open]:animate-[fade-in_150ms_ease] data-[state=closed]:animate-[fade-out_130ms_ease]" }),
|
|
171
171
|
createVNode(unref(DialogContent), {
|
|
172
172
|
class: "fixed left-1/2 top-[12vh] z-(--z-modal) flex max-h-[76vh] w-[min(92vw,40rem)] -translate-x-1/2 flex-col overflow-hidden rounded-card border border-line bg-surface-raised shadow-panel outline-none data-[state=open]:animate-[pop-in_160ms_var(--ease-emphasized)] data-[state=closed]:animate-[pop-out_130ms_ease]",
|
|
173
173
|
onOpenAutoFocus: _cache[1] || (_cache[1] = withModifiers(() => {
|
|
@@ -304,7 +304,7 @@ const _export_sfc = (sfc, props) => {
|
|
|
304
304
|
}
|
|
305
305
|
return target;
|
|
306
306
|
};
|
|
307
|
-
const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
307
|
+
const LpCommandPalette = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-7fba2dd8"]]);
|
|
308
308
|
export {
|
|
309
309
|
LpCommandPalette as default
|
|
310
310
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -98,6 +98,7 @@ export { useLayout } from './layout/useLayout';
|
|
|
98
98
|
export type { UseLayout, UseLayoutOptions } from './layout/useLayout';
|
|
99
99
|
export { applyTheme, applyThemeWithTransition, bootstrapTheme, parseTheme, serializeTheme, THEME_COOKIE, themeFromCookie, themeToCssRule, themeToCssVars, useTheme, } from './theme/useTheme';
|
|
100
100
|
export type { RevealOrigin } from './theme/useTheme';
|
|
101
|
+
export { detectBackdropFilter, isBrokenBackdropEngine, NO_BACKDROP_ATTR } from './theme/backdrop';
|
|
101
102
|
export { DEFAULT_SURFACE } from './theme/tokens';
|
|
102
103
|
export { defineTheme } from './theme/define';
|
|
103
104
|
export type { ThemeOverrides } from './theme/define';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { _ } from "./chunks/LayoutCanvas-Dd20boUG.js";
|
|
2
|
-
import { _ as _2 } from "./chunks/LpAddressInput-
|
|
2
|
+
import { _ as _2 } from "./chunks/LpAddressInput-CWlfDyKz.js";
|
|
3
3
|
import { _ as _3 } from "./chunks/LpMapPicker-DFKrd1Ne.js";
|
|
4
4
|
import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
|
|
5
5
|
import { _ as _4 } from "./chunks/LpAlert-BAYHDSS1.js";
|
|
6
|
-
import { _ as _5 } from "./chunks/LpAppShell-
|
|
6
|
+
import { _ as _5 } from "./chunks/LpAppShell-D42la4ZD.js";
|
|
7
7
|
import { _ as _6 } from "./chunks/LpAutocomplete-DIgXmtCA.js";
|
|
8
8
|
import { _ as _7 } from "./chunks/LpAvatar-B1Ez0ZAx.js";
|
|
9
9
|
import { _ as _8 } from "./chunks/LpBadge-CXzBPnwO.js";
|
|
@@ -14,12 +14,12 @@ import { _ as _12 } from "./chunks/LpCard-BmrTvWOe.js";
|
|
|
14
14
|
import { _ as _13 } from "./chunks/LpCheckbox-BO0zuuPh.js";
|
|
15
15
|
import { _ as _14 } from "./chunks/LpCodeBlock-CSiOSPs8.js";
|
|
16
16
|
import { default as default2 } from "./components/LpCommandPalette.vue.js";
|
|
17
|
-
import { _ as _15 } from "./chunks/LpConfirmDialog-
|
|
17
|
+
import { _ as _15 } from "./chunks/LpConfirmDialog-JNW4rpx3.js";
|
|
18
18
|
import { _ as _16 } from "./chunks/LpContextMenu-D0mzXqA7.js";
|
|
19
19
|
import { _ as _17 } from "./chunks/LpDatePicker-dY2q4dr4.js";
|
|
20
20
|
import { _ as _18 } from "./chunks/LpDisclosure-DP6yfgfM.js";
|
|
21
21
|
import { _ as _19 } from "./chunks/LpDivider-CUX46KR0.js";
|
|
22
|
-
import { _ as _20 } from "./chunks/LpDrawer-
|
|
22
|
+
import { _ as _20 } from "./chunks/LpDrawer-CYm-nUIZ.js";
|
|
23
23
|
import { _ as _21 } from "./chunks/LpDropdownMenu-DJpoqRCb.js";
|
|
24
24
|
import { _ as _22 } from "./chunks/LpEmptyState-CrfyRJUG.js";
|
|
25
25
|
import { _ as _23 } from "./chunks/LpFileTree-DXb3yKzH.js";
|
|
@@ -28,11 +28,11 @@ import { _ as _24 } from "./chunks/LpFormField-Bn-ZwC6z.js";
|
|
|
28
28
|
import { _ as _25 } from "./chunks/LpIcon-CCnX5_2j.js";
|
|
29
29
|
import { _ as _26 } from "./chunks/LpInfraNode-C_PHcthC.js";
|
|
30
30
|
import { _ as _27 } from "./chunks/LpInput-BauZRyzm.js";
|
|
31
|
-
import { _ as _28 } from "./chunks/LpLightbox-
|
|
31
|
+
import { _ as _28 } from "./chunks/LpLightbox-CzhFe9wZ.js";
|
|
32
32
|
import { fileNameOf } from "./components/lightbox.js";
|
|
33
33
|
import { _ as _29 } from "./chunks/LpLink-DHwOEp4e.js";
|
|
34
34
|
import { _ as _30 } from "./chunks/LpLogViewer-tmKPNTuU.js";
|
|
35
|
-
import { _ as _31 } from "./chunks/LpModal-
|
|
35
|
+
import { _ as _31 } from "./chunks/LpModal-BJTiTSDJ.js";
|
|
36
36
|
import { _ as _32 } from "./chunks/LpNotificationBell-DHze8wYN.js";
|
|
37
37
|
import { _ as _33 } from "./chunks/LpNumberField-C5ar-OwE.js";
|
|
38
38
|
import { _ as _34 } from "./chunks/LpOtpInput-DqaT0Z75.js";
|
|
@@ -48,7 +48,7 @@ import { _ as _42 } from "./chunks/LpScrollArea-DQ0Yudfu.js";
|
|
|
48
48
|
import { _ as _43 } from "./chunks/LpSegmented-BmpanyAo.js";
|
|
49
49
|
import { _ as _44 } from "./chunks/LpSelect-D5mU4cZO.js";
|
|
50
50
|
import { _ as _45 } from "./chunks/LpServiceNode-Dc-Ut5RS.js";
|
|
51
|
-
import { _ as _46 } from "./chunks/LpSidebar-
|
|
51
|
+
import { _ as _46 } from "./chunks/LpSidebar-DlLZJk1u.js";
|
|
52
52
|
import { _ as _47 } from "./chunks/LpSkeleton-eSFIo4kD.js";
|
|
53
53
|
import { _ as _48 } from "./chunks/LpSlider-B1SF3pRV.js";
|
|
54
54
|
import { _ as _49 } from "./chunks/LpStat-CdbEFOvn.js";
|
|
@@ -58,8 +58,8 @@ import { _ as _52 } from "./chunks/LpTable-DqZ-L4Au.js";
|
|
|
58
58
|
import { _ as _53 } from "./chunks/LpTableOfContents-CjvxPMpU.js";
|
|
59
59
|
import { _ as _54 } from "./chunks/LpTabs-D7aTVPOb.js";
|
|
60
60
|
import { _ as _55 } from "./chunks/LpTextarea-CRAkhemZ.js";
|
|
61
|
-
import { p as presets, a as parseTheme, s as serializeTheme } from "./chunks/LpThemeSwitcher-
|
|
62
|
-
import { D, _ as _56, T, b, c, d, e, f,
|
|
61
|
+
import { p as presets, a as parseTheme, s as serializeTheme } from "./chunks/LpThemeSwitcher-B_kj0T2b.js";
|
|
62
|
+
import { D, _ as _56, N, T, b, c, d, e, f, g, i, l, h, j, n, r, t, k, m, u, v } from "./chunks/LpThemeSwitcher-B_kj0T2b.js";
|
|
63
63
|
import { _ as _57 } from "./chunks/LpTilt-DuyKbpkA.js";
|
|
64
64
|
import { _ as _58 } from "./chunks/LpToaster-gDB1EaU3.js";
|
|
65
65
|
import { _ as _59 } from "./chunks/LpTooltip-CIwoRCag.js";
|
|
@@ -167,6 +167,7 @@ export {
|
|
|
167
167
|
_59 as LpTooltip,
|
|
168
168
|
_60 as LpTopologyCanvas,
|
|
169
169
|
_61 as LpUptimeBar,
|
|
170
|
+
N as NO_BACKDROP_ATTR,
|
|
170
171
|
OSM_TILES,
|
|
171
172
|
T as THEME_COOKIE,
|
|
172
173
|
UI_CONFIG_VERSION,
|
|
@@ -188,6 +189,7 @@ export {
|
|
|
188
189
|
defineBlocks,
|
|
189
190
|
defineTheme,
|
|
190
191
|
deserializeLayout,
|
|
192
|
+
g as detectBackdropFilter,
|
|
191
193
|
emptyAddress,
|
|
192
194
|
fileIcon,
|
|
193
195
|
fileNameOf,
|
|
@@ -195,9 +197,10 @@ export {
|
|
|
195
197
|
formatAddress,
|
|
196
198
|
formatModified,
|
|
197
199
|
formatSize,
|
|
200
|
+
i as isBrokenBackdropEngine,
|
|
198
201
|
l as leavepulse,
|
|
199
|
-
|
|
200
|
-
|
|
202
|
+
h as light,
|
|
203
|
+
j as lime,
|
|
201
204
|
loadCountries,
|
|
202
205
|
makeLayout,
|
|
203
206
|
matchCountryByValue,
|
|
@@ -220,8 +223,8 @@ export {
|
|
|
220
223
|
subtreeIds,
|
|
221
224
|
subtreeSize,
|
|
222
225
|
t as themeFromCookie,
|
|
223
|
-
|
|
224
|
-
|
|
226
|
+
k as themeToCssRule,
|
|
227
|
+
m as themeToCssVars,
|
|
225
228
|
tileUrl,
|
|
226
229
|
unproject,
|
|
227
230
|
useInputFilter,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Set on <html> when backdrop-filter resolves but does not composite. */
|
|
2
|
+
export declare const NO_BACKDROP_ATTR = "data-lp-no-backdrop";
|
|
3
|
+
export declare function isBrokenBackdropEngine(nav?: Navigator): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Flag the document when backdrop-filter cannot be trusted, so the fallback
|
|
6
|
+
* rules in tokens.css can trade blur for opacity. Safe to call more than once
|
|
7
|
+
* and a no-op outside the browser.
|
|
8
|
+
*/
|
|
9
|
+
export declare function detectBackdropFilter(root?: HTMLElement): boolean;
|
package/package.json
CHANGED
|
@@ -83,10 +83,23 @@ const flatItems = computed<SidebarItem[]>(() =>
|
|
|
83
83
|
|
|
84
84
|
// Default active test: exact match for "/", prefix match (on a segment
|
|
85
85
|
// boundary) otherwise — so "/billing" doesn't light up on "/billing-x".
|
|
86
|
+
function matchesPath(item: SidebarItem): boolean {
|
|
87
|
+
if (item.id === "/") return props.path === "/"
|
|
88
|
+
return props.path === item.id || props.path!.startsWith(`${item.id}/`)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Only the LONGEST match lights up. A parent whose id prefixes its children —
|
|
92
|
+
// "/dcim" over "/dcim/power" — otherwise stays lit alongside whichever child is
|
|
93
|
+
// open, and two highlighted items read as two current pages.
|
|
86
94
|
function defaultActive(item: SidebarItem): boolean {
|
|
87
95
|
if (props.path == null) return item.id === props.modelValue
|
|
88
|
-
if (item
|
|
89
|
-
return
|
|
96
|
+
if (!matchesPath(item)) return false
|
|
97
|
+
return !flatItems.value.some(
|
|
98
|
+
(other) =>
|
|
99
|
+
other.id !== item.id &&
|
|
100
|
+
other.id.length > item.id.length &&
|
|
101
|
+
matchesPath(other),
|
|
102
|
+
)
|
|
90
103
|
}
|
|
91
104
|
const activeTest = computed(() => props.isActive ?? defaultActive)
|
|
92
105
|
|
|
@@ -212,7 +212,7 @@ onBeforeUnmount(() => window.removeEventListener("keydown", onHotkey))
|
|
|
212
212
|
<DialogRoot :open="open" @update:open="(v) => emit('update:open', v)">
|
|
213
213
|
<DialogPortal>
|
|
214
214
|
<DialogOverlay
|
|
215
|
-
class="fixed inset-0 z-(--z-overlay)
|
|
215
|
+
class="lp-scrim fixed inset-0 z-(--z-overlay) backdrop-blur-sm data-[state=open]:animate-[fade-in_150ms_ease] data-[state=closed]:animate-[fade-out_130ms_ease]"
|
|
216
216
|
/>
|
|
217
217
|
<DialogContent
|
|
218
218
|
class="fixed left-1/2 top-[12vh] z-(--z-modal) flex max-h-[76vh] w-[min(92vw,40rem)] -translate-x-1/2 flex-col overflow-hidden rounded-card border border-line bg-surface-raised shadow-panel outline-none data-[state=open]:animate-[pop-in_160ms_var(--ease-emphasized)] data-[state=closed]:animate-[pop-out_130ms_ease]"
|
|
@@ -345,7 +345,7 @@ const edgeStripClass = computed(() =>
|
|
|
345
345
|
>
|
|
346
346
|
<DrawerPortal>
|
|
347
347
|
<DrawerOverlay
|
|
348
|
-
class="fixed inset-0 z-(--z-overlay)
|
|
348
|
+
class="lp-scrim fixed inset-0 z-(--z-overlay) backdrop-blur-sm data-[state=open]:animate-[fade-in_150ms_ease] data-[state=closed]:animate-[fade-out_130ms_ease]"
|
|
349
349
|
/>
|
|
350
350
|
<DrawerContent
|
|
351
351
|
:class="contentClass"
|
|
@@ -270,11 +270,13 @@ const ARROW =
|
|
|
270
270
|
<template>
|
|
271
271
|
<DialogRoot :open="open" @update:open="(v) => emit('update:open', v)">
|
|
272
272
|
<DialogPortal>
|
|
273
|
+
<!-- Sits on the lightbox rung, not the shared modal one: opening this
|
|
274
|
+
from inside a dialog must cover that dialog, not tie with it. -->
|
|
273
275
|
<DialogOverlay
|
|
274
|
-
class="fixed inset-0 z-(--z-overlay)
|
|
276
|
+
class="lp-scrim fixed inset-0 z-(--z-lightbox-overlay) backdrop-blur-sm data-[state=open]:animate-[fade-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[fade-out_120ms_ease]"
|
|
275
277
|
/>
|
|
276
278
|
<DialogContent
|
|
277
|
-
class="fixed inset-0 z-(--z-
|
|
279
|
+
class="fixed inset-0 z-(--z-lightbox) flex flex-col outline-none data-[state=open]:animate-[lightbox-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[lightbox-out_140ms_cubic-bezier(0.4,0,1,1)] motion-reduce:animate-none"
|
|
278
280
|
@keydown="onKeydown"
|
|
279
281
|
>
|
|
280
282
|
<!-- reka wants both for a labelled dialog; the caption is the title. -->
|
|
@@ -76,7 +76,7 @@ const bodyPad = computed(() =>
|
|
|
76
76
|
<DialogRoot :open="open" @update:open="(v) => $emit('update:open', v)">
|
|
77
77
|
<DialogPortal>
|
|
78
78
|
<DialogOverlay
|
|
79
|
-
class="fixed inset-0 z-(--z-overlay)
|
|
79
|
+
class="lp-scrim fixed inset-0 z-(--z-overlay) backdrop-blur-sm data-[state=open]:animate-[fade-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[fade-out_120ms_ease]"
|
|
80
80
|
/>
|
|
81
81
|
<!-- Centred by a full-screen flex wrapper rather than by translating the
|
|
82
82
|
panel off its own centre. With `top:50% / -translate-y-1/2` the panel
|
package/src/index.ts
CHANGED
|
@@ -165,6 +165,7 @@ export {
|
|
|
165
165
|
useTheme,
|
|
166
166
|
} from "./theme/useTheme"
|
|
167
167
|
export type { RevealOrigin } from "./theme/useTheme"
|
|
168
|
+
export { detectBackdropFilter, isBrokenBackdropEngine, NO_BACKDROP_ATTR } from "./theme/backdrop"
|
|
168
169
|
export { DEFAULT_SURFACE } from "./theme/tokens"
|
|
169
170
|
export { defineTheme } from "./theme/define"
|
|
170
171
|
export type { ThemeOverrides } from "./theme/define"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* backdrop-filter capability detection.
|
|
3
|
+
*
|
|
4
|
+
* Every glass surface in the kit (dialog scrims, dropdown panels, the glass
|
|
5
|
+
* skin) leans on backdrop-filter. WebKitGTK — the engine Tauri embeds on Linux,
|
|
6
|
+
* and the one behind WebKit2GTK desktop shells — parses the property, reports
|
|
7
|
+
* it as supported through BOTH CSS.supports() and @supports, resolves it to a
|
|
8
|
+
* real computed value, and then composites it as a no-op. The layer tints; it
|
|
9
|
+
* never blurs.
|
|
10
|
+
*
|
|
11
|
+
* That makes the usual feature query worthless: there is no CSS condition that
|
|
12
|
+
* separates a working implementation from this one, because the engine answers
|
|
13
|
+
* "yes" to all of them. Tailwind's own Safari workaround does not fire either
|
|
14
|
+
* (WebKitGTK matches `margin-trim`, which the hack uses to rule Safari out).
|
|
15
|
+
*
|
|
16
|
+
* So the check is a runtime one, and deliberately conservative: it only flags
|
|
17
|
+
* the engine fingerprint that is known to be broken, and treats anything it
|
|
18
|
+
* cannot positively identify as working. A false negative costs a blur nobody
|
|
19
|
+
* sees; a false positive would strip the blur from browsers that render it
|
|
20
|
+
* correctly, which is the worse failure.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** Set on <html> when backdrop-filter resolves but does not composite. */
|
|
24
|
+
export const NO_BACKDROP_ATTR = "data-lp-no-backdrop"
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
* WebKit that is neither Chromium nor an Apple platform. Chromium keeps
|
|
28
|
+
* "AppleWebKit" in its UA string, so it has to be excluded explicitly; real
|
|
29
|
+
* Safari only ever runs on a Mac/iOS platform string. What survives both
|
|
30
|
+
* filters is WebKitGTK/WPE — the broken case.
|
|
31
|
+
*/
|
|
32
|
+
export function isBrokenBackdropEngine(nav: Navigator = navigator): boolean {
|
|
33
|
+
const ua = nav.userAgent
|
|
34
|
+
if (!/AppleWebKit/.test(ua)) return false
|
|
35
|
+
if (/Chrome|Chromium|Edg\//.test(ua)) return false
|
|
36
|
+
return !/Mac|iPhone|iPad|iPod/.test(nav.platform || "")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Flag the document when backdrop-filter cannot be trusted, so the fallback
|
|
41
|
+
* rules in tokens.css can trade blur for opacity. Safe to call more than once
|
|
42
|
+
* and a no-op outside the browser.
|
|
43
|
+
*/
|
|
44
|
+
export function detectBackdropFilter(
|
|
45
|
+
root: HTMLElement = document.documentElement,
|
|
46
|
+
): boolean {
|
|
47
|
+
if (typeof navigator === "undefined") return true
|
|
48
|
+
const usable = !isBrokenBackdropEngine()
|
|
49
|
+
root.toggleAttribute(NO_BACKDROP_ATTR, !usable)
|
|
50
|
+
return usable
|
|
51
|
+
}
|
package/src/theme/useTheme.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* serialize/parse with validation.
|
|
6
6
|
*/
|
|
7
7
|
import { computed, readonly, shallowRef, type ComputedRef, type DeepReadonly, type Ref } from "vue"
|
|
8
|
+
import { detectBackdropFilter } from "./backdrop"
|
|
8
9
|
import {
|
|
9
10
|
COLOR_VARS,
|
|
10
11
|
DEFAULT_SURFACE,
|
|
@@ -75,6 +76,9 @@ export function applyTheme(theme: TokenSet, root: HTMLElement = document.documen
|
|
|
75
76
|
}
|
|
76
77
|
root.dataset.themeMode = theme.mode
|
|
77
78
|
root.style.colorScheme = theme.mode
|
|
79
|
+
// Engine capability, not theme data — but this is the one path every consumer
|
|
80
|
+
// already calls, so the glass fallback lands without extra wiring.
|
|
81
|
+
detectBackdropFilter(root)
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
/** Point a circular theme reveal expands from (viewport px). */
|
package/src/tokens/tokens.css
CHANGED
|
@@ -120,6 +120,15 @@
|
|
|
120
120
|
--z-popover: 120;
|
|
121
121
|
--z-tooltip: 130;
|
|
122
122
|
--z-toast: 200;
|
|
123
|
+
|
|
124
|
+
/* A lightbox is usually opened FROM a dialog — click a thumbnail in a modal
|
|
125
|
+
and the full image comes up over it. Sharing --z-overlay/--z-modal left
|
|
126
|
+
that order to DOM insertion alone, so the lightbox's own scrim landed on
|
|
127
|
+
top of the modal panel and swallowed it while double-darkening the page
|
|
128
|
+
behind. Its own rung above the modal (but below tooltip/toast, which must
|
|
129
|
+
outrank everything) makes the stack explicit. */
|
|
130
|
+
--z-lightbox-overlay: 140;
|
|
131
|
+
--z-lightbox: 150;
|
|
123
132
|
}
|
|
124
133
|
|
|
125
134
|
/* ── overlay enter/exit animations ───────────────
|
|
@@ -379,6 +388,45 @@
|
|
|
379
388
|
-webkit-backdrop-filter: blur(var(--surface-panel-blur)) saturate(var(--surface-panel-saturate));
|
|
380
389
|
}
|
|
381
390
|
|
|
391
|
+
/*
|
|
392
|
+
* No-backdrop fallback. WebKitGTK — the engine behind Tauri on Linux — parses
|
|
393
|
+
* backdrop-filter and reports it through both CSS.supports() and @supports,
|
|
394
|
+
* then composites it as a no-op: the layer tints but never blurs. Since the
|
|
395
|
+
* lie makes @supports useless here, detectBackdropFilter() sets this flag on
|
|
396
|
+
* <html> instead, and every glass surface trades the blur it cannot get for
|
|
397
|
+
* the opacity it can. Without it a translucent panel over busy content reads
|
|
398
|
+
* as unfinished rather than frosted.
|
|
399
|
+
*/
|
|
400
|
+
:root[data-lp-no-backdrop] {
|
|
401
|
+
--surface-panel-blur: 0px;
|
|
402
|
+
--surface-panel-saturate: 100%;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
:root[data-lp-no-backdrop] .lp-skin-panel {
|
|
406
|
+
backdrop-filter: none;
|
|
407
|
+
-webkit-backdrop-filter: none;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/*
|
|
411
|
+
* Scrims lean on the same trick. `.lp-scrim` marks the full-screen overlay
|
|
412
|
+
* behind a dialog; where the blur lands the tint stays light, and where it
|
|
413
|
+
* cannot the tint carries the separation on its own.
|
|
414
|
+
*
|
|
415
|
+
* The bump is deliberately mild. Scrims stack — a lightbox opened from a modal
|
|
416
|
+
* paints two — and opacity compounds, so a value tuned to look right alone goes
|
|
417
|
+
* nearly black in a pair. 0.62 reads as a clear backdrop on its own and still
|
|
418
|
+
* leaves the stacked case legible.
|
|
419
|
+
*/
|
|
420
|
+
.lp-scrim {
|
|
421
|
+
background-color: rgb(0 0 0 / var(--scrim-opacity, 0.5));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
:root[data-lp-no-backdrop] .lp-scrim {
|
|
425
|
+
backdrop-filter: none;
|
|
426
|
+
-webkit-backdrop-filter: none;
|
|
427
|
+
--scrim-opacity: 0.62;
|
|
428
|
+
}
|
|
429
|
+
|
|
382
430
|
/* The overlay gradient rides above the base fill but under the content. */
|
|
383
431
|
.lp-skin-panel::before {
|
|
384
432
|
content: "";
|