vue-typed-virtual-list
Version:
A small, efficient, easy-to-use virtual list for Vue 3
210 lines (209 loc) • 5.34 kB
JavaScript
import { reactive as w, ref as v, computed as S, nextTick as x, watch as I, isRef as N, defineComponent as C, createVNode as h, toRefs as O } from "vue";
import { useResizeObserver as D, useElementSize as E, watchOnce as q } from "@vueuse/core";
function T(t, r, s) {
var l, u, n, i, a;
r == null && (r = 100);
function p() {
var c = Date.now() - i;
c < r && c >= 0 ? l = setTimeout(p, r - c) : (l = null, s || (a = t.apply(n, u), n = u = null));
}
var d = function() {
n = this, u = arguments, i = Date.now();
var c = s && !l;
return l || (l = setTimeout(p, r)), c && (a = t.apply(n, u), n = u = null), a;
};
return d.clear = function() {
l && (clearTimeout(l), l = null);
}, d.flush = function() {
l && (a = t.apply(n, u), n = u = null, clearTimeout(l), l = null);
}, d;
}
T.debounce = T;
var P = T;
function R(t, r) {
let s = t.length - 1, l = 0, u = 0, n = null, i = -1;
if (t[s] < r)
return r;
for (; l <= s; ) {
if (u = l + s >> 1, n = t[u], n === r)
return u;
if (n > r)
s = u - 1;
else if (n < r)
i = u, l = u + 1;
else
return l;
}
return i;
}
const U = 1, y = (t) => N(t) ? t.value : t;
function A(t) {
const r = () => y(t.items), s = () => y(t.assumedSize), l = () => y(t.padding), u = w({}), n = (e, o) => {
u[e] = o, a();
}, i = v({ length: 0 }), a = P(() => {
const e = r(), o = {
length: e.length
};
o[0] = 0;
for (let f = 1; f < e.length; f++)
o[f] = o[f - 1] + (u[f - 1] ?? s());
i.value = o;
}, U), p = (e) => i.value[e];
a(), a.flush();
let d = 0, c = 0;
const g = S(() => {
let e = R(i.value, t.scrollPosition.value), o = e;
for (; i.value[o] < i.value[e] + t.containerSize.value; )
o++;
const f = Math.ceil(l() / 2);
return e = Math.max(0, e - f), o += f + 1, (d !== e || c !== o) && (d = e, c = o, x(() => {
var m;
return (m = t.onItemsChanged) == null ? void 0 : m.call(t, { start: e, end: o });
})), r().slice(e, o).map((m, b) => ({
index: b + e,
ref: m,
offset: i.value[b + e] ?? 0
}));
}), z = S(() => {
const e = r().length;
return e === 0 ? 0 : i.value[e - 1] + (u[e - 1] ?? s());
});
return I(z, (e, o) => {
var f;
e !== o && ((f = t.onTotalSizeChanged) == null || f.call(t, e - o));
}), {
offsets: i,
getOffset: p,
measure: n,
visibleItems: g,
totalSize: z
};
}
function M(t) {
const r = v("down");
return I([t], ([s], [l]) => {
s > l ? r.value = "down" : s < l && (r.value = "up");
}), {
scrollDirection: r
};
}
const _ = /* @__PURE__ */ C({
props: {
offset: {
required: !0,
type: Number
},
index: {
required: !0,
type: Number
}
},
emits: ["sizeUpdated"],
setup(t, {
emit: r,
slots: s
}) {
const l = v(null), u = S(() => ({
position: "absolute",
top: `${t.offset}px`,
left: 0,
right: 0
}));
return D(l, ([n]) => {
if (!n)
return;
const {
height: i
} = n.contentRect;
r("sizeUpdated", i);
}), () => {
var n;
return h("div", {
ref: l,
style: u.value,
"aria-rowindex": t.index + 1
}, [(n = s.default) == null ? void 0 : n.call(s)]);
};
}
});
function V() {
return /* @__PURE__ */ C({
props: {
items: {
required: !0,
type: Array
},
defaultSize: {
type: Number,
required: !0
},
padding: {
type: Number,
default: 10
}
},
emits: ["visibleItemsChanged"],
setup(r, {
emit: s,
slots: l,
expose: u
}) {
const n = O(r), i = v(null), a = v(0), {
height: p
} = E(i), {
scrollDirection: d
} = M(a), c = A({
assumedSize: n.defaultSize,
items: n.items,
scrollPosition: a,
containerSize: p,
padding: n.padding,
onItemsChanged: (e) => s("visibleItemsChanged", e),
onTotalSizeChanged: (e) => {
!i.value || d.value !== "up" || a.value === 0 || (i.value.scrollTop += e);
}
}), g = S(() => ({
width: "1px",
height: `${c.totalSize.value}px`
}));
return u({
scrollTo: (e) => {
const o = c.getOffset(e);
o === void 0 || !i.value || (i.value.scrollTop = o, q(c.offsets, () => {
x(() => {
i.value.scrollTop = c.getOffset(e);
});
}));
}
}), () => h("div", {
"aria-rowcount": n.items.value.length,
onScroll: () => {
var e;
a.value = ((e = i.value) == null ? void 0 : e.scrollTop) ?? 0;
},
ref: i,
style: {
height: "100%",
overflowY: "auto",
position: "relative"
}
}, [h("div", {
style: g.value
}, null), c.visibleItems.value.map((e) => h(_, {
key: e.index,
onSizeUpdated: (o) => c.measure(e.index, o),
offset: e.offset,
index: e.index
}, {
default: () => {
var o;
return [(o = l.item) == null ? void 0 : o.call(l, e)];
}
}))]);
}
});
}
export {
V as createVirtualScroller
};
//# sourceMappingURL=vue-typed-virtual-list.js.map