@leavepulse/ui 0.37.8 → 0.37.10

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.
@@ -15,8 +15,8 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {
15
15
  }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
16
16
  onScroll?: ((event: Event) => any) | undefined;
17
17
  }>, {
18
- fade: boolean;
19
18
  instant: boolean;
19
+ fade: boolean;
20
20
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
21
21
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
22
22
  declare const _default: typeof __VLS_export;
@@ -14,15 +14,24 @@ function useShift(options = {}) {
14
14
  let lastWidth = 0;
15
15
  let inFlight = false;
16
16
  let tweenStart = 0;
17
- const scrolled = /* @__PURE__ */ new Map();
17
+ const scrolled = /* @__PURE__ */ new Set();
18
18
  const rememberScroll = (event) => {
19
19
  const target = event.target;
20
- if (target instanceof Element) scrolled.set(target, target.scrollTop);
20
+ if (target instanceof Element) scrolled.add(target);
21
21
  };
22
- function restoreScroll() {
23
- for (const [node, top] of scrolled) {
22
+ function readScroll() {
23
+ const tops = /* @__PURE__ */ new Map();
24
+ for (const node of scrolled) {
24
25
  if (!node.isConnected) scrolled.delete(node);
25
- else if (node.scrollTop !== top) node.scrollTop = top;
26
+ else tops.set(node, node.scrollTop);
27
+ }
28
+ return tops;
29
+ }
30
+ function restoreScroll(tops) {
31
+ for (const [node, top] of tops) {
32
+ if (node.isConnected && node.scrollTop !== top) {
33
+ node.scrollTo({ top, behavior: "instant" });
34
+ }
26
35
  }
27
36
  }
28
37
  const axisOf = () => toValue(options.axis) ?? "height";
@@ -39,6 +48,7 @@ function useShift(options = {}) {
39
48
  }
40
49
  const fromHeight = lastHeight;
41
50
  const fromWidth = lastWidth;
51
+ const tops = readScroll();
42
52
  if (wantsHeight()) {
43
53
  node.style.height = "";
44
54
  node.style.maxHeight = "none";
@@ -51,7 +61,7 @@ function useShift(options = {}) {
51
61
  const toWidth = wantsWidth() ? node.offsetWidth : 0;
52
62
  if (wantsHeight()) node.style.maxHeight = "";
53
63
  if (wantsWidth()) node.style.maxWidth = "";
54
- restoreScroll();
64
+ restoreScroll(tops);
55
65
  lastHeight = toHeight;
56
66
  lastWidth = toWidth;
57
67
  const movesHeight = wantsHeight() && fromHeight !== 0 && toHeight !== fromHeight;
@@ -62,7 +72,7 @@ function useShift(options = {}) {
62
72
  void node.offsetHeight;
63
73
  if (movesHeight) node.style.height = `${toHeight}px`;
64
74
  if (movesWidth) node.style.width = `${toWidth}px`;
65
- restoreScroll();
75
+ restoreScroll(tops);
66
76
  inFlight = true;
67
77
  tweenStart = performance.now();
68
78
  resizing.value = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.37.8",
3
+ "version": "0.37.10",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -92,22 +92,40 @@ export function useShift(
92
92
  /** When the in-flight tween started, to tell a duplicate from a follow-up. */
93
93
  let tweenStart = 0
94
94
  /**
95
- * Scroll positions inside the box, kept from the scroll events themselves.
95
+ * Scrolling lists inside the box, found through their scroll events.
96
96
  * Measuring lifts the height cap, and for that one reflow a scrolling list
97
97
  * inside (a picker, a span tree) is as tall as its content: it no longer
98
98
  * overflows, the browser clamps its scrollTop to 0, and putting the cap back
99
99
  * does not bring the position back. Every click that changed the content —
100
100
  * ticking an item, picking a span — threw the list back to the top.
101
+ *
102
+ * The position to put back is read live just before measuring, and only a
103
+ * list the measurement actually moved is touched, with an instant write. An
104
+ * earlier version replayed the last scroll event's figure through
105
+ * `scrollTop` on every retune. Inside a `scroll-smooth` area that write is
106
+ * itself animated, its in-between frames were recorded as new positions,
107
+ * and any mutation while the user scrolled — a row re-rendering on hover —
108
+ * replayed a stale one: a long dialog crept back to the top and could not
109
+ * be scrolled at all.
101
110
  */
102
- const scrolled = new Map<Element, number>()
111
+ const scrolled = new Set<Element>()
103
112
  const rememberScroll = (event: Event) => {
104
113
  const target = event.target
105
- if (target instanceof Element) scrolled.set(target, target.scrollTop)
114
+ if (target instanceof Element) scrolled.add(target)
106
115
  }
107
- function restoreScroll() {
108
- for (const [node, top] of scrolled) {
116
+ function readScroll(): Map<Element, number> {
117
+ const tops = new Map<Element, number>()
118
+ for (const node of scrolled) {
109
119
  if (!node.isConnected) scrolled.delete(node)
110
- else if (node.scrollTop !== top) node.scrollTop = top
120
+ else tops.set(node, node.scrollTop)
121
+ }
122
+ return tops
123
+ }
124
+ function restoreScroll(tops: Map<Element, number>) {
125
+ for (const [node, top] of tops) {
126
+ if (node.isConnected && node.scrollTop !== top) {
127
+ node.scrollTo({ top, behavior: "instant" })
128
+ }
111
129
  }
112
130
  }
113
131
 
@@ -153,6 +171,7 @@ export function useShift(
153
171
  // already reflowed, so the element can only report where it is going.
154
172
  const fromHeight = lastHeight
155
173
  const fromWidth = lastWidth
174
+ const tops = readScroll()
156
175
 
157
176
  // Measured with any cap lifted, then the clamp is left to CSS. A `max-*`
158
177
  // from a class makes the element report the clipped figure once the cap
@@ -170,7 +189,7 @@ export function useShift(
170
189
  const toWidth = wantsWidth() ? node.offsetWidth : 0
171
190
  if (wantsHeight()) node.style.maxHeight = ""
172
191
  if (wantsWidth()) node.style.maxWidth = ""
173
- restoreScroll()
192
+ restoreScroll(tops)
174
193
  lastHeight = toHeight
175
194
  lastWidth = toWidth
176
195
 
@@ -193,7 +212,7 @@ export function useShift(
193
212
  void node.offsetHeight
194
213
  if (movesHeight) node.style.height = `${toHeight}px`
195
214
  if (movesWidth) node.style.width = `${toWidth}px`
196
- restoreScroll()
215
+ restoreScroll(tops)
197
216
 
198
217
  inFlight = true
199
218
  tweenStart = performance.now()
@@ -451,8 +451,8 @@
451
451
  position: relative;
452
452
  border-width: var(--surface-border-width);
453
453
  box-shadow: var(--surface-panel-inset), var(--surface-panel-shadow);
454
- backdrop-filter: blur(var(--surface-panel-blur)) saturate(var(--surface-panel-saturate));
455
454
  -webkit-backdrop-filter: blur(var(--surface-panel-blur)) saturate(var(--surface-panel-saturate));
455
+ backdrop-filter: blur(var(--surface-panel-blur)) saturate(var(--surface-panel-saturate));
456
456
  }
457
457
 
458
458
  /*
@@ -470,8 +470,8 @@
470
470
  }
471
471
 
472
472
  :root[data-lp-no-backdrop] .lp-skin-panel {
473
- backdrop-filter: none;
474
473
  -webkit-backdrop-filter: none;
474
+ backdrop-filter: none;
475
475
  }
476
476
 
477
477
  /*
@@ -486,8 +486,8 @@
486
486
  */
487
487
  .lp-scrim {
488
488
  background-color: rgb(0 0 0 / var(--scrim-opacity, 0.5));
489
- backdrop-filter: blur(var(--scrim-blur, 4px));
490
489
  -webkit-backdrop-filter: blur(var(--scrim-blur, 4px));
490
+ backdrop-filter: blur(var(--scrim-blur, 4px));
491
491
  }
492
492
 
493
493
  /*
@@ -497,9 +497,17 @@
497
497
  * while the engines that do paint it disagreed with the token. One owner for
498
498
  * both halves keeps the two cases in step.
499
499
  */
500
+ /*
501
+ * The prefixed declaration comes FIRST, everywhere in this file. Lightning CSS
502
+ * (Tailwind v4's minifier) folds the pair into one property and keeps the
503
+ * last spelling it sees: with `-webkit-` last, a consumer's production build
504
+ * shipped only `-webkit-backdrop-filter`, which Chromium and Firefox ignore —
505
+ * the scrim tinted but never blurred, while the unminified dev server and
506
+ * this preview looked fine.
507
+ */
500
508
  :root[data-lp-no-backdrop] .lp-scrim {
501
- backdrop-filter: none;
502
509
  -webkit-backdrop-filter: none;
510
+ backdrop-filter: none;
503
511
  --scrim-opacity: 0.62;
504
512
  }
505
513