@wyxos/vibe 1.4.0 → 1.4.1

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/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { nextTick as X, defineComponent as dt, computed as et, ref as B, onMounted as gt, onUnmounted as pt, createElementBlock as Y, openBlock as J, createElementVNode as C, normalizeStyle as mt, createVNode as ht, createCommentVNode as yt, TransitionGroup as vt, unref as V, withCtx as bt, Fragment as xt, renderList as wt, mergeProps as nt, renderSlot as Tt, normalizeClass as Mt, toDisplayString as at } from "vue";
1
+ import { nextTick as X, defineComponent as gt, computed as et, ref as B, onMounted as pt, onUnmounted as mt, createElementBlock as Y, openBlock as J, normalizeClass as nt, createElementVNode as C, normalizeStyle as ht, createVNode as yt, createCommentVNode as vt, TransitionGroup as bt, unref as q, withCtx as xt, Fragment as wt, renderList as Tt, mergeProps as at, renderSlot as Mt, toDisplayString as rt } from "vue";
2
2
  function It() {
3
3
  const t = document.createElement("div");
4
4
  t.style.visibility = "hidden", t.style.overflow = "scroll", t.style.msOverflowStyle = "scrollbar", t.style.width = "100px", t.style.height = "100px", document.body.appendChild(t);
@@ -7,15 +7,15 @@ function It() {
7
7
  const o = t.offsetWidth - a.offsetWidth;
8
8
  return document.body.removeChild(t), o;
9
9
  }
10
- function St(t, a, o, s = {}) {
10
+ function St(t, a, o, l = {}) {
11
11
  const {
12
12
  gutterX: b = 0,
13
13
  gutterY: r = 0,
14
14
  header: d = 0,
15
15
  footer: i = 0,
16
16
  paddingLeft: w = 0,
17
- paddingRight: h = 0,
18
- sizes: m = {
17
+ paddingRight: m = 0,
18
+ sizes: h = {
19
19
  base: 1,
20
20
  sm: 2,
21
21
  md: 3,
@@ -23,81 +23,81 @@ function St(t, a, o, s = {}) {
23
23
  xl: 5,
24
24
  "2xl": 6
25
25
  },
26
- placement: p = "masonry"
27
- } = s;
28
- let T = 0, E = 0;
26
+ placement: g = "masonry"
27
+ } = l;
28
+ let x = 0, E = 0;
29
29
  try {
30
30
  if (a && a.nodeType === 1 && typeof window < "u" && window.getComputedStyle) {
31
- const f = window.getComputedStyle(a);
32
- T = parseFloat(f.paddingLeft) || 0, E = parseFloat(f.paddingRight) || 0;
31
+ const u = window.getComputedStyle(a);
32
+ x = parseFloat(u.paddingLeft) || 0, E = parseFloat(u.paddingRight) || 0;
33
33
  }
34
34
  } catch {
35
35
  }
36
- const $ = (w || 0) + T, A = (h || 0) + E, W = a.offsetWidth - a.clientWidth, M = W > 0 ? W + 2 : It() + 2, H = a.offsetWidth - M - $ - A, N = b * (o - 1), I = Math.floor((H - N) / o), y = t.map((f) => {
37
- const v = f.width, S = f.height;
38
- return Math.round(I * S / v) + i + d;
36
+ const W = (w || 0) + x, O = (m || 0) + E, M = a.offsetWidth - a.clientWidth, T = M > 0 ? M + 2 : It() + 2, L = a.offsetWidth - T - W - O, $ = b * (o - 1), I = Math.floor((L - $) / o), p = t.map((u) => {
37
+ const k = u.width, H = u.height;
38
+ return Math.round(I * H / k) + i + d;
39
39
  });
40
- if (p === "sequential-balanced") {
41
- const f = y.length;
42
- if (f === 0) return [];
43
- const v = (e, n, l) => e + (n > 0 ? r : 0) + l;
44
- let S = Math.max(...y), L = y.reduce((e, n) => e + n, 0) + r * Math.max(0, f - 1);
40
+ if (g === "sequential-balanced") {
41
+ const u = p.length;
42
+ if (u === 0) return [];
43
+ const k = (e, n, c) => e + (n > 0 ? r : 0) + c;
44
+ let H = Math.max(...p), N = p.reduce((e, n) => e + n, 0) + r * Math.max(0, u - 1);
45
45
  const R = (e) => {
46
- let n = 1, l = 0, g = 0;
47
- for (let x = 0; x < f; x++) {
48
- const P = y[x], k = v(l, g, P);
49
- if (k <= e)
50
- l = k, g++;
51
- else if (n++, l = P, g = 1, P > e || n > o) return !1;
46
+ let n = 1, c = 0, f = 0;
47
+ for (let y = 0; y < u; y++) {
48
+ const P = p[y], S = k(c, f, P);
49
+ if (S <= e)
50
+ c = S, f++;
51
+ else if (n++, c = P, f = 1, P > e || n > o) return !1;
52
52
  }
53
53
  return n <= o;
54
54
  };
55
- for (; S < L; ) {
56
- const e = Math.floor((S + L) / 2);
57
- R(e) ? L = e : S = e + 1;
55
+ for (; H < N; ) {
56
+ const e = Math.floor((H + N) / 2);
57
+ R(e) ? N = e : H = e + 1;
58
58
  }
59
- const _ = L, O = new Array(o).fill(0);
59
+ const _ = N, A = new Array(o).fill(0);
60
60
  let z = o - 1, D = 0, j = 0;
61
- for (let e = f - 1; e >= 0; e--) {
62
- const n = y[e], l = e < z;
63
- !(v(D, j, n) <= _) || l ? (O[z] = e + 1, z--, D = n, j = 1) : (D = v(D, j, n), j++);
61
+ for (let e = u - 1; e >= 0; e--) {
62
+ const n = p[e], c = e < z;
63
+ !(k(D, j, n) <= _) || c ? (A[z] = e + 1, z--, D = n, j = 1) : (D = k(D, j, n), j++);
64
64
  }
65
- O[0] = 0;
66
- const q = [], U = new Array(o).fill(0);
65
+ A[0] = 0;
66
+ const V = [], U = new Array(o).fill(0);
67
67
  for (let e = 0; e < o; e++) {
68
- const n = O[e], l = e + 1 < o ? O[e + 1] : f, g = e * (I + b);
69
- for (let x = n; x < l; x++) {
70
- const k = { ...t[x], columnWidth: I, imageHeight: 0, columnHeight: 0, left: 0, top: 0 }, F = y[x] - (i + d);
71
- k.imageHeight = F, k.columnHeight = y[x], k.left = g, k.top = U[e], U[e] += k.columnHeight + (x + 1 < l ? r : 0), q.push(k);
68
+ const n = A[e], c = e + 1 < o ? A[e + 1] : u, f = e * (I + b);
69
+ for (let y = n; y < c; y++) {
70
+ const S = { ...t[y], columnWidth: I, imageHeight: 0, columnHeight: 0, left: 0, top: 0 }, F = p[y] - (i + d);
71
+ S.imageHeight = F, S.columnHeight = p[y], S.left = f, S.top = U[e], U[e] += S.columnHeight + (y + 1 < c ? r : 0), V.push(S);
72
72
  }
73
73
  }
74
- return q;
74
+ return V;
75
75
  }
76
- const c = new Array(o).fill(0), u = [];
77
- for (let f = 0; f < t.length; f++) {
78
- const v = t[f], S = { ...v, columnWidth: 0, imageHeight: 0, columnHeight: 0, left: 0, top: 0 }, L = c.indexOf(Math.min(...c)), R = v.width, _ = v.height;
79
- S.columnWidth = I, S.left = L * (I + b), S.imageHeight = Math.round(I * _ / R), S.columnHeight = S.imageHeight + i + d, S.top = c[L], c[L] += S.columnHeight + r, u.push(S);
76
+ const s = new Array(o).fill(0), v = [];
77
+ for (let u = 0; u < t.length; u++) {
78
+ const k = t[u], H = { ...k, columnWidth: 0, imageHeight: 0, columnHeight: 0, left: 0, top: 0 }, N = s.indexOf(Math.min(...s)), R = k.width, _ = k.height;
79
+ H.columnWidth = I, H.left = N * (I + b), H.imageHeight = Math.round(I * _ / R), H.columnHeight = H.imageHeight + i + d, H.top = s[N], s[N] += H.columnHeight + r, v.push(H);
80
80
  }
81
- return u;
81
+ return v;
82
82
  }
83
- var kt = typeof global == "object" && global && global.Object === Object && global, Ht = typeof self == "object" && self && self.Object === Object && self, ut = kt || Ht || Function("return this")(), K = ut.Symbol, ft = Object.prototype, Et = ft.hasOwnProperty, Pt = ft.toString, G = K ? K.toStringTag : void 0;
83
+ var kt = typeof global == "object" && global && global.Object === Object && global, Ht = typeof self == "object" && self && self.Object === Object && self, ft = kt || Ht || Function("return this")(), K = ft.Symbol, dt = Object.prototype, Et = dt.hasOwnProperty, Pt = dt.toString, G = K ? K.toStringTag : void 0;
84
84
  function Lt(t) {
85
85
  var a = Et.call(t, G), o = t[G];
86
86
  try {
87
87
  t[G] = void 0;
88
- var s = !0;
88
+ var l = !0;
89
89
  } catch {
90
90
  }
91
91
  var b = Pt.call(t);
92
- return s && (a ? t[G] = o : delete t[G]), b;
92
+ return l && (a ? t[G] = o : delete t[G]), b;
93
93
  }
94
94
  var $t = Object.prototype, Nt = $t.toString;
95
95
  function Wt(t) {
96
96
  return Nt.call(t);
97
97
  }
98
- var At = "[object Null]", Ot = "[object Undefined]", rt = K ? K.toStringTag : void 0;
98
+ var Ot = "[object Null]", At = "[object Undefined]", ot = K ? K.toStringTag : void 0;
99
99
  function jt(t) {
100
- return t == null ? t === void 0 ? Ot : At : rt && rt in Object(t) ? Lt(t) : Wt(t);
100
+ return t == null ? t === void 0 ? At : Ot : ot && ot in Object(t) ? Lt(t) : Wt(t);
101
101
  }
102
102
  function Bt(t) {
103
103
  return t != null && typeof t == "object";
@@ -120,12 +120,12 @@ function Z(t) {
120
120
  var a = typeof t;
121
121
  return t != null && (a == "object" || a == "function");
122
122
  }
123
- var ot = NaN, qt = /^[-+]0x[0-9a-f]+$/i, Vt = /^0b[01]+$/i, Gt = /^0o[0-7]+$/i, Xt = parseInt;
124
- function it(t) {
123
+ var it = NaN, Vt = /^[-+]0x[0-9a-f]+$/i, qt = /^0b[01]+$/i, Gt = /^0o[0-7]+$/i, Xt = parseInt;
124
+ function st(t) {
125
125
  if (typeof t == "number")
126
126
  return t;
127
127
  if (zt(t))
128
- return ot;
128
+ return it;
129
129
  if (Z(t)) {
130
130
  var a = typeof t.valueOf == "function" ? t.valueOf() : t;
131
131
  t = Z(a) ? a + "" : a;
@@ -133,65 +133,65 @@ function it(t) {
133
133
  if (typeof t != "string")
134
134
  return t === 0 ? t : +t;
135
135
  t = _t(t);
136
- var o = Vt.test(t);
137
- return o || Gt.test(t) ? Xt(t.slice(2), o ? 2 : 8) : qt.test(t) ? ot : +t;
136
+ var o = qt.test(t);
137
+ return o || Gt.test(t) ? Xt(t.slice(2), o ? 2 : 8) : Vt.test(t) ? it : +t;
138
138
  }
139
139
  var Q = function() {
140
- return ut.Date.now();
140
+ return ft.Date.now();
141
141
  }, Ut = "Expected a function", Yt = Math.max, Jt = Math.min;
142
- function st(t, a, o) {
143
- var s, b, r, d, i, w, h = 0, m = !1, p = !1, T = !0;
142
+ function lt(t, a, o) {
143
+ var l, b, r, d, i, w, m = 0, h = !1, g = !1, x = !0;
144
144
  if (typeof t != "function")
145
145
  throw new TypeError(Ut);
146
- a = it(a) || 0, Z(o) && (m = !!o.leading, p = "maxWait" in o, r = p ? Yt(it(o.maxWait) || 0, a) : r, T = "trailing" in o ? !!o.trailing : T);
147
- function E(c) {
148
- var u = s, f = b;
149
- return s = b = void 0, h = c, d = t.apply(f, u), d;
146
+ a = st(a) || 0, Z(o) && (h = !!o.leading, g = "maxWait" in o, r = g ? Yt(st(o.maxWait) || 0, a) : r, x = "trailing" in o ? !!o.trailing : x);
147
+ function E(s) {
148
+ var v = l, u = b;
149
+ return l = b = void 0, m = s, d = t.apply(u, v), d;
150
150
  }
151
- function $(c) {
152
- return h = c, i = setTimeout(M, a), m ? E(c) : d;
151
+ function W(s) {
152
+ return m = s, i = setTimeout(T, a), h ? E(s) : d;
153
153
  }
154
- function A(c) {
155
- var u = c - w, f = c - h, v = a - u;
156
- return p ? Jt(v, r - f) : v;
154
+ function O(s) {
155
+ var v = s - w, u = s - m, k = a - v;
156
+ return g ? Jt(k, r - u) : k;
157
157
  }
158
- function W(c) {
159
- var u = c - w, f = c - h;
160
- return w === void 0 || u >= a || u < 0 || p && f >= r;
158
+ function M(s) {
159
+ var v = s - w, u = s - m;
160
+ return w === void 0 || v >= a || v < 0 || g && u >= r;
161
161
  }
162
- function M() {
163
- var c = Q();
164
- if (W(c))
165
- return H(c);
166
- i = setTimeout(M, A(c));
162
+ function T() {
163
+ var s = Q();
164
+ if (M(s))
165
+ return L(s);
166
+ i = setTimeout(T, O(s));
167
167
  }
168
- function H(c) {
169
- return i = void 0, T && s ? E(c) : (s = b = void 0, d);
168
+ function L(s) {
169
+ return i = void 0, x && l ? E(s) : (l = b = void 0, d);
170
170
  }
171
- function N() {
172
- i !== void 0 && clearTimeout(i), h = 0, s = w = b = i = void 0;
171
+ function $() {
172
+ i !== void 0 && clearTimeout(i), m = 0, l = w = b = i = void 0;
173
173
  }
174
174
  function I() {
175
- return i === void 0 ? d : H(Q());
175
+ return i === void 0 ? d : L(Q());
176
176
  }
177
- function y() {
178
- var c = Q(), u = W(c);
179
- if (s = arguments, b = this, w = c, u) {
177
+ function p() {
178
+ var s = Q(), v = M(s);
179
+ if (l = arguments, b = this, w = s, v) {
180
180
  if (i === void 0)
181
- return $(w);
182
- if (p)
183
- return clearTimeout(i), i = setTimeout(M, a), E(w);
181
+ return W(w);
182
+ if (g)
183
+ return clearTimeout(i), i = setTimeout(T, a), E(w);
184
184
  }
185
- return i === void 0 && (i = setTimeout(M, a)), d;
185
+ return i === void 0 && (i = setTimeout(T, a)), d;
186
186
  }
187
- return y.cancel = N, y.flush = I, y;
187
+ return p.cancel = $, p.flush = I, p;
188
188
  }
189
- function lt(t) {
189
+ function ct(t) {
190
190
  const a = window.innerWidth, o = t.sizes;
191
191
  return a >= 1536 && o["2xl"] ? o["2xl"] : a >= 1280 && o.xl ? o.xl : a >= 1024 && o.lg ? o.lg : a >= 768 && o.md ? o.md : a >= 640 && o.sm ? o.sm : o.base;
192
192
  }
193
193
  function Kt(t) {
194
- return t.reduce((o, s) => Math.max(o, s.top + s.columnHeight), 0) + 500;
194
+ return t.reduce((o, l) => Math.max(o, l.top + l.columnHeight), 0) + 500;
195
195
  }
196
196
  function Qt(t) {
197
197
  return {
@@ -213,47 +213,47 @@ function Zt(t, a = 0) {
213
213
  }
214
214
  function tt(t, a) {
215
215
  const o = new Array(a).fill(0);
216
- for (let s = 0; s < t.length; s++) {
217
- const b = t[s], r = s % a;
216
+ for (let l = 0; l < t.length; l++) {
217
+ const b = t[l], r = l % a;
218
218
  o[r] = Math.max(o[r], b.top + b.columnHeight);
219
219
  }
220
220
  return o;
221
221
  }
222
222
  function te(t) {
223
223
  function a(r, d) {
224
- const i = parseInt(r.dataset.left || "0", 10), w = parseInt(r.dataset.top || "0", 10), h = parseInt(r.dataset.index || "0", 10), m = Math.min(h * 20, 160), p = r.style.getPropertyValue("--masonry-opacity-delay");
225
- r.style.setProperty("--masonry-opacity-delay", `${m}ms`), requestAnimationFrame(() => {
224
+ const i = parseInt(r.dataset.left || "0", 10), w = parseInt(r.dataset.top || "0", 10), m = parseInt(r.dataset.index || "0", 10), h = Math.min(m * 20, 160), g = r.style.getPropertyValue("--masonry-opacity-delay");
225
+ r.style.setProperty("--masonry-opacity-delay", `${h}ms`), requestAnimationFrame(() => {
226
226
  r.style.opacity = "1", r.style.transform = `translate3d(${i}px, ${w}px, 0) scale(1)`;
227
- const T = () => {
228
- p ? r.style.setProperty("--masonry-opacity-delay", p) : r.style.removeProperty("--masonry-opacity-delay"), r.removeEventListener("transitionend", T), d();
227
+ const x = () => {
228
+ g ? r.style.setProperty("--masonry-opacity-delay", g) : r.style.removeProperty("--masonry-opacity-delay"), r.removeEventListener("transitionend", x), d();
229
229
  };
230
- r.addEventListener("transitionend", T);
230
+ r.addEventListener("transitionend", x);
231
231
  });
232
232
  }
233
233
  function o(r) {
234
234
  const d = parseInt(r.dataset.left || "0", 10), i = parseInt(r.dataset.top || "0", 10);
235
235
  r.style.opacity = "0", r.style.transform = `translate3d(${d}px, ${i + 10}px, 0) scale(0.985)`;
236
236
  }
237
- function s(r) {
237
+ function l(r) {
238
238
  const d = parseInt(r.dataset.left || "0", 10), i = parseInt(r.dataset.top || "0", 10);
239
239
  r.style.transition = "none", r.style.opacity = "1", r.style.transform = `translate3d(${d}px, ${i}px, 0) scale(1)`, r.style.removeProperty("--masonry-opacity-delay"), r.offsetWidth, r.style.transition = "";
240
240
  }
241
241
  function b(r, d) {
242
- const i = parseInt(r.dataset.left || "0", 10), w = parseInt(r.dataset.top || "0", 10), m = getComputedStyle(r).getPropertyValue("--masonry-leave-duration") || "", p = parseFloat(m), T = Number.isFinite(p) && p > 0 ? p : 200, E = r.style.transitionDuration, $ = () => {
243
- r.removeEventListener("transitionend", A), clearTimeout(W), r.style.transitionDuration = E || "";
244
- }, A = (M) => {
245
- (!M || M.target === r) && ($(), d());
246
- }, W = setTimeout(() => {
247
- $(), d();
248
- }, T + 100);
242
+ const i = parseInt(r.dataset.left || "0", 10), w = parseInt(r.dataset.top || "0", 10), h = getComputedStyle(r).getPropertyValue("--masonry-leave-duration") || "", g = parseFloat(h), x = Number.isFinite(g) && g > 0 ? g : 200, E = r.style.transitionDuration, W = () => {
243
+ r.removeEventListener("transitionend", O), clearTimeout(M), r.style.transitionDuration = E || "";
244
+ }, O = (T) => {
245
+ (!T || T.target === r) && (W(), d());
246
+ }, M = setTimeout(() => {
247
+ W(), d();
248
+ }, x + 100);
249
249
  requestAnimationFrame(() => {
250
- r.style.transitionDuration = `${T}ms`, r.style.opacity = "0", r.style.transform = `translate3d(${i}px, ${w + 10}px, 0) scale(0.985)`, r.addEventListener("transitionend", A);
250
+ r.style.transitionDuration = `${x}ms`, r.style.opacity = "0", r.style.transform = `translate3d(${i}px, ${w + 10}px, 0) scale(0.985)`, r.addEventListener("transitionend", O);
251
251
  });
252
252
  }
253
253
  return {
254
254
  onEnter: a,
255
255
  onBeforeEnter: o,
256
- onBeforeLeave: s,
256
+ onBeforeLeave: l,
257
257
  onLeave: b
258
258
  };
259
259
  }
@@ -261,61 +261,57 @@ function ee({
261
261
  container: t,
262
262
  masonry: a,
263
263
  columns: o,
264
- containerHeight: s,
264
+ containerHeight: l,
265
265
  isLoading: b,
266
266
  maxItems: r,
267
267
  pageSize: d,
268
268
  refreshLayout: i,
269
269
  setItemsRaw: w,
270
- loadNext: h,
271
- leaveEstimateMs: m
270
+ loadNext: m,
271
+ leaveEstimateMs: h
272
272
  }) {
273
- let p = !1;
274
- async function T() {
273
+ let g = !1, x = 0;
274
+ async function E() {
275
275
  if (!t.value) return;
276
- const { scrollTop: M, clientHeight: H } = t.value, N = M + H, I = tt(a.value, o.value), c = Math.max(...I) + 300 < N - 1, u = M + H >= s.value - 1;
277
- if ((c || u) && !b.value && !p)
276
+ const { scrollTop: M, clientHeight: T } = t.value, L = M + T, $ = M > x + 1;
277
+ x = M;
278
+ const I = tt(a.value, o.value), s = Math.max(...I) + 300 < L - 1, v = M + T >= l.value - 1;
279
+ if ((s || v) && $ && !b.value && !g)
278
280
  try {
279
- a.value.length > r && await E(I), await h(), await X();
280
- } catch (f) {
281
- console.error("Error in scroll handler:", f);
281
+ a.value.length > r && await W(I), await m(), await X();
282
+ } catch (u) {
283
+ console.error("Error in scroll handler:", u);
282
284
  }
283
285
  }
284
- async function E(M) {
286
+ async function W(M) {
285
287
  if (!a.value.length || a.value.length <= d) return;
286
- const H = a.value.reduce((u, f) => {
287
- const v = f.page;
288
- return u[v] || (u[v] = []), u[v].push(f), u;
289
- }, {}), N = Object.keys(H).sort((u, f) => parseInt(u) - parseInt(f));
290
- if (N.length === 0) return;
291
- let I = 0;
292
- const y = [];
293
- for (const u of N)
294
- if (y.push(u), I += H[u].length, I >= d) break;
295
- const c = a.value.filter((u) => !y.includes(String(u.page)));
296
- c.length !== a.value.length && (p = !0, w(c), await X(), await A($()), i(c), await X(), await W(), p = !1);
297
- }
298
- function $() {
299
- return (typeof m == "number" && m > 0 ? m : 250) + 50;
288
+ const T = a.value.reduce((s, v) => {
289
+ const u = v.page;
290
+ return s[u] || (s[u] = []), s[u].push(v), s;
291
+ }, {}), L = Object.keys(T).sort((s, v) => parseInt(s) - parseInt(v));
292
+ if (L.length === 0) return;
293
+ let $ = 0;
294
+ const I = [];
295
+ for (const s of L)
296
+ if (I.push(s), $ += T[s].length, $ >= d) break;
297
+ const p = a.value.filter((s) => !I.includes(String(s.page)));
298
+ p.length !== a.value.length && (g = !0, w(p), await X(), await new Promise((s) => requestAnimationFrame(() => s())), i(p), await X(), await O(), g = !1);
300
299
  }
301
- function A(M) {
302
- return new Promise((H) => setTimeout(H, M));
303
- }
304
- async function W() {
300
+ async function O() {
305
301
  if (!t.value) return;
306
- const { scrollTop: M, clientHeight: H } = t.value, N = M + H * 0.4, I = tt(a.value, o.value), y = I.indexOf(Math.max(...I)), c = a.value.filter((v, S) => S % o.value === y);
307
- if (c.length === 0) return;
308
- let u = c[0];
309
- for (const v of c)
310
- v.top <= N && v.top >= u.top && (u = v);
311
- const f = Math.max(0, u.top - H * 0.4);
312
- Math.abs(f - M) > 4 && t.value.scrollTo({ top: f, behavior: "auto" });
302
+ const { scrollTop: M, clientHeight: T } = t.value, L = M + T * 0.4, $ = tt(a.value, o.value), I = $.indexOf(Math.max(...$)), p = a.value.filter((u, k) => k % o.value === I);
303
+ if (p.length === 0) return;
304
+ let s = p[0];
305
+ for (const u of p)
306
+ u.top <= L && u.top >= s.top && (s = u);
307
+ const v = Math.max(0, s.top - T * 0.4);
308
+ Math.abs(v - M) > 4 && t.value.scrollTo({ top: v, behavior: "auto" });
313
309
  }
314
310
  return {
315
- handleScroll: T
311
+ handleScroll: E
316
312
  };
317
313
  }
318
- const ne = ["src"], ae = ["onClick"], re = /* @__PURE__ */ dt({
314
+ const ne = ["src"], ae = ["onClick"], re = /* @__PURE__ */ gt({
319
315
  __name: "Masonry",
320
316
  props: {
321
317
  getNextPage: {
@@ -390,6 +386,11 @@ const ne = ["src"], ae = ["onClick"], re = /* @__PURE__ */ dt({
390
386
  transitionEasing: {
391
387
  type: String,
392
388
  default: "cubic-bezier(.22,.61,.36,1)"
389
+ },
390
+ // Force motion even when user has reduced-motion enabled
391
+ forceMotion: {
392
+ type: Boolean,
393
+ default: !1
393
394
  }
394
395
  },
395
396
  emits: [
@@ -402,7 +403,7 @@ const ne = ["src"], ae = ["onClick"], re = /* @__PURE__ */ dt({
402
403
  "retry:stop"
403
404
  ],
404
405
  setup(t, { expose: a, emit: o }) {
405
- const s = t, b = {
406
+ const l = t, b = {
406
407
  sizes: { base: 1, sm: 2, md: 3, lg: 4, xl: 5, "2xl": 6 },
407
408
  gutterX: 10,
408
409
  gutterY: 10,
@@ -415,241 +416,239 @@ const ne = ["src"], ae = ["onClick"], re = /* @__PURE__ */ dt({
415
416
  var e;
416
417
  return {
417
418
  ...b,
418
- ...s.layout,
419
+ ...l.layout,
419
420
  sizes: {
420
421
  ...b.sizes,
421
- ...((e = s.layout) == null ? void 0 : e.sizes) || {}
422
+ ...((e = l.layout) == null ? void 0 : e.sizes) || {}
422
423
  }
423
424
  };
424
425
  }), d = o, i = et({
425
- get: () => s.items,
426
+ get: () => l.items,
426
427
  set: (e) => d("update:items", e)
427
- }), w = B(7), h = B(null), m = B([]);
428
+ }), w = B(7), m = B(null), h = B([]);
428
429
  B(null);
429
- const p = B(!1), T = B(0), E = B({
430
+ const g = B(!1), x = B(0), E = B({
430
431
  distanceToTrigger: 0,
431
432
  isNearTrigger: !1
432
- }), $ = () => {
433
- if (!h.value) return;
434
- const { scrollTop: e, clientHeight: n } = h.value, l = e + n, g = tt(i.value, w.value), P = Math.max(...g) + 300, k = Math.max(0, P - l), F = k <= 100;
433
+ }), W = () => {
434
+ if (!m.value) return;
435
+ const { scrollTop: e, clientHeight: n } = m.value, c = e + n, f = tt(i.value, w.value), P = Math.max(...f) + 300, S = Math.max(0, P - c), F = S <= 100;
435
436
  E.value = {
436
- distanceToTrigger: Math.round(k),
437
+ distanceToTrigger: Math.round(S),
437
438
  isNearTrigger: F
438
439
  };
439
- }, { onEnter: A, onBeforeEnter: W, onBeforeLeave: M, onLeave: H } = te(), { handleScroll: N } = ee({
440
- container: h,
440
+ }, { onEnter: O, onBeforeEnter: M, onBeforeLeave: T, onLeave: L } = te(), { handleScroll: $ } = ee({
441
+ container: m,
441
442
  masonry: i,
442
443
  columns: w,
443
- containerHeight: T,
444
- isLoading: p,
445
- maxItems: s.maxItems,
446
- pageSize: s.pageSize,
447
- refreshLayout: y,
444
+ containerHeight: x,
445
+ isLoading: g,
446
+ maxItems: l.maxItems,
447
+ pageSize: l.pageSize,
448
+ refreshLayout: p,
448
449
  setItemsRaw: (e) => {
449
450
  i.value = e;
450
451
  },
451
- loadNext: S,
452
- leaveEstimateMs: s.leaveDurationMs
452
+ loadNext: H,
453
+ leaveEstimateMs: l.leaveDurationMs
453
454
  });
454
455
  a({
455
- isLoading: p,
456
- refreshLayout: y,
457
- containerHeight: T,
458
- remove: L,
456
+ isLoading: g,
457
+ refreshLayout: p,
458
+ containerHeight: x,
459
+ remove: N,
459
460
  removeMany: R,
460
- loadNext: S,
461
- loadPage: v,
461
+ loadNext: H,
462
+ loadPage: k,
462
463
  reset: D,
463
464
  init: U,
464
- paginationHistory: m
465
+ paginationHistory: h
465
466
  });
466
467
  function I(e) {
467
468
  const n = Kt(e);
468
- let l = 0;
469
- if (h.value) {
470
- const { scrollTop: g, clientHeight: x } = h.value;
471
- l = g + x + 100;
469
+ let c = 0;
470
+ if (m.value) {
471
+ const { scrollTop: f, clientHeight: y } = m.value;
472
+ c = f + y + 100;
472
473
  }
473
- T.value = Math.max(n, l);
474
+ x.value = Math.max(n, c);
474
475
  }
475
- function y(e) {
476
- if (!h.value) return;
477
- const n = St(e, h.value, w.value, r.value);
476
+ function p(e) {
477
+ if (!m.value) return;
478
+ const n = St(e, m.value, w.value, r.value);
478
479
  I(n), i.value = n;
479
480
  }
480
- function c(e, n) {
481
- return new Promise((l) => {
482
- const g = Math.max(0, e | 0), x = Date.now();
483
- n(g, g);
481
+ function s(e, n) {
482
+ return new Promise((c) => {
483
+ const f = Math.max(0, e | 0), y = Date.now();
484
+ n(f, f);
484
485
  const P = setInterval(() => {
485
- const k = Date.now() - x, F = Math.max(0, g - k);
486
- n(F, g), F <= 0 && (clearInterval(P), l());
486
+ const S = Date.now() - y, F = Math.max(0, f - S);
487
+ n(F, f), F <= 0 && (clearInterval(P), c());
487
488
  }, 100);
488
489
  });
489
490
  }
490
- async function u(e) {
491
+ async function v(e) {
491
492
  try {
492
- const n = await f(() => s.getNextPage(e));
493
- return y([...i.value, ...n.items]), n;
493
+ const n = await u(() => l.getNextPage(e));
494
+ return p([...i.value, ...n.items]), n;
494
495
  } catch (n) {
495
496
  throw console.error("Error in getContent:", n), n;
496
497
  }
497
498
  }
498
- async function f(e) {
499
+ async function u(e) {
499
500
  let n = 0;
500
- const l = s.retryMaxAttempts;
501
- let g = s.retryInitialDelayMs;
501
+ const c = l.retryMaxAttempts;
502
+ let f = l.retryInitialDelayMs;
502
503
  for (; ; )
503
504
  try {
504
- const x = await e();
505
- return n > 0 && d("retry:stop", { attempt: n, success: !0 }), x;
506
- } catch (x) {
507
- if (n++, n > l)
508
- throw d("retry:stop", { attempt: n - 1, success: !1 }), x;
509
- d("retry:start", { attempt: n, max: l, totalMs: g }), await c(g, (P, k) => {
510
- d("retry:tick", { attempt: n, remainingMs: P, totalMs: k });
511
- }), g += s.retryBackoffStepMs;
505
+ const y = await e();
506
+ return n > 0 && d("retry:stop", { attempt: n, success: !0 }), y;
507
+ } catch (y) {
508
+ if (n++, n > c)
509
+ throw d("retry:stop", { attempt: n - 1, success: !1 }), y;
510
+ d("retry:start", { attempt: n, max: c, totalMs: f }), await s(f, (P, S) => {
511
+ d("retry:tick", { attempt: n, remainingMs: P, totalMs: S });
512
+ }), f += l.retryBackoffStepMs;
512
513
  }
513
514
  }
514
- async function v(e) {
515
- if (!p.value) {
516
- p.value = !0;
515
+ async function k(e) {
516
+ if (!g.value) {
517
+ g.value = !0;
517
518
  try {
518
- const n = i.value.length, l = await u(e);
519
- return m.value.push(l.nextPage), await z(n), l;
519
+ const n = i.value.length, c = await v(e);
520
+ return h.value.push(c.nextPage), await z(n), c;
520
521
  } catch (n) {
521
522
  throw console.error("Error loading page:", n), n;
522
523
  } finally {
523
- p.value = !1;
524
+ g.value = !1;
524
525
  }
525
526
  }
526
527
  }
527
- async function S() {
528
- if (!p.value) {
529
- p.value = !0;
528
+ async function H() {
529
+ if (!g.value) {
530
+ g.value = !0;
530
531
  try {
531
- const e = i.value.length, n = m.value[m.value.length - 1], l = await u(n);
532
- return m.value.push(l.nextPage), await z(e), l;
532
+ const e = i.value.length, n = h.value[h.value.length - 1], c = await v(n);
533
+ return h.value.push(c.nextPage), await z(e), c;
533
534
  } catch (e) {
534
535
  throw console.error("Error loading next page:", e), e;
535
536
  } finally {
536
- p.value = !1;
537
+ g.value = !1;
537
538
  }
538
539
  }
539
540
  }
540
- async function L(e) {
541
- const n = i.value.filter((l) => l.id !== e.id);
542
- i.value = n, await X(), requestAnimationFrame(() => {
543
- requestAnimationFrame(() => {
544
- y(n);
545
- });
541
+ async function N(e) {
542
+ var c;
543
+ const n = i.value.filter((f) => f.id !== e.id);
544
+ i.value = n, await X(), (c = m.value) == null || c.offsetHeight, requestAnimationFrame(() => {
545
+ p(n);
546
546
  });
547
547
  }
548
548
  async function R(e) {
549
+ var f;
549
550
  if (!e || e.length === 0) return;
550
- const n = new Set(e.map((g) => g.id)), l = i.value.filter((g) => !n.has(g.id));
551
- i.value = l, await X(), requestAnimationFrame(() => {
552
- requestAnimationFrame(() => {
553
- y(l);
554
- });
551
+ const n = new Set(e.map((y) => y.id)), c = i.value.filter((y) => !n.has(y.id));
552
+ i.value = c, await X(), (f = m.value) == null || f.offsetHeight, requestAnimationFrame(() => {
553
+ p(c);
555
554
  });
556
555
  }
557
556
  function _() {
558
- w.value = lt(r.value), y(i.value);
557
+ w.value = ct(r.value), p(i.value);
559
558
  }
560
- let O = !1;
559
+ let A = !1;
561
560
  async function z(e) {
562
- if (!s.backfillEnabled || O) return;
563
- const n = (e || 0) + (s.pageSize || 0);
564
- if (!(!s.pageSize || s.pageSize <= 0 || m.value[m.value.length - 1] == null) && !(i.value.length >= n)) {
565
- O = !0;
561
+ if (!l.backfillEnabled || A) return;
562
+ const n = (e || 0) + (l.pageSize || 0);
563
+ if (!(!l.pageSize || l.pageSize <= 0 || h.value[h.value.length - 1] == null) && !(i.value.length >= n)) {
564
+ A = !0;
566
565
  try {
567
- let g = 0;
568
- for (d("backfill:start", { target: n, fetched: i.value.length, calls: g }); i.value.length < n && g < s.backfillMaxCalls && m.value[m.value.length - 1] != null; ) {
569
- await c(s.backfillDelayMs, (P, k) => {
566
+ let f = 0;
567
+ for (d("backfill:start", { target: n, fetched: i.value.length, calls: f }); i.value.length < n && f < l.backfillMaxCalls && h.value[h.value.length - 1] != null; ) {
568
+ await s(l.backfillDelayMs, (P, S) => {
570
569
  d("backfill:tick", {
571
570
  fetched: i.value.length,
572
571
  target: n,
573
- calls: g,
572
+ calls: f,
574
573
  remainingMs: P,
575
- totalMs: k
574
+ totalMs: S
576
575
  });
577
576
  });
578
- const x = m.value[m.value.length - 1];
577
+ const y = h.value[h.value.length - 1];
579
578
  try {
580
- p.value = !0;
581
- const P = await u(x);
582
- m.value.push(P.nextPage);
579
+ g.value = !0;
580
+ const P = await v(y);
581
+ h.value.push(P.nextPage);
583
582
  } finally {
584
- p.value = !1;
583
+ g.value = !1;
585
584
  }
586
- g++;
585
+ f++;
587
586
  }
588
- d("backfill:stop", { fetched: i.value.length, calls: g });
587
+ d("backfill:stop", { fetched: i.value.length, calls: f });
589
588
  } finally {
590
- O = !1;
589
+ A = !1;
591
590
  }
592
591
  }
593
592
  }
594
593
  function D() {
595
- h.value && h.value.scrollTo({
594
+ m.value && m.value.scrollTo({
596
595
  top: 0,
597
596
  behavior: "smooth"
598
- }), i.value = [], T.value = 0, m.value = [s.loadAtPage], E.value = {
597
+ }), i.value = [], x.value = 0, h.value = [l.loadAtPage], E.value = {
599
598
  distanceToTrigger: 0,
600
599
  isNearTrigger: !1
601
600
  };
602
601
  }
603
- const j = st(() => {
604
- N(), $();
605
- }, 200), q = st(_, 200);
606
- function U(e, n, l) {
607
- m.value = [n], m.value.push(l), y([...i.value, ...e]), $();
602
+ const j = lt(() => {
603
+ $(), W();
604
+ }, 200), V = lt(_, 200);
605
+ function U(e, n, c) {
606
+ h.value = [n], h.value.push(c), p([...i.value, ...e]), W();
608
607
  }
609
- return gt(async () => {
608
+ return pt(async () => {
610
609
  var e;
611
610
  try {
612
- w.value = lt(r.value);
613
- const n = s.loadAtPage;
614
- m.value = [n], s.skipInitialLoad || await v(m.value[0]), $();
611
+ w.value = ct(r.value);
612
+ const n = l.loadAtPage;
613
+ h.value = [n], l.skipInitialLoad || await k(h.value[0]), W();
615
614
  } catch (n) {
616
- console.error("Error during component initialization:", n), p.value = !1;
615
+ console.error("Error during component initialization:", n), g.value = !1;
617
616
  }
618
- (e = h.value) == null || e.addEventListener("scroll", j), window.addEventListener("resize", q);
619
- }), pt(() => {
617
+ (e = m.value) == null || e.addEventListener("scroll", j), window.addEventListener("resize", V);
618
+ }), mt(() => {
620
619
  var e;
621
- (e = h.value) == null || e.removeEventListener("scroll", j), window.removeEventListener("resize", q);
620
+ (e = m.value) == null || e.removeEventListener("scroll", j), window.removeEventListener("resize", V);
622
621
  }), (e, n) => (J(), Y("div", {
623
- class: "overflow-auto w-full flex-1 masonry-container",
622
+ class: nt(["overflow-auto w-full flex-1 masonry-container", { "force-motion": l.forceMotion }]),
624
623
  ref_key: "container",
625
- ref: h
624
+ ref: m
626
625
  }, [
627
626
  C("div", {
628
627
  class: "relative",
629
- style: mt({ height: `${T.value}px`, "--masonry-duration": `${t.transitionDurationMs}ms`, "--masonry-leave-duration": `${t.leaveDurationMs}ms`, "--masonry-ease": t.transitionEasing })
628
+ style: ht({ height: `${x.value}px`, "--masonry-duration": `${t.transitionDurationMs}ms`, "--masonry-leave-duration": `${t.leaveDurationMs}ms`, "--masonry-ease": t.transitionEasing })
630
629
  }, [
631
- ht(vt, {
630
+ yt(bt, {
632
631
  name: "masonry",
633
632
  css: !1,
634
- onEnter: V(A),
635
- onBeforeEnter: V(W),
636
- onLeave: V(H),
637
- onBeforeLeave: V(M)
633
+ onEnter: q(O),
634
+ onBeforeEnter: q(M),
635
+ onLeave: q(L),
636
+ onBeforeLeave: q(T)
638
637
  }, {
639
- default: bt(() => [
640
- (J(!0), Y(xt, null, wt(i.value, (l, g) => (J(), Y("div", nt({
641
- key: `${l.page}-${l.id}`,
638
+ default: xt(() => [
639
+ (J(!0), Y(wt, null, Tt(i.value, (c, f) => (J(), Y("div", at({
640
+ key: `${c.page}-${c.id}`,
642
641
  class: "absolute masonry-item",
643
642
  ref_for: !0
644
- }, V(Zt)(l, g)), [
645
- Tt(e.$slots, "item", nt({ ref_for: !0 }, { item: l, remove: L }), () => [
643
+ }, q(Zt)(c, f)), [
644
+ Mt(e.$slots, "item", at({ ref_for: !0 }, { item: c, remove: N }), () => [
646
645
  C("img", {
647
- src: l.src,
646
+ src: c.src,
648
647
  class: "w-full"
649
648
  }, null, 8, ne),
650
649
  C("button", {
651
650
  class: "absolute bottom-0 right-0 bg-red-500 text-white p-2 rounded cursor-pointer",
652
- onClick: (x) => L(l)
651
+ onClick: (y) => N(c)
653
652
  }, n[0] || (n[0] = [
654
653
  C("i", { class: "fas fa-trash" }, null, -1)
655
654
  ]), 8, ae)
@@ -658,28 +657,28 @@ const ne = ["src"], ae = ["onClick"], re = /* @__PURE__ */ dt({
658
657
  ]),
659
658
  _: 3
660
659
  }, 8, ["onEnter", "onBeforeEnter", "onLeave", "onBeforeLeave"]),
661
- T.value > 0 ? (J(), Y("div", {
660
+ x.value > 0 ? (J(), Y("div", {
662
661
  key: 0,
663
- class: Mt(["fixed bottom-4 right-4 bg-gray-800 text-white text-xs rounded-full px-3 py-1.5 shadow-lg z-10 transition-opacity duration-300", { "opacity-50 hover:opacity-100": !E.value.isNearTrigger, "opacity-100": E.value.isNearTrigger }])
662
+ class: nt(["fixed bottom-4 right-4 bg-gray-800 text-white text-xs rounded-full px-3 py-1.5 shadow-lg z-10 transition-opacity duration-300", { "opacity-50 hover:opacity-100": !E.value.isNearTrigger, "opacity-100": E.value.isNearTrigger }])
664
663
  }, [
665
- C("span", null, at(i.value.length) + " items", 1),
664
+ C("span", null, rt(i.value.length) + " items", 1),
666
665
  n[1] || (n[1] = C("span", { class: "mx-2" }, "|", -1)),
667
- C("span", null, at(E.value.distanceToTrigger) + "px to load", 1)
668
- ], 2)) : yt("", !0)
666
+ C("span", null, rt(E.value.distanceToTrigger) + "px to load", 1)
667
+ ], 2)) : vt("", !0)
669
668
  ], 4)
670
- ], 512));
669
+ ], 2));
671
670
  }
672
671
  }), oe = (t, a) => {
673
672
  const o = t.__vccOpts || t;
674
- for (const [s, b] of a)
675
- o[s] = b;
673
+ for (const [l, b] of a)
674
+ o[l] = b;
676
675
  return o;
677
- }, ct = /* @__PURE__ */ oe(re, [["__scopeId", "data-v-dc6ab8b8"]]), se = {
676
+ }, ut = /* @__PURE__ */ oe(re, [["__scopeId", "data-v-a75cd886"]]), se = {
678
677
  install(t) {
679
- t.component("WyxosMasonry", ct), t.component("WMasonry", ct);
678
+ t.component("WyxosMasonry", ut), t.component("WMasonry", ut);
680
679
  }
681
680
  };
682
681
  export {
683
- ct as Masonry,
682
+ ut as Masonry,
684
683
  se as default
685
684
  };
package/lib/vibe.css CHANGED
@@ -1 +1 @@
1
- .masonry-container[data-v-dc6ab8b8]{overflow-anchor:none}.masonry-item[data-v-dc6ab8b8]{will-change:transform,opacity;contain:layout paint;transition:transform var(--masonry-duration, .45s) var(--masonry-ease, cubic-bezier(.22, .61, .36, 1)),opacity var(--masonry-leave-duration, .16s) ease-out var(--masonry-opacity-delay, 0ms);backface-visibility:hidden}.masonry-move[data-v-dc6ab8b8]{transition:transform var(--masonry-duration, .45s) var(--masonry-ease, cubic-bezier(.22, .61, .36, 1))}@media (prefers-reduced-motion: reduce){.masonry-item[data-v-dc6ab8b8],.masonry-move[data-v-dc6ab8b8]{transition-duration:1ms!important}}
1
+ .masonry-container[data-v-a75cd886]{overflow-anchor:none}.masonry-item[data-v-a75cd886]{will-change:transform,opacity;contain:layout paint;transition:transform var(--masonry-duration, .45s) var(--masonry-ease, cubic-bezier(.22, .61, .36, 1)),opacity var(--masonry-leave-duration, .16s) ease-out var(--masonry-opacity-delay, 0ms);backface-visibility:hidden}.masonry-move[data-v-a75cd886]{transition:transform var(--masonry-duration, .45s) var(--masonry-ease, cubic-bezier(.22, .61, .36, 1))}@media (prefers-reduced-motion: reduce){.masonry-container:not(.force-motion) .masonry-item[data-v-a75cd886],.masonry-container:not(.force-motion) .masonry-move[data-v-a75cd886]{transition-duration:1ms!important}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wyxos/vibe",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "main": "lib/index.js",
5
5
  "module": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -9,6 +9,7 @@
9
9
  "import": "./lib/index.js",
10
10
  "types": "./lib/index.d.ts"
11
11
  },
12
+ "./vibe.css": "./lib/vibe.css",
12
13
  "./package.json": "./package.json"
13
14
  },
14
15
  "type": "module",
@@ -16,6 +17,9 @@
16
17
  "lib",
17
18
  "src"
18
19
  ],
20
+ "sideEffects": [
21
+ "./lib/vibe.css"
22
+ ],
19
23
  "scripts": {
20
24
  "dev": "vite",
21
25
  "build": "vue-tsc --noEmit && vite build && node write-cname.js",
package/src/Masonry.vue CHANGED
@@ -82,6 +82,11 @@ const props = defineProps({
82
82
  transitionEasing: {
83
83
  type: String,
84
84
  default: 'cubic-bezier(.22,.61,.36,1)'
85
+ },
86
+ // Force motion even when user has reduced-motion enabled
87
+ forceMotion: {
88
+ type: Boolean,
89
+ default: false
85
90
  }
86
91
  })
87
92
 
@@ -295,10 +300,11 @@ async function remove(item: any) {
295
300
  const next = (masonry.value as any[]).filter(i => i.id !== item.id)
296
301
  masonry.value = next
297
302
  await nextTick()
303
+ // Force a reflow so current transforms are committed
304
+ void container.value?.offsetHeight
305
+ // Start FLIP on next frame (single RAF)
298
306
  requestAnimationFrame(() => {
299
- requestAnimationFrame(() => {
300
- refreshLayout(next)
301
- })
307
+ refreshLayout(next)
302
308
  })
303
309
  }
304
310
 
@@ -308,10 +314,11 @@ async function removeMany(items: any[]) {
308
314
  const next = (masonry.value as any[]).filter(i => !ids.has(i.id))
309
315
  masonry.value = next
310
316
  await nextTick()
317
+ // Force a reflow so survivors' current transforms are committed
318
+ void container.value?.offsetHeight
319
+ // Start FLIP on next frame (single RAF)
311
320
  requestAnimationFrame(() => {
312
- requestAnimationFrame(() => {
313
- refreshLayout(next)
314
- })
321
+ refreshLayout(next)
315
322
  })
316
323
  }
317
324
 
@@ -433,7 +440,7 @@ onUnmounted(() => {
433
440
  </script>
434
441
 
435
442
  <template>
436
- <div class="overflow-auto w-full flex-1 masonry-container" ref="container">
443
+ <div class="overflow-auto w-full flex-1 masonry-container" :class="{ 'force-motion': props.forceMotion }" ref="container">
437
444
  <div class="relative"
438
445
  :style="{height: `${containerHeight}px`, '--masonry-duration': `${transitionDurationMs}ms`, '--masonry-leave-duration': `${leaveDurationMs}ms`, '--masonry-ease': transitionEasing}">
439
446
  <transition-group name="masonry" :css="false" @enter="onEnter" @before-enter="onBeforeEnter"
@@ -482,8 +489,8 @@ onUnmounted(() => {
482
489
  }
483
490
 
484
491
  @media (prefers-reduced-motion: reduce) {
485
- .masonry-item,
486
- .masonry-move {
492
+ .masonry-container:not(.force-motion) .masonry-item,
493
+ .masonry-container:not(.force-motion) .masonry-move {
487
494
  transition-duration: 1ms !important;
488
495
  }
489
496
  }
@@ -31,6 +31,7 @@ export function useMasonryScroll({
31
31
  leaveEstimateMs?: number
32
32
  }) {
33
33
  let cleanupInProgress = false
34
+ let lastScrollTop = 0
34
35
 
35
36
  async function handleScroll() {
36
37
  if (!container.value) return
@@ -38,12 +39,16 @@ export function useMasonryScroll({
38
39
  const { scrollTop, clientHeight } = container.value
39
40
  const visibleBottom = scrollTop + clientHeight
40
41
 
42
+ // Determine scroll direction (down only)
43
+ const isScrollingDown = scrollTop > lastScrollTop + 1 // tolerate tiny jitter
44
+ lastScrollTop = scrollTop
45
+
41
46
  const columnHeights = calculateColumnHeights(masonry.value, columns.value)
42
47
  const longestColumn = Math.max(...columnHeights)
43
48
  const whitespaceVisible = longestColumn + 300 < visibleBottom - 1
44
49
  const reachedContainerBottom = scrollTop + clientHeight >= containerHeight.value - 1
45
50
 
46
- if ((whitespaceVisible || reachedContainerBottom) && !isLoading.value && !cleanupInProgress) {
51
+ if ((whitespaceVisible || reachedContainerBottom) && isScrollingDown && !isLoading.value && !cleanupInProgress) {
47
52
  try {
48
53
  if (masonry.value.length > maxItems) {
49
54
  await handleItemCleanup(columnHeights)
@@ -87,7 +92,8 @@ export function useMasonryScroll({
87
92
 
88
93
  setItemsRaw(remainingItems)
89
94
  await nextTick()
90
- await waitFor(msLeaveEstimate())
95
+ // Allow leave to start, then FLIP survivors concurrently (single RAF)
96
+ await new Promise<void>(r => requestAnimationFrame(() => r()))
91
97
 
92
98
  refreshLayout(remainingItems)
93
99
  await nextTick()