@leavepulse/ui 0.54.0 → 0.55.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/README.md +3 -1
- package/dist/chunks/LpThemeProvider-CQ2jC9TA.js +69 -0
- package/dist/chunks/{LpThemeSwitcher-CPmhYOjX.js → LpThemeSwitcher-Fz85BCRS.js} +1 -1
- package/dist/chunks/{useTheme-DJm1JSsY.js → useTheme-2qHG8NEU.js} +14 -10
- package/dist/components/LpThemeProvider.vue.d.ts +5 -0
- package/dist/components/LpThemeProvider.vue.js +1 -1
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -10
- package/dist/theme/useTheme.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/LpThemeProvider.vue +49 -6
- package/src/index.ts +1 -0
- package/src/theme/useTheme.ts +28 -14
- package/dist/chunks/LpThemeProvider-Be4r2TQX.js +0 -35
package/README.md
CHANGED
|
@@ -61,7 +61,9 @@ useTheme().setSystem({ light, dark }) // follow the OS colour scheme
|
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
`LpThemeProvider` applies a theme to one subtree only (a dark sidebar in a light
|
|
64
|
-
app, a live preview).
|
|
64
|
+
app, a live preview). With `transition`, changing its `theme` reveals the new one
|
|
65
|
+
with the same circular transition as the page switch, from where the user last
|
|
66
|
+
pressed. `revealTransition(update, origin)` runs any repaint behind that reveal.
|
|
65
67
|
|
|
66
68
|
### App-wide configuration
|
|
67
69
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { defineComponent, ref, shallowRef, computed, watch, nextTick, openBlock, createBlock, resolveDynamicComponent, mergeProps, unref, withCtx, renderSlot } from "vue";
|
|
2
|
+
import { t as themeEntries, r as revealTransition } from "./useTheme-2qHG8NEU.js";
|
|
3
|
+
import { useMergedAttrs } from "../composables/useMergedClass.js";
|
|
4
|
+
const PRESS_FRESH_MS = 1500;
|
|
5
|
+
let lastPress = null;
|
|
6
|
+
if (typeof window !== "undefined") {
|
|
7
|
+
window.addEventListener(
|
|
8
|
+
"pointerdown",
|
|
9
|
+
(event) => lastPress = { x: event.clientX, y: event.clientY, at: performance.now() },
|
|
10
|
+
{ capture: true, passive: true }
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
const __default__ = { inheritAttrs: false };
|
|
14
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
15
|
+
...__default__,
|
|
16
|
+
__name: "LpThemeProvider",
|
|
17
|
+
props: {
|
|
18
|
+
theme: {},
|
|
19
|
+
as: { default: "div" },
|
|
20
|
+
transition: { type: Boolean, default: false }
|
|
21
|
+
},
|
|
22
|
+
setup(__props) {
|
|
23
|
+
const props = __props;
|
|
24
|
+
const { class: rootClass, attrs: rest } = useMergedAttrs("bg-surface text-ink");
|
|
25
|
+
const root = ref(null);
|
|
26
|
+
const shown = shallowRef(props.theme);
|
|
27
|
+
const style = computed(() => ({
|
|
28
|
+
...Object.fromEntries(themeEntries(shown.value)),
|
|
29
|
+
colorScheme: shown.value.mode
|
|
30
|
+
}));
|
|
31
|
+
function originOf() {
|
|
32
|
+
if (lastPress && performance.now() - lastPress.at < PRESS_FRESH_MS) return lastPress;
|
|
33
|
+
const value = root.value;
|
|
34
|
+
const el = value instanceof HTMLElement ? value : value?.$el;
|
|
35
|
+
const box = el?.getBoundingClientRect?.();
|
|
36
|
+
return box ? { x: box.left + box.width / 2, y: box.top + box.height / 2 } : void 0;
|
|
37
|
+
}
|
|
38
|
+
watch(
|
|
39
|
+
() => props.theme,
|
|
40
|
+
(next) => {
|
|
41
|
+
if (!props.transition) {
|
|
42
|
+
shown.value = next;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
revealTransition(async () => {
|
|
46
|
+
shown.value = next;
|
|
47
|
+
await nextTick();
|
|
48
|
+
}, originOf());
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
return (_ctx, _cache) => {
|
|
52
|
+
return openBlock(), createBlock(resolveDynamicComponent(__props.as), mergeProps({
|
|
53
|
+
ref_key: "root",
|
|
54
|
+
ref: root,
|
|
55
|
+
style: style.value,
|
|
56
|
+
"data-theme-mode": shown.value.mode,
|
|
57
|
+
class: unref(rootClass)
|
|
58
|
+
}, unref(rest)), {
|
|
59
|
+
default: withCtx(() => [
|
|
60
|
+
renderSlot(_ctx.$slots, "default")
|
|
61
|
+
]),
|
|
62
|
+
_: 3
|
|
63
|
+
}, 16, ["style", "data-theme-mode", "class"]);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
export {
|
|
68
|
+
_sfc_main as _
|
|
69
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineComponent, computed, onMounted, ref, openBlock, createBlock, withCtx, createElementVNode, normalizeClass, createElementBlock, createVNode, toDisplayString, createCommentVNode } from "vue";
|
|
2
2
|
import { useLabels } from "../composables/useLpConfig.js";
|
|
3
|
-
import { u as useTheme, p as presets } from "./useTheme-
|
|
3
|
+
import { u as useTheme, p as presets } from "./useTheme-2qHG8NEU.js";
|
|
4
4
|
import { _ as _sfc_main$1 } from "./LpContextMenu-EF_90u3g.js";
|
|
5
5
|
import { _ as _sfc_main$2 } from "./LpIcon-DYtaid0K.js";
|
|
6
6
|
const _hoisted_1 = ["aria-label", "title"];
|
|
@@ -439,10 +439,10 @@ function applyTheme(theme, root = document.documentElement) {
|
|
|
439
439
|
root.style.colorScheme = theme.mode;
|
|
440
440
|
detectBackdropFilter(root);
|
|
441
441
|
}
|
|
442
|
-
function
|
|
442
|
+
function revealTransition(update, origin, durationMs = 480) {
|
|
443
443
|
const doc = document;
|
|
444
444
|
if (typeof doc.startViewTransition !== "function" || prefersReducedMotion()) {
|
|
445
|
-
|
|
445
|
+
void update();
|
|
446
446
|
return;
|
|
447
447
|
}
|
|
448
448
|
const x = origin?.x ?? window.innerWidth / 2;
|
|
@@ -451,9 +451,9 @@ function applyThemeWithTransition(theme, origin, root = document.documentElement
|
|
|
451
451
|
Math.max(x, window.innerWidth - x),
|
|
452
452
|
Math.max(y, window.innerHeight - y)
|
|
453
453
|
);
|
|
454
|
-
const transition = doc.startViewTransition(
|
|
454
|
+
const transition = doc.startViewTransition(update);
|
|
455
455
|
transition.ready.then(() => {
|
|
456
|
-
|
|
456
|
+
document.documentElement.animate(
|
|
457
457
|
{
|
|
458
458
|
clipPath: [`circle(${endRadius}px at ${x}px ${y}px)`, `circle(0px at ${x}px ${y}px)`]
|
|
459
459
|
},
|
|
@@ -471,6 +471,9 @@ function applyThemeWithTransition(theme, origin, root = document.documentElement
|
|
|
471
471
|
);
|
|
472
472
|
});
|
|
473
473
|
}
|
|
474
|
+
function applyThemeWithTransition(theme, origin, root = document.documentElement, durationMs = 480) {
|
|
475
|
+
revealTransition(() => applyTheme(theme, root), origin, durationMs);
|
|
476
|
+
}
|
|
474
477
|
function serializeTheme(theme) {
|
|
475
478
|
return JSON.stringify(theme);
|
|
476
479
|
}
|
|
@@ -614,14 +617,15 @@ export {
|
|
|
614
617
|
light as l,
|
|
615
618
|
prefersDarkScheme as m,
|
|
616
619
|
nord as n,
|
|
617
|
-
|
|
620
|
+
rose as o,
|
|
618
621
|
presets as p,
|
|
619
|
-
|
|
620
|
-
|
|
622
|
+
setGlassBackdrop as q,
|
|
623
|
+
revealTransition as r,
|
|
621
624
|
serializeTheme as s,
|
|
622
625
|
themeEntries as t,
|
|
623
626
|
useTheme as u,
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
+
themeFromCookie as v,
|
|
628
|
+
themeToCssRule as w,
|
|
629
|
+
themeToCssVars as x,
|
|
630
|
+
violet as y
|
|
627
631
|
};
|
|
@@ -6,12 +6,17 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
6
6
|
theme: TokenSet;
|
|
7
7
|
/** Root element. A wrapper that should not affect layout can use `class="contents"`. */
|
|
8
8
|
as?: string | Component;
|
|
9
|
+
/** Reveal a change of `theme` with the circular transition. */
|
|
10
|
+
transition?: boolean;
|
|
9
11
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
|
|
10
12
|
theme: TokenSet;
|
|
11
13
|
/** Root element. A wrapper that should not affect layout can use `class="contents"`. */
|
|
12
14
|
as?: string | Component;
|
|
15
|
+
/** Reveal a change of `theme` with the circular transition. */
|
|
16
|
+
transition?: boolean;
|
|
13
17
|
}> & Readonly<{}>, {
|
|
14
18
|
as: string | Component;
|
|
19
|
+
transition: boolean;
|
|
15
20
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
|
|
16
21
|
default?: (props: {}) => any;
|
|
17
22
|
}>;
|
package/dist/index.d.ts
CHANGED
|
@@ -168,7 +168,7 @@ export { blockTitle, defineBlocks } from "./layout/registry";
|
|
|
168
168
|
export type { BlockDef, BlockRegistry } from "./layout/registry";
|
|
169
169
|
export { useLayout } from "./layout/useLayout";
|
|
170
170
|
export type { UseLayout, UseLayoutOptions } from "./layout/useLayout";
|
|
171
|
-
export { applyTheme, applyThemeWithTransition, bootstrapTheme, parseTheme, serializeTheme, THEME_COOKIE, themeFromCookie, themeToCssRule, themeToCssVars, useTheme, } from "./theme/useTheme";
|
|
171
|
+
export { applyTheme, applyThemeWithTransition, revealTransition, bootstrapTheme, parseTheme, serializeTheme, THEME_COOKIE, themeFromCookie, themeToCssRule, themeToCssVars, useTheme, } from "./theme/useTheme";
|
|
172
172
|
export type { RevealOrigin } from "./theme/useTheme";
|
|
173
173
|
export { detectBackdropFilter, GLASS_BACKDROP_ATTR, isBrokenBackdropEngine, NO_BACKDROP_ATTR, setGlassBackdrop } from "./theme/backdrop";
|
|
174
174
|
export { DEFAULT_SURFACE } from "./theme/tokens";
|
package/dist/index.js
CHANGED
|
@@ -100,7 +100,7 @@ import { _ as _71 } from "./chunks/LpTabs-BMSBB78h.js";
|
|
|
100
100
|
import { _ as _72 } from "./chunks/LpTagsInput-D1SuqbhB.js";
|
|
101
101
|
import { addTags, splitTags } from "./components/tags.js";
|
|
102
102
|
import { _ as _73 } from "./chunks/LpTextarea-uuScWo6-.js";
|
|
103
|
-
import { _ as _74 } from "./chunks/LpThemeSwitcher-
|
|
103
|
+
import { _ as _74 } from "./chunks/LpThemeSwitcher-Fz85BCRS.js";
|
|
104
104
|
import { _ as _75 } from "./chunks/LpTilt-B7KHX1zH.js";
|
|
105
105
|
import { _ as _76 } from "./chunks/LpToaster-DTtWuKvq.js";
|
|
106
106
|
import { _ as _77 } from "./chunks/LpTooltip-CXVKcOXT.js";
|
|
@@ -111,8 +111,8 @@ import { deserializeLayout, serializeLayout } from "./layout/tree.js";
|
|
|
111
111
|
import { addLeaf, addTab, countBlocks, countLeaves, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, setActiveTab } from "./layout/tree.js";
|
|
112
112
|
import { blockTitle, defineBlocks } from "./layout/registry.js";
|
|
113
113
|
import { layoutController, useLayout } from "./layout/useLayout.js";
|
|
114
|
-
import { p as presets, a as parseTheme, s as serializeTheme } from "./chunks/useTheme-
|
|
115
|
-
import { D, G, N, T, b as b2, c as c2, d, e, f, g, h, i as i2, l, j, n, k, m, r, o,
|
|
114
|
+
import { p as presets, a as parseTheme, s as serializeTheme } from "./chunks/useTheme-2qHG8NEU.js";
|
|
115
|
+
import { D, G, N, T, b as b2, c as c2, d, e, f, g, h, i as i2, l, j, n, k, m, r, o, q, t, v, w, x, u, y } from "./chunks/useTheme-2qHG8NEU.js";
|
|
116
116
|
import { useClipboard } from "./composables/useClipboard.js";
|
|
117
117
|
import { activeShortcuts, appleModifierLabel, findHotkeyConflicts, hotkeyConflicts, hotkeySteps, isAppleKeyboard, matchesHotkey, mergeShortcutGroups, pendingHotkey, sequenceProgress, useAppleKeyboard, useHotkeys } from "./composables/useHotkeys.js";
|
|
118
118
|
import { anyModalOpen, topModalElement, useModalLayer } from "./components/modalDepth.js";
|
|
@@ -137,7 +137,7 @@ import { useShift } from "./composables/useShift.js";
|
|
|
137
137
|
import { useTilt } from "./composables/useTilt.js";
|
|
138
138
|
import { useZoomPan } from "./composables/useZoomPan.js";
|
|
139
139
|
import { useToast } from "./composables/useToast.js";
|
|
140
|
-
import { _ as _81 } from "./chunks/LpThemeProvider-
|
|
140
|
+
import { _ as _81 } from "./chunks/LpThemeProvider-CQ2jC9TA.js";
|
|
141
141
|
import { isInternalHref, resolveLink } from "./components/link.js";
|
|
142
142
|
function defineTheme(base, overrides = {}) {
|
|
143
143
|
const src = typeof base === "string" ? presets[base] : base;
|
|
@@ -399,10 +399,11 @@ export {
|
|
|
399
399
|
resolveCssColor,
|
|
400
400
|
resolveLink,
|
|
401
401
|
resolveSlices,
|
|
402
|
+
r as revealTransition,
|
|
402
403
|
rgbToHex,
|
|
403
404
|
rgbToHsv,
|
|
404
405
|
ringWindow,
|
|
405
|
-
|
|
406
|
+
o as rose,
|
|
406
407
|
sameRange,
|
|
407
408
|
seamProgress,
|
|
408
409
|
seamReached,
|
|
@@ -411,7 +412,7 @@ export {
|
|
|
411
412
|
serializeLayout,
|
|
412
413
|
serializeTheme,
|
|
413
414
|
setActiveTab,
|
|
414
|
-
|
|
415
|
+
q as setGlassBackdrop,
|
|
415
416
|
sortNodes,
|
|
416
417
|
splitTags,
|
|
417
418
|
stepMatch,
|
|
@@ -420,9 +421,9 @@ export {
|
|
|
420
421
|
subtreeSize,
|
|
421
422
|
svgToPng,
|
|
422
423
|
t as themeEntries,
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
424
|
+
v as themeFromCookie,
|
|
425
|
+
w as themeToCssRule,
|
|
426
|
+
x as themeToCssVars,
|
|
426
427
|
tileUrl,
|
|
427
428
|
toCsv,
|
|
428
429
|
toRecentItem,
|
|
@@ -459,5 +460,5 @@ export {
|
|
|
459
460
|
useTilt,
|
|
460
461
|
useToast,
|
|
461
462
|
useZoomPan,
|
|
462
|
-
|
|
463
|
+
y as violet
|
|
463
464
|
};
|
package/dist/theme/useTheme.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export interface RevealOrigin {
|
|
|
15
15
|
x: number;
|
|
16
16
|
y: number;
|
|
17
17
|
}
|
|
18
|
+
export declare function revealTransition(update: () => void | Promise<void>, origin?: RevealOrigin, durationMs?: number): void;
|
|
18
19
|
export declare function applyThemeWithTransition(theme: TokenSet, origin?: RevealOrigin, root?: HTMLElement, durationMs?: number): void;
|
|
19
20
|
export declare function serializeTheme(theme: TokenSet): string;
|
|
20
21
|
export declare function themeToCssVars(theme: TokenSet): string;
|
package/package.json
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
// The last press anywhere on the page: a theme change triggered by a toggle
|
|
3
|
+
// reveals from the toggle the user just pressed.
|
|
4
|
+
const PRESS_FRESH_MS = 1500
|
|
5
|
+
let lastPress: { x: number; y: number; at: number } | null = null
|
|
6
|
+
if (typeof window !== "undefined") {
|
|
7
|
+
window.addEventListener(
|
|
8
|
+
"pointerdown",
|
|
9
|
+
(event) => (lastPress = { x: event.clientX, y: event.clientY, at: performance.now() }),
|
|
10
|
+
{ capture: true, passive: true },
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
|
|
2
14
|
export default { inheritAttrs: false }
|
|
3
15
|
</script>
|
|
4
16
|
|
|
@@ -9,10 +21,14 @@ export default { inheritAttrs: false }
|
|
|
9
21
|
* keeps its own look. Every token is declared on this element, derived ones
|
|
10
22
|
* included, so kit components inside re-skin while the page around them keeps
|
|
11
23
|
* the global theme. Nothing is persisted; this is presentation only.
|
|
24
|
+
*
|
|
25
|
+
* With `transition`, a change of `theme` repaints the subtree behind the same
|
|
26
|
+
* circular reveal the global theme switch uses, growing from where the user
|
|
27
|
+
* last pressed, or from the subtree's centre.
|
|
12
28
|
*/
|
|
13
|
-
import { computed, type Component } from "vue"
|
|
29
|
+
import { computed, nextTick, ref, shallowRef, watch, type Component, type ComponentPublicInstance } from "vue"
|
|
14
30
|
import type { TokenSet } from "../theme/tokens"
|
|
15
|
-
import { themeEntries } from "../theme/useTheme"
|
|
31
|
+
import { revealTransition, themeEntries, type RevealOrigin } from "../theme/useTheme"
|
|
16
32
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
17
33
|
|
|
18
34
|
const props = withDefaults(
|
|
@@ -20,22 +36,49 @@ const props = withDefaults(
|
|
|
20
36
|
theme: TokenSet
|
|
21
37
|
/** Root element. A wrapper that should not affect layout can use `class="contents"`. */
|
|
22
38
|
as?: string | Component
|
|
39
|
+
/** Reveal a change of `theme` with the circular transition. */
|
|
40
|
+
transition?: boolean
|
|
23
41
|
}>(),
|
|
24
|
-
{ as: "div" },
|
|
42
|
+
{ as: "div", transition: false },
|
|
25
43
|
)
|
|
26
44
|
|
|
27
45
|
// Paints its own surface and text so the scoped theme reads as one; a class
|
|
28
46
|
// like `bg-transparent` overrides that.
|
|
29
47
|
const { class: rootClass, attrs: rest } = useMergedAttrs("bg-surface text-ink")
|
|
30
48
|
|
|
49
|
+
const root = ref<HTMLElement | ComponentPublicInstance | null>(null)
|
|
50
|
+
const shown = shallowRef(props.theme)
|
|
51
|
+
|
|
31
52
|
const style = computed(() => ({
|
|
32
|
-
...Object.fromEntries(themeEntries(
|
|
33
|
-
colorScheme:
|
|
53
|
+
...Object.fromEntries(themeEntries(shown.value)),
|
|
54
|
+
colorScheme: shown.value.mode,
|
|
34
55
|
}))
|
|
56
|
+
|
|
57
|
+
function originOf(): RevealOrigin | undefined {
|
|
58
|
+
if (lastPress && performance.now() - lastPress.at < PRESS_FRESH_MS) return lastPress
|
|
59
|
+
const value = root.value
|
|
60
|
+
const el = value instanceof HTMLElement ? value : (value?.$el as Element | undefined)
|
|
61
|
+
const box = el?.getBoundingClientRect?.()
|
|
62
|
+
return box ? { x: box.left + box.width / 2, y: box.top + box.height / 2 } : undefined
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
watch(
|
|
66
|
+
() => props.theme,
|
|
67
|
+
(next) => {
|
|
68
|
+
if (!props.transition) {
|
|
69
|
+
shown.value = next
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
revealTransition(async () => {
|
|
73
|
+
shown.value = next
|
|
74
|
+
await nextTick()
|
|
75
|
+
}, originOf())
|
|
76
|
+
},
|
|
77
|
+
)
|
|
35
78
|
</script>
|
|
36
79
|
|
|
37
80
|
<template>
|
|
38
|
-
<component :is="as" :style="style" :data-theme-mode="
|
|
81
|
+
<component :is="as" ref="root" :style="style" :data-theme-mode="shown.mode" :class="rootClass" v-bind="rest">
|
|
39
82
|
<slot />
|
|
40
83
|
</component>
|
|
41
84
|
</template>
|
package/src/index.ts
CHANGED
package/src/theme/useTheme.ts
CHANGED
|
@@ -167,30 +167,28 @@ export interface RevealOrigin {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
type ViewTransitionDocument = Document & {
|
|
170
|
-
startViewTransition?: (cb: () => void) => { ready: Promise<void>; finished: Promise<void> }
|
|
170
|
+
startViewTransition?: (cb: () => void | Promise<void>) => { ready: Promise<void>; finished: Promise<void> }
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
/*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
* underneath.
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
174
|
+
* Run `update` behind a "circular reveal": a clip-path circle grows from
|
|
175
|
+
* `origin` (viewport px, the centre when omitted) and shows the repainted page
|
|
176
|
+
* underneath. Only what `update` changed looks different, so repainting one
|
|
177
|
+
* subtree reveals just that subtree. Uses the View Transitions API and falls
|
|
178
|
+
* back to a plain update when it's unsupported or the user prefers reduced
|
|
179
|
+
* motion. `update` may be async (a framework flush); the snapshot waits for it.
|
|
180
180
|
*/
|
|
181
|
-
export function
|
|
182
|
-
|
|
181
|
+
export function revealTransition(
|
|
182
|
+
update: () => void | Promise<void>,
|
|
183
183
|
origin?: RevealOrigin,
|
|
184
|
-
root: HTMLElement = document.documentElement,
|
|
185
184
|
durationMs = 480,
|
|
186
185
|
): void {
|
|
187
186
|
const doc = document as ViewTransitionDocument
|
|
188
187
|
if (typeof doc.startViewTransition !== "function" || prefersReducedMotion()) {
|
|
189
|
-
|
|
188
|
+
void update()
|
|
190
189
|
return
|
|
191
190
|
}
|
|
192
191
|
|
|
193
|
-
// Centre of the viewport is the default origin if no click point is given.
|
|
194
192
|
const x = origin?.x ?? window.innerWidth / 2
|
|
195
193
|
const y = origin?.y ?? window.innerHeight / 2
|
|
196
194
|
// Radius that reaches the farthest viewport corner from the origin.
|
|
@@ -199,13 +197,15 @@ export function applyThemeWithTransition(
|
|
|
199
197
|
Math.max(y, window.innerHeight - y),
|
|
200
198
|
)
|
|
201
199
|
|
|
202
|
-
const transition = doc.startViewTransition(
|
|
200
|
+
const transition = doc.startViewTransition(update)
|
|
203
201
|
transition.ready.then(() => {
|
|
204
202
|
// Animate the OLD snapshot shrinking to a point, revealing the (already
|
|
205
203
|
// repainted) live page underneath through the growing hole. Clipping the
|
|
206
204
|
// old layer — not growing the new one — avoids a one-frame flash of the new
|
|
207
205
|
// theme before the circle starts, which read as the theme "changing twice".
|
|
208
|
-
|
|
206
|
+
// The pseudo-elements belong to the document element whatever was updated,
|
|
207
|
+
// so the animation always targets it.
|
|
208
|
+
document.documentElement.animate(
|
|
209
209
|
{
|
|
210
210
|
clipPath: [`circle(${endRadius}px at ${x}px ${y}px)`, `circle(0px at ${x}px ${y}px)`],
|
|
211
211
|
},
|
|
@@ -224,6 +224,20 @@ export function applyThemeWithTransition(
|
|
|
224
224
|
})
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
/*
|
|
228
|
+
* Apply a theme with the circular reveal, usually from the toggle button's
|
|
229
|
+
* centre. `root` scopes the theme to a subtree; the reveal still runs on the
|
|
230
|
+
* document, since that is where the transition snapshots live.
|
|
231
|
+
*/
|
|
232
|
+
export function applyThemeWithTransition(
|
|
233
|
+
theme: TokenSet,
|
|
234
|
+
origin?: RevealOrigin,
|
|
235
|
+
root: HTMLElement = document.documentElement,
|
|
236
|
+
durationMs = 480,
|
|
237
|
+
): void {
|
|
238
|
+
revealTransition(() => applyTheme(theme, root), origin, durationMs)
|
|
239
|
+
}
|
|
240
|
+
|
|
227
241
|
export function serializeTheme(theme: TokenSet): string {
|
|
228
242
|
return JSON.stringify(theme)
|
|
229
243
|
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { defineComponent, computed, openBlock, createBlock, resolveDynamicComponent, mergeProps, unref, withCtx, renderSlot } from "vue";
|
|
2
|
-
import { t as themeEntries } from "./useTheme-DJm1JSsY.js";
|
|
3
|
-
import { useMergedAttrs } from "../composables/useMergedClass.js";
|
|
4
|
-
const __default__ = { inheritAttrs: false };
|
|
5
|
-
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
6
|
-
...__default__,
|
|
7
|
-
__name: "LpThemeProvider",
|
|
8
|
-
props: {
|
|
9
|
-
theme: {},
|
|
10
|
-
as: { default: "div" }
|
|
11
|
-
},
|
|
12
|
-
setup(__props) {
|
|
13
|
-
const props = __props;
|
|
14
|
-
const { class: rootClass, attrs: rest } = useMergedAttrs("bg-surface text-ink");
|
|
15
|
-
const style = computed(() => ({
|
|
16
|
-
...Object.fromEntries(themeEntries(props.theme)),
|
|
17
|
-
colorScheme: props.theme.mode
|
|
18
|
-
}));
|
|
19
|
-
return (_ctx, _cache) => {
|
|
20
|
-
return openBlock(), createBlock(resolveDynamicComponent(__props.as), mergeProps({
|
|
21
|
-
style: style.value,
|
|
22
|
-
"data-theme-mode": __props.theme.mode,
|
|
23
|
-
class: unref(rootClass)
|
|
24
|
-
}, unref(rest)), {
|
|
25
|
-
default: withCtx(() => [
|
|
26
|
-
renderSlot(_ctx.$slots, "default")
|
|
27
|
-
]),
|
|
28
|
-
_: 3
|
|
29
|
-
}, 16, ["style", "data-theme-mode", "class"]);
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
export {
|
|
34
|
-
_sfc_main as _
|
|
35
|
-
};
|