UNPKG

vue-typed-virtual-list

Version:

A small, efficient, easy-to-use virtual list for Vue 3

210 lines (209 loc) 5.34 kB
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