@leavepulse/ui 0.95.1 → 0.95.2

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.
@@ -1,7 +1,10 @@
1
1
  import { ref, onScopeDispose, readonly } from "vue";
2
+ import { prefersReducedMotion } from "./easing.js";
2
3
  const OVERLAY = '[role="dialog"], [role="alertdialog"], [role="menu"], [role="listbox"], [data-state="open"][data-side]';
3
4
  const INPUT_EVENTS = ["pointerdown", "keydown", "wheel", "touchstart", "input"];
4
5
  const CARRY_MS = 6e4;
6
+ const FADE_OUT_MS = 220;
7
+ const FADE_IN_MS = 320;
5
8
  function editableFocused() {
6
9
  const el = document.activeElement;
7
10
  if (!(el instanceof HTMLElement)) return false;
@@ -34,6 +37,9 @@ function useAppUpdate(options) {
34
37
  const carried = readCarried(key);
35
38
  if (carried && carried.href === location.href && Date.now() - carried.at < CARRY_MS) {
36
39
  restored.value = true;
40
+ if (!prefersReducedMotion()) {
41
+ document.documentElement.animate([{ opacity: 0 }, { opacity: 1 }], { duration: FADE_IN_MS, easing: "cubic-bezier(0.2, 0, 0, 1)" });
42
+ }
37
43
  const top = carried.top;
38
44
  const restore = () => window.scrollTo({ top, behavior: "instant" });
39
45
  requestAnimationFrame(() => requestAnimationFrame(restore));
@@ -44,7 +50,12 @@ function useAppUpdate(options) {
44
50
  sessionStorage.setItem(key, JSON.stringify({ href: location.href, top: window.scrollY, at: Date.now() }));
45
51
  } catch {
46
52
  }
47
- location.reload();
53
+ if (document.hidden || prefersReducedMotion()) {
54
+ location.reload();
55
+ return;
56
+ }
57
+ const fade = document.documentElement.animate([{ opacity: 1 }, { opacity: 0 }], { duration: FADE_OUT_MS, easing: "ease-in", fill: "forwards" });
58
+ fade.finished.then(() => location.reload(), () => location.reload());
48
59
  }
49
60
  function safe() {
50
61
  if (document.hidden) return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.95.1",
3
+ "version": "0.95.2",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -1,4 +1,5 @@
1
1
  import { onScopeDispose, readonly, ref, type Ref } from "vue"
2
+ import { prefersReducedMotion } from "./easing"
2
3
 
3
4
  /*
4
5
  * A new build is out and every open tab should run it, without anyone losing
@@ -20,6 +21,10 @@ import { onScopeDispose, readonly, ref, type Ref } from "vue"
20
21
  * window scroll position, and anything the app already keeps in storage, such
21
22
  * as form drafts. After the reload `restored` is true for the app to say
22
23
  * "updated" if it wants to.
24
+ *
25
+ * A visible reload does not blink: the page fades out before it and fades back
26
+ * in after it, so the swap reads as one change rather than a flash of blank.
27
+ * Under reduced motion, and in a hidden tab, it simply reloads.
23
28
  */
24
29
 
25
30
  export interface AppUpdateOptions {
@@ -51,6 +56,8 @@ export interface AppUpdate {
51
56
  const OVERLAY = '[role="dialog"], [role="alertdialog"], [role="menu"], [role="listbox"], [data-state="open"][data-side]'
52
57
  const INPUT_EVENTS = ["pointerdown", "keydown", "wheel", "touchstart", "input"] as const
53
58
  const CARRY_MS = 60_000
59
+ const FADE_OUT_MS = 220
60
+ const FADE_IN_MS = 320
54
61
 
55
62
  interface Carried {
56
63
  href: string
@@ -94,6 +101,9 @@ export function useAppUpdate(options: AppUpdateOptions): AppUpdate {
94
101
  const carried = readCarried(key)
95
102
  if (carried && carried.href === location.href && Date.now() - carried.at < CARRY_MS) {
96
103
  restored.value = true
104
+ if (!prefersReducedMotion()) {
105
+ document.documentElement.animate([{ opacity: 0 }, { opacity: 1 }], { duration: FADE_IN_MS, easing: "cubic-bezier(0.2, 0, 0, 1)" })
106
+ }
97
107
  const top = carried.top
98
108
  const restore = () => window.scrollTo({ top, behavior: "instant" })
99
109
  requestAnimationFrame(() => requestAnimationFrame(restore))
@@ -106,7 +116,12 @@ export function useAppUpdate(options: AppUpdateOptions): AppUpdate {
106
116
  } catch {
107
117
  // Storage off: the reload still happens, only the scroll is lost.
108
118
  }
109
- location.reload()
119
+ if (document.hidden || prefersReducedMotion()) {
120
+ location.reload()
121
+ return
122
+ }
123
+ const fade = document.documentElement.animate([{ opacity: 1 }, { opacity: 0 }], { duration: FADE_OUT_MS, easing: "ease-in", fill: "forwards" })
124
+ fade.finished.then(() => location.reload(), () => location.reload())
110
125
  }
111
126
 
112
127
  function safe(): boolean {