@virtuoso.dev/masonry
Version:
Virtualized React component for rendering masonry layouts
654 lines (653 loc) • 18.5 kB
JavaScript
import { jsxs as Yt, jsx as M, Fragment as Dt } from "react/jsx-runtime";
import { Cell as g, link as vt, pipe as w, filter as St, map as y, DerivedCell as $, combine as gt, withLatestFrom as It, changeWith as $t, Signal as Rt, scan as Nt, sub as jt, pub as Gt, Realm as Ut, RealmContext as qt, useRealm as Kt, useCellValues as Jt, useCellValue as Qt } from "@virtuoso.dev/gurx";
import { forwardRef as Xt, useMemo as Zt, useImperativeHandle as te, useEffect as Ct, useState as ee, useRef as ft, useCallback as _ } from "react";
const Et = ({ index: t }) => /* @__PURE__ */ Yt("div", { children: [
"Item ",
t
] }), Mt = g(Et), D = g(0), at = g(null), tt = g(null, () => {
vt(
w(
tt,
St((t) => t !== null),
y((t) => t.length)
),
D
);
}), X = g(0), ht = g(0), ne = g(0), Q = g(0), re = X, et = g(0), j = g(!1), oe = $(0, () => w(
gt(ht, j, et),
y(([t, e, n]) => e ? t - Math.max(n, 0) : t)
)), G = { lvl: 0 };
function Vt(t, e, n, r = G, o = G) {
return { k: t, l: r, lvl: n, r: o, v: e };
}
function a(t) {
return t === G;
}
function nt() {
return G;
}
function mt(t, e) {
if (a(t)) return G;
const { k: n, l: r, r: o } = t;
if (e === n) {
if (a(r))
return o;
if (a(o))
return r;
const [s, l] = Ht(r);
return Z(m(t, { k: s, l: Ot(r), v: l }));
}
return e < n ? Z(m(t, { l: mt(r, e) })) : Z(m(t, { r: mt(o, e) }));
}
function U(t, e, n = "k") {
if (a(t))
return [Number.NEGATIVE_INFINITY, void 0];
if (t[n] === e)
return [t.k, t.v];
if (t[n] < e) {
const r = U(t.r, e, n);
return r[0] === Number.NEGATIVE_INFINITY ? [t.k, t.v] : r;
}
return U(t.l, e, n);
}
function k(t, e, n) {
return a(t) ? Vt(e, n, 1) : e === t.k ? m(t, { k: e, v: n }) : e < t.k ? yt(m(t, { l: k(t.l, e, n) })) : yt(m(t, { r: k(t.r, e, n) }));
}
function dt(t, e, n) {
if (a(t))
return [];
const { k: r, l: o, r: s, v: l } = t;
let i = [];
return r > e && (i = i.concat(dt(o, e, n))), r >= e && r <= n && i.push({ k: r, v: l }), r <= n && (i = i.concat(dt(s, e, n))), i;
}
function Ht(t) {
return a(t.r) ? [t.k, t.v] : Ht(t.r);
}
function Ot(t) {
return a(t.r) ? t.l : Z(m(t, { r: Ot(t.r) }));
}
function m(t, e) {
return Vt(e.k ?? t.k, e.v ?? t.v, e.lvl ?? t.lvl, e.l ?? t.l, e.r ?? t.r);
}
function ut(t) {
return a(t) || t.lvl > t.r.lvl;
}
function yt(t) {
return xt(kt(t));
}
function Z(t) {
const { l: e, lvl: n, r } = t;
if (r.lvl >= n - 1 && e.lvl >= n - 1)
return t;
if (n > r.lvl + 1) {
if (ut(e))
return kt(m(t, { lvl: n - 1 }));
if (!a(e) && !a(e.r))
return m(e.r, {
l: m(e, { r: e.r.l }),
lvl: n,
r: m(t, {
l: e.r.r,
lvl: n - 1
})
});
throw new Error("Unexpected empty nodes");
}
if (ut(t))
return xt(m(t, { lvl: n - 1 }));
if (!a(r) && !a(r.l)) {
const o = r.l, s = ut(o) ? r.lvl - 1 : r.lvl;
return m(o, {
l: m(t, {
lvl: n - 1,
r: o.l
}),
lvl: o.lvl + 1,
r: xt(m(r, { l: o.r, lvl: s }))
});
}
throw new Error("Unexpected empty nodes");
}
function At(t, e, n) {
if (a(t))
return [];
const r = U(t, e)[0];
return se(dt(t, r, n));
}
function Tt(t, e) {
const n = t.length;
if (n === 0)
return [];
let { index: r, value: o } = e(t[0]);
const s = [];
for (let l = 1; l < n; l++) {
const { index: i, value: u } = e(t[l]);
s.push({ end: i - 1, start: r, value: o }), r = i, o = u;
}
return s.push({ end: Number.POSITIVE_INFINITY, start: r, value: o }), s;
}
function se(t) {
return Tt(t, ({ k: e, v: n }) => ({ index: e, value: n }));
}
function xt(t) {
const { lvl: e, r: n } = t;
return !a(n) && !a(n.r) && n.lvl === e && n.r.lvl === e ? m(n, { l: m(t, { r: n.l }), lvl: e + 1 }) : t;
}
function kt(t) {
const { l: e } = t;
return !a(e) && e.lvl === t.lvl ? m(e, { r: m(t, { l: e.r }) }) : t;
}
function le(t) {
const { endIndex: e, size: n, startIndex: r } = t;
return (o) => o.start === r && (o.end === e || o.end === Number.POSITIVE_INFINITY) && o.value === n;
}
function Pt(t, e) {
let n = a(t) ? 0 : Number.POSITIVE_INFINITY;
for (const r of e) {
const { endIndex: o, size: s, startIndex: l } = r;
if (n = Math.min(n, l), a(t)) {
t = k(t, 0, s);
continue;
}
const i = At(t, l - 1, o + 1);
if (i.some(le(r)))
continue;
let u = !1, c = !1;
for (const { end: d, start: R, value: S } of i)
u ? (o >= R || s === S) && (t = mt(t, R)) : (c = S !== s, u = !0), d > o && o >= R && S !== s && (t = k(t, o + 1, S));
c && (t = k(t, l, s));
}
return [t, n];
}
function rt(t, e, n, r = 0) {
let o = t.length - 1;
for (; r <= o; ) {
const s = Math.floor((r + o) / 2), l = t[s], i = n(l, e);
if (i === 0)
return s;
if (i === -1) {
if (o - r < 2)
return s - 1;
o = s - 1;
} else {
if (o === r)
return s;
r = s + 1;
}
}
throw new Error(`Failed binary finding record in array - ${t.join(",")}, searched for ${e}`);
}
function ie(t, e, n) {
return t[rt(t, e, n)];
}
function ce(t, e, n, r) {
const o = rt(t, e, r), s = rt(t, n, r, o);
return t.slice(o, s + 1);
}
function Wt({ index: t }, e) {
return e === t ? 0 : e < t ? -1 : 1;
}
function ue({ offset: t }, e) {
return e === t ? 0 : e < t ? -1 : 1;
}
function fe(t) {
return { index: t.index, value: t };
}
function ae(t, e, n, r = 0) {
return r > 0 && (e = Math.max(e, ie(t, r, Wt).offset)), e = Math.max(0, e), Tt(ce(t, e, n, ue), fe);
}
function he(t, [e, n]) {
let r = 0, o = 0, s = 0, l = 0;
if (n !== 0) {
l = rt(t, n - 1, Wt), s = t[l].offset;
const u = U(e, n - 1);
if (u[1] === void 0)
throw new Error("Invariant violation");
r = u[0], o = u[1], t.length && t[l].height === U(e, n)[1] && (l -= 1), t = t.slice(0, l + 1);
} else
t = [];
for (const { start: i, value: u } of At(e, n, Number.POSITIVE_INFINITY)) {
const c = (i - r) * o + s;
t.push({ height: u, index: i, offset: c }), r = i, s = c, o = u;
}
return [t, o, s, r];
}
function me(t, [e, n]) {
if (n.length > 0 && a(t) && e.length === 2) {
const r = e[0].size, o = e[1].size;
return [
n.reduce((s, l) => k(k(s, l, r), l + 1, o), nt()),
0
];
}
return Pt(t, e);
}
const zt = Rt(), Ft = Rt(), lt = g([], () => {
$t(lt, Ft, (t, e) => {
const n = t.slice();
for (const [r, o] of Object.entries(e))
n[Number.parseInt(r, 10)] = o;
for (let r = 0; r < n.length; r++)
if (n[r] === void 0)
return t;
return n;
});
}), P = g(0), pt = g(0), ot = g([], () => {
vt(
w(
P,
It(lt),
y(([t, e]) => {
const n = Array.from({ length: t }, () => [nt(), 0]);
if (e.length > 0) {
const r = Array.from({ length: t }, () => 0), o = Array.from({ length: t }, () => 0);
for (let s = 0; s < e.length; s++) {
const l = e[s], i = r.indexOf(Math.min(...r)), u = o[i];
o[i]++, r[i] += l, n[i] = Pt(n[i][0], [
{ endIndex: u, size: l, startIndex: u }
]);
}
}
return n.map(([r]) => [r, 0]);
})
),
ot
), $t(ot, zt, (t, e) => {
const n = t.map(([o]) => o), r = [];
for (let o = 0; o < e.length; o++) {
const s = n[o] ?? nt();
if (e[o].length === 0) {
r.push([s, 0]);
continue;
}
r.push(me(s, [e[o], []]));
}
return r;
});
}), _t = $([], () => w(
ot,
y((t) => t.map(([e]) => e))
)), de = $([], (t) => t.pipe(
ot,
y((e) => e.map(([, n]) => n))
)), it = $([], () => w(
_t,
It(de),
Nt((t, [e, n]) => {
const r = [];
for (let o = 0; o < e.length; o++) {
const s = t[o] ?? [[]];
r.push(he(s[0], [e[o], n[o]]));
}
return r;
}, [])
)), xe = $(
[],
() => w(
it,
y((t) => t.map(([e]) => e))
)
), ve = $(
[],
() => w(
it,
y((t) => t.map(([, e]) => e))
)
), ge = $(
[],
() => w(
it,
y((t) => t.map(([, , e]) => e))
)
), Ie = $(
[],
(t) => t.pipe(
it,
y((e) => e.map(([, , , n]) => n))
)
), Y = $([], () => w(
P,
It(D, pt, lt),
y(([t, e, n, r]) => {
if (r.length > 0) {
const l = Array.from({ length: t }, () => 0), i = Array.from({ length: t }, () => []);
for (let u = 0; u < r.length; u++) {
const c = r[u], d = l.indexOf(Math.min(...l));
i[d].push(u), l[d] += c;
}
return i;
}
const o = n === 0 ? Math.min(t, e) : n;
return Array.from({ length: t }, (l, i) => {
const u = [];
for (let c = i; c < o; c += t)
u.push(c);
return u;
});
})
)), pe = $(
[],
() => w(
gt(P, D, Ie, ge, ve, Y),
y(([t, e, n, r, o, s]) => {
const l = s.reduce((c, d) => c + d.length, 0), i = Math.ceil((e - l) / t);
return Array.from({ length: t }, (c, d) => {
const S = s[d].length + i - n[d];
return r[d] + S * o[d];
});
})
)
), st = g({ columns: [], data: [], indexesInColumns: [], totalCount: -1 }, (t) => {
vt(
w(
gt(
P,
re,
oe,
_t,
xe,
D,
pe,
tt,
Y,
pt,
et,
j
),
Nt(
(e, [
n,
r,
o,
s,
l,
i,
u,
c,
d,
R,
S,
B
]) => {
if (i === 0)
return e;
const bt = B ? S + r : 0, f = Math.min(Math.max(r - bt, 0), Math.max(...u) - o), h = f + o, b = R === 0 ? Math.min(n, i) : R;
return {
columns: Array.from(
{ length: n },
(q, I) => {
const x = e.columns[I], V = s[I] ?? nt(), C = l[I] ?? [], p = u[I] ?? 0, H = d[I] ?? [];
if (a(V) || H.length === 0) {
const A = [];
let T = 0;
for (let E = I; E < b; E += n)
A.push({
columnIndex: I,
data: c == null ? void 0 : c[E],
height: 0,
index: E,
indexInColumn: T++,
offset: 0
});
return {
index: I,
items: A,
listBottom: 0,
listTop: 0,
offsetTree: C,
totalHeight: 0,
viewportBottom: h
};
}
if (
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
x && x.offsetTree === C && e.totalCount === i && e.data === c && e.indexesInColumns === d && f >= x.listTop && h <= x.listBottom
)
return x;
const K = [], W = H.length - 1, z = 0, ct = ae(C, f, h, z);
let v = 0, L = 0, O = !1;
for (const A of ct) {
const {
value: { height: T, offset: E }
} = A;
let F = A.start;
v = E, E < f && (F += Math.floor((f - E) / T), v += (F - A.start) * T), F < z && (v += (z - F) * T, F = z);
const Bt = Math.min(A.end, W);
for (let J = F; J <= Bt && !(v >= h); J++) {
const wt = H[J], Lt = {
columnIndex: I,
data: c == null ? void 0 : c[wt],
height: T,
index: wt,
indexInColumn: J,
offset: v
};
O || (O = !0, L = v), K.push(Lt), v += T;
}
}
return {
index: I,
items: K,
listBottom: v,
listTop: L,
offsetTree: C,
totalHeight: p,
viewportBottom: h
};
}
),
data: c,
indexesInColumns: d,
totalCount: i
};
},
{
columns: [],
data: [],
indexesInColumns: [],
totalCount: -1
}
)
),
st
), jt(
w(
st,
St((e) => e.columns.some((n) => n.listBottom < n.viewportBottom))
),
(e) => {
const n = e.columns.slice().sort((l, i) => l.listBottom - i.listBottom)[0], r = t.getValue(Y), o = r.flat(), s = o.length ? Math.max(...o) + 1 : 0;
s >= t.getValue(D) || Gt(
Y,
r.map((l, i) => i === n.index ? [...l, s] : l)
);
}
);
}), be = Xt(
({ columnCount: t, context: e, data: n, initialItemCount: r = 0, ItemContent: o = Et, useWindowScroll: s = !1, ...l }, i) => {
const u = Zt(() => {
const c = new Ut();
return c.register(Y), c.register(st), c.register(lt), c.pubIn({
[P]: t,
[at]: e,
[tt]: n.slice(),
[pt]: r,
[Mt]: o,
[j]: s
}), c;
}, []);
return te(i, () => ({}), []), Ct(() => {
u.pubIn({
[P]: t,
[at]: e,
[tt]: n.slice()
});
}), /* @__PURE__ */ M(qt.Provider, { value: u, children: /* @__PURE__ */ M(ye, { ...l }) });
}
);
be.displayName = "VirtuosoMasonry";
const we = {
// eslint-disable-next-line @typescript-eslint/no-empty-function
observe: () => {
},
// eslint-disable-next-line @typescript-eslint/no-empty-function
unobserve: () => {
}
}, ye = ({ style: t, ...e }) => {
const n = Kt(), [r] = ee(() => {
if (typeof ResizeObserver > "u") {
if (typeof window < "u")
throw new Error("ResizeObserver not found. Please ensure that you have a polyfill installed.");
return we;
}
return new ResizeObserver((f) => {
const h = f.length, b = n.getValue(P), N = n.getValue(j), q = Array.from({ length: b }, () => []), I = {};
let x = {};
for (let V = 0; V < h; V++) {
const C = f[V], p = C.target;
if (p === l.current) {
const O = p.ownerDocument.defaultView;
x = {
...x,
[Q]: N ? O.document.documentElement.scrollHeight : p.scrollHeight,
[X]: N ? O.scrollY : p.scrollTop,
[ht]: N ? O.innerHeight : C.contentRect.height,
[ne]: N ? O.innerWidth : p.clientWidth,
...N ? {} : { [et]: C.contentRect.top, [Q]: p.scrollHeight }
};
continue;
}
if (p.dataset.role === "column") {
l.current && (x = {
...x,
[Q]: l.current.scrollHeight
});
continue;
}
if (p.dataset.index === void 0)
continue;
const H = Number.parseInt(p.dataset.index), K = Number.parseFloat(p.dataset.knownSize ?? ""), W = C.contentRect.height, z = Number.parseInt(p.dataset.columnIndex ?? "0"), ct = Number.parseInt(p.dataset.absoluteIndex ?? "0");
if (I[ct] = W, W === K)
continue;
const v = q[z], L = v[v.length - 1];
v.length === 0 || L.size !== W || L.endIndex !== H - 1 ? v.push({ endIndex: H, size: W, startIndex: H }) : v[v.length - 1].endIndex++;
}
q.some((V) => V.length > 0) && (x = {
...x,
[zt]: q
}), Object.keys(I).length > 0 && (x = {
...x,
[Ft]: I
}), n.pubIn(x);
});
}), o = _(
(f) => {
r.observe(f);
},
[r]
), s = _(
(f) => {
r.unobserve(f);
},
[r]
), l = ft(null), i = ft([]), u = _(
(f) => (h) => {
if (h)
i.current[f] = h, r.observe(h, { box: "border-box" });
else {
const b = i.current[f];
b != null && (r.unobserve(b), i.current[f] = null);
}
},
[r]
), c = _(() => {
const f = l.current;
f !== null && n.pub(X, f.scrollTop);
}, [n]), d = _(
(f) => {
f ? (l.current = f, f.addEventListener("scroll", c), r.observe(f, { box: "border-box" })) : l.current && (l.current.removeEventListener("scroll", c), r.unobserve(l.current), l.current = null);
},
[r, c]
), [R, S, B] = Jt(st, Mt, j);
return Ct(() => {
var f;
if (B) {
const h = (f = l.current) == null ? void 0 : f.ownerDocument.defaultView, b = () => {
var N;
n.pubIn({
[et]: ((N = l.current) == null ? void 0 : N.getBoundingClientRect().top) ?? 0,
[Q]: h.document.documentElement.scrollHeight,
[X]: h.scrollY,
[ht]: h.innerHeight
});
};
return h.addEventListener("scroll", b), b(), () => {
h.removeEventListener("scroll", b);
};
}
}, [B, n]), /* @__PURE__ */ M(Dt, { children: /* @__PURE__ */ M(
"div",
{
...e,
"data-testid": "virtuoso-scroller",
ref: d,
style: {
...B ? {
boxSizing: "border-box",
display: "flex",
flexDirection: "row"
} : {
boxSizing: "border-box",
display: "flex",
flexDirection: "row",
overflowY: "scroll"
},
...t
},
children: R.columns.map((f, h) => /* @__PURE__ */ M(
"div",
{
"data-testid": "virtuoso-list",
ref: u(h),
style: {
boxSizing: "content-box",
flexGrow: 1,
height: f.totalHeight,
overflowAnchor: "none",
position: "relative"
},
children: f.items.map((b) => /* @__PURE__ */ M(Se, { item: b, ItemContent: S, mount: o, unmount: s }, b.index))
},
`column-${h}`
))
}
) });
}, Se = ({ item: t, ItemContent: e, mount: n, unmount: r }) => {
const o = Qt(at), s = ft(null), l = _(
(i) => {
i ? (s.current = i, n(i)) : s.current && (r(s.current), s.current = null);
},
[n, r]
);
return /* @__PURE__ */ M(
"div",
{
"data-absolute-index": t.index,
"data-column-index": t.columnIndex,
"data-index": t.indexInColumn,
"data-known-size": t.height,
ref: l,
style: {
boxSizing: "border-box",
overflowAnchor: "none",
position: "absolute",
top: t.offset,
width: "100%"
},
children: /* @__PURE__ */ M(e, { context: o, data: t.data, index: t.index })
}
);
};
export {
be as VirtuosoMasonry
};