UNPKG

hightable

Version:

A dynamic windowed scrolling table component for react

1,355 lines (1,354 loc) 45.2 kB
import { jsx as x, jsxs as X } from "react/jsx-runtime"; import { createContext as oe, useContext as Z, useEffect as G, useCallback as y, useState as $, useMemo as T, useRef as F, forwardRef as mt, useImperativeHandle as bt } from "react"; import { flushSync as xt } from "react-dom"; function mn(e) { const t = e.then((n) => (t.resolved = n, n)).catch((n) => { throw t.rejected = n, n; }); return t; } function Ne(e) { const t = Promise.resolve(e); return t.resolved = e, t; } function je() { let e, t; const n = new Promise((r, i) => { e = r, t = i; }); return n.resolve = (r) => { n.resolved = r, e(r); }, n.reject = (r) => { n.rejected = r, t(r); }, n; } function Rt(e) { return { index: je(), cells: Object.fromEntries(e.map((t) => [t, je()])) }; } function It(e, t, n) { const r = new Array(t).fill(null).map(() => Rt(n)); return e.then((i) => { i.length !== t && console.warn(`Expected ${t} rows, got ${i.length}`), i.forEach( (o, s) => { const l = r[s]; if (l === void 0) throw new Error(`Expected row ${s} to exist`); for (const c of n) { const d = l.cells[c]; if (d === void 0) throw new Error(`Expected cell ${c} to exist in row ${s}`); o.cells[c] instanceof Promise ? o.cells[c].then((u) => { d.resolve(u); }).catch((u) => { d.reject(u); }) : d.resolve(o.cells[c]); } o.index instanceof Promise ? o.index.then((c) => { l.index.resolve(c); }).catch((c) => { l.index.reject(c); }) : l.index.resolve(o.index); } ); }).catch((i) => { r.forEach((o) => { for (const s of Object.values(o.cells)) s.reject(i); o.index.reject(i); }); }), r; } async function Et(e) { const t = e.index, n = Object.values(e.cells), r = Object.keys(e.cells), [i, ...o] = await Promise.all([t, ...n]); return { index: i, cells: Object.fromEntries(r.map((s, l) => [s, o[l]])) }; } function bn(e) { return Promise.all(e.map(Et)); } function St(e) { return e.getColumn ? e.getColumn : function({ column: n, start: r = 0, end: i = e.numRows }) { if (!e.header.includes(n)) throw new Error(`Invalid column: ${n}`); return Promise.all(e.rows({ start: r, end: i }).map((o) => o.cells[n])); }; } async function Be({ data: e, column: t }) { if (!e.header.includes(t)) throw new Error(`Invalid column: ${t}`); const r = (await St(e)({ column: t })).map((l, c) => ({ value: l, index: c })), i = Array.from(r).sort(({ value: l }, { value: c }) => l < c ? -1 : l > c ? 1 : 0), o = i.length; return i.reduce(({ lastValue: l, lastRank: c, ranks: d }, { value: u, index: g }, f) => u === l ? (d[g] = c, { ranks: d, lastValue: l, lastRank: c }) : (d[g] = f, { ranks: d, lastValue: u, lastRank: f }), { ranks: Array(o).fill(-1), lastValue: void 0, lastRank: 0 }).ranks; } function Ue(e) { if (!(0 in e)) throw new Error("orderBy should have at least one element"); const t = e[0].ranks.length; return Array.from({ length: t }, (i, o) => o).sort((i, o) => { for (const { direction: s, ranks: l } of e) { const c = l[i], d = l[o]; if (c === void 0 || d === void 0) throw new Error("Invalid ranks"); const u = s === "ascending" ? 1 : -1; if (c < d) return -u; if (c > d) return u; } return 0; }); } function Fe({ data: e }) { const { numRows: t } = e, n = Array.from({ length: t }, (r, i) => i); return Promise.resolve(n); } function xn(e) { if (e.sortable) return e; const t = /* @__PURE__ */ new Map(); return { ...e, rows({ start: n, end: r, orderBy: i }) { if (i && i.length > 0) { if (i.some(({ column: u }) => !e.header.includes(u))) throw new Error(`Invalid orderBy field: ${i.map(({ column: u }) => u).join(", ")}`); const s = [...i, { column: "", direction: "ascending" }].map(async ({ column: u, direction: g }) => { const f = t.get(u) ?? (u === "" ? Fe({ data: e }) : Be({ data: e, column: u })); t.has(u) || t.set(u, f); const h = await f; return { column: u, direction: g, ranks: h }; }), d = Promise.all(s).then(Ue).then((u) => u.slice(n, r)).then((u) => Promise.all( // TODO(SL): optimize to fetch groups of rows instead of individual rows? u.map((g) => { const f = e.rows({ start: g, end: g + 1 }); if (!(0 in f)) throw new Error("data.rows should have return one async row"); return f[0]; }) )); return It(d, r - n, e.header); } else return e.rows({ start: n, end: r }); }, sortable: !0 }; } function Rn(e) { if (!(0 in e)) return { header: [], numRows: 0, rows: () => [], getColumn: () => Promise.resolve([]) }; const t = Object.keys(e[0]); return { header: t, numRows: e.length, rows({ start: n, end: r }) { return e.slice(n, r).map((i, o) => ({ index: Ne(n + o), cells: Object.fromEntries(Object.entries(i).map(([s, l]) => [s, Ne(l)])) })); }, getColumn({ column: n, start: r = 0, end: i = e.length }) { if (!t.includes(n)) throw new Error(`Invalid column: ${n}`); return Promise.resolve(e.slice(r, i).map((o) => o[n])); } }; } function Ce() { return { ranges: [], anchor: void 0 }; } function _(e) { return Number.isInteger(e) && e >= 0; } function we(e) { return _(e.start) && _(e.end) && e.end > e.start; } function Q(e) { if (e.length === 0) return !0; if (e.some((t) => !we(t))) return !1; for (let t = 0; t < e.length - 1; t++) { const n = e[t], r = e[t + 1]; if (!n || !r || n.end >= r.start) return !1; } return !0; } function Ie({ ranges: e, index: t }) { if (!_(t)) throw new Error("Invalid index"); if (!Q(e)) throw new Error("Invalid ranges"); return e.some((n) => n.start <= t && t < n.end); } function ze({ ranges: e, length: t }) { if (!Q(e)) throw new Error("Invalid ranges"); if (t && !_(t)) throw new Error("Invalid length"); return e.length === 1 && 0 in e && e[0].start === 0 && e[0].end === t; } function Je({ ranges: e, length: t }) { if (!Q(e)) throw new Error("Invalid ranges"); if (t && !_(t)) throw new Error("Invalid length"); return ze({ ranges: e, length: t }) ? [] : [{ start: 0, end: t }]; } function Ee({ ranges: e, range: t }) { if (!Q(e)) throw new Error("Invalid ranges"); if (!we(t)) throw new Error("Invalid range"); const n = [], { start: r, end: i } = t; let o = 0; for (; o < e.length; ) { const s = e[o]; if (!s) throw new Error("Invalid range"); if (s.end >= r) break; n.push({ ...s }), o++; } for (; o < e.length; ) { const s = e[o]; if (!s) throw new Error("Invalid range"); if (s.start > i) break; t.start = Math.min(t.start, s.start), t.end = Math.max(t.end, s.end), o++; } for (n.push(t); o < e.length; ) { const s = e[o]; if (!s) throw new Error("Invalid range"); n.push({ ...s }), o++; } return n; } function Dt({ ranges: e, index: t }) { return Ee({ ranges: e, range: { start: t, end: t + 1 } }); } function qe({ ranges: e, range: t }) { if (!Q(e)) throw new Error("Invalid ranges"); if (!we(t)) throw new Error("Invalid range"); const n = [], { start: r, end: i } = t; let o = 0; for (; o < e.length; ) { const s = e[o]; if (!s) throw new Error("Invalid range"); if (s.end >= r) break; n.push({ ...s }), o++; } for (; o < e.length; ) { const s = e[o]; if (!s) throw new Error("Invalid range"); if (s.start >= i) break; s.start < r && n.push({ start: s.start, end: r }), s.end > i && n.push({ start: i, end: s.end }), o++; } for (; o < e.length; ) { const s = e[o]; if (!s) throw new Error("Invalid range"); n.push({ ...s }), o++; } return n; } function Xe({ ranges: e, anchor: t, index: n }) { if (!Q(e)) throw new Error("Invalid ranges"); if (t === void 0) return e; if (!_(t)) throw new Error("Invalid anchor"); if (!_(n)) throw new Error("Invalid index"); if (t === n) return Se({ ranges: e, index: n }); const r = t < n ? { start: t, end: n + 1 } : { start: n, end: t + 1 }; if (!we(r)) throw new Error("Invalid range"); return Ie({ ranges: e, index: t }) ? Ee({ ranges: e, range: r }) : qe({ ranges: e, range: r }); } function Se({ ranges: e, index: t }) { if (!_(t)) throw new Error("Invalid index"); const n = { start: t, end: t + 1 }; return Ie({ ranges: e, index: t }) ? qe({ ranges: e, range: n }) : Ee({ ranges: e, range: n }); } function Pt({ selection: e, index: t }) { return { ranges: Se({ ranges: e.ranges, index: t }), anchor: t }; } function kt({ selection: e, index: t }) { return { ranges: Xe({ ranges: e.ranges, anchor: e.anchor, index: t }), anchor: t }; } function Ot(e) { const t = e.length, n = Array(t).fill(-1); return e.forEach((r, i) => { if (r < 0 || r >= t) throw new Error("Invalid index: out of bounds"); if (!Number.isInteger(r)) throw new Error("Invalid index: not an integer"); if (n[r] !== -1) throw new Error("Duplicate index"); n[r] = i; }), n; } function Ke({ index: e, array: t }) { const n = t[e]; if (n === void 0) throw new Error("Data index not found in the data frame"); return n; } function Me({ selection: e, permutationIndexes: t }) { const n = t.length, { ranges: r, anchor: i } = e; if (!Q(e.ranges)) throw new Error("Invalid ranges"); if (i !== void 0 && !_(i)) throw new Error("Invalid anchor"); let o = []; if (r.length === 0) o = []; else if (r.length === 1 && 0 in r && r[0].start === 0 && r[0].end === n) o = [{ start: 0, end: n }]; else for (const l of r) { const { start: c, end: d } = l; for (let u = c; u < d; u++) o = Dt({ ranges: o, index: Ke({ index: u, array: t }) }); } const s = i !== void 0 ? Ke({ index: i, array: t }) : void 0; return { ranges: o, anchor: s }; } async function At({ tableIndex: e, selection: t, orderBy: n, data: r, ranksMap: i, setRanksMap: o }) { if (!r.sortable) throw new Error("Data frame is not sortable"); const l = [...n, { column: "", direction: "ascending" }].map(({ column: v, direction: m }) => ({ column: v, direction: m, ranks: i.get(v) ?? (v === "" ? Fe({ data: r }) : Be({ data: r, column: v })) })); l.some(({ column: v }) => !i.has(v)) && o((v) => { const m = new Map(v); return l.forEach(({ column: a, ranks: b }) => m.set(a, b)), m; }); const c = await Promise.all(l.map(async ({ column: v, direction: m, ranks: a }) => ({ column: v, direction: m, ranks: await a }))), d = Ue(c), u = Ot(d), g = Me({ selection: t, permutationIndexes: u }), { ranges: f, anchor: h } = g, S = e, w = { ranges: Xe({ ranges: f, anchor: h, index: e }), anchor: S }; return Me({ selection: w, permutationIndexes: d }); } function $t(e, t) { return e.length !== t.length ? !1 : e.every((n, r) => { const i = t[r]; return i ? n.column === i.column && n.direction === i.direction : !1; }); } function Nt(e, t) { const n = []; let r; const i = []; for (const o of e) r ? i.push(o) : o.column === t ? r = o : n.push(o); return { prefix: n, item: r, suffix: i }; } function jt(e, t) { const { prefix: n, item: r, suffix: i } = Nt(t, e); return r && n.length === 0 ? r.direction === "ascending" ? [{ column: e, direction: "descending" }, ...i] : [...i] : [{ column: e, direction: "ascending" }, ...n, ...i]; } function Ge(e) { const t = e ? `${e}px` : void 0; return { minWidth: t, maxWidth: t }; } function xe(e) { return e.offsetWidth + 1; } function Ct(e) { return e.clientWidth - 1; } function ne(e) { return typeof e == "number" && Number.isFinite(e) && !isNaN(e) && e >= 0; } function Le(e) { return (e == null ? void 0 : e.width) !== void 0 && e.measured === void 0; } function Kt(e) { let t = 0; for (const { width: n, indexes: r } of e) t += n * r.length; return t; } function Mt({ columnWidths: e, availableWidth: t, clamp: n, numColumns: r }) { var S; if (!ne(t)) return e; const i = n(0), o = e.filter((w) => (w == null ? void 0 : w.measured) !== void 0).length; if (o === 0) return e; let s = t; for (const w of e) if ((w == null ? void 0 : w.measured) === void 0) { if ((w == null ? void 0 : w.width) === void 0) { s -= i; continue; } s -= w.width; } e.length < r && (s -= (r - e.length) * i); const l = 5, c = r <= 3 ? 1 / r : 0.3, d = n(c * s - l), u = /* @__PURE__ */ new Map(); for (const [w, p] of e.entries()) if ((p == null ? void 0 : p.measured) !== void 0) { const { measured: v } = p, m = u.get(v); m ? m.push(w) : u.set(v, [w]); } const g = [...u.entries()].map( ([w, p]) => ({ width: w, indexes: p }) ).sort((w, p) => w.width - p.width); let f = 0; for (; g.length > 0 && f < 100; ) { f++; const w = Kt(g); if (w <= s) { const m = Math.floor((s - w) / o); for (const a of g) a.width = n(a.width + m); break; } const p = g.at(-1), v = g.at(-2); if (p === void 0 || p.width <= d) break; v === void 0 || v.width < d ? p.width = d : (g.pop(), v.indexes.push(...p.indexes)); } let h; for (const { width: w, indexes: p } of g) for (const v of p) { const m = { width: w, measured: (S = e[v]) == null ? void 0 : S.measured // keep the measured width if it exists (it should) }; e[v] = m, h = m, s -= w; } return h !== void 0 && s > 0 && (h.width = n(h.width + s)), e; } const re = { colIndex: 1, // the cursor cell is initially the top-left cell rowIndex: 1, // shouldFocus: !1, enterCellsNavigation: !1, onTableKeyDown: void 0, onScrollKeyDown: void 0, setColIndex: void 0, setRowIndex: void 0, setShouldFocus: void 0, setEnterCellsNavigation: void 0 }, De = oe(re); function Lt({ colCount: e, rowCount: t, rowPadding: n, children: r }) { const [i, o] = $(re.colIndex), [s, l] = $(re.rowIndex), [c, d] = $(!1), [u, g] = $(!1), f = y((p) => { const { key: v } = p, m = p.ctrlKey || p.metaKey; if (v === "ArrowRight") o(m ? e : (a) => a < e ? a + 1 : a); else if (v === "ArrowLeft") o(m ? 1 : (a) => a > 1 ? a - 1 : a); else if (v === "ArrowDown") l(m ? t : (a) => a < t ? a + 1 : a); else if (v === "ArrowUp") l(m ? 1 : (a) => a > 1 ? a - 1 : a); else if (v === "Home") m && l(1), o(1); else if (v === "End") m && l(t), o(e); else if (v === "PageDown") l((a) => a + n <= t ? a + n : t); else if (v === "PageUp") l((a) => a - n >= 1 ? a - n : 1); else if (v !== " ") return; p.stopPropagation(), p.preventDefault(), d(!0); }, [e, t, n]), h = y((p) => { const { key: v } = p; (v === "Tab" && !p.shiftKey || v === "Enter" || v === " ") && (p.stopPropagation(), p.preventDefault(), g(!0), d(!0)); }, []), S = y(() => { o(re.colIndex), l(re.rowIndex), g(!0), d(!0); }, []), w = T(() => ({ colIndex: i, rowIndex: s, onTableKeyDown: f, onScrollKeyDown: h, setColIndex: o, setRowIndex: l, shouldFocus: c, setShouldFocus: d, enterCellsNavigation: u, setEnterCellsNavigation: g, focusFirstCell: S }), [ i, s, f, h, c, u, g, S ]); return /* @__PURE__ */ x(De.Provider, { value: w, children: r }); } function ge({ ref: e, ariaColIndex: t, ariaRowIndex: n }) { const { colIndex: r, rowIndex: i, setColIndex: o, setRowIndex: s, shouldFocus: l, setShouldFocus: c } = Z(De), d = t === r && n === i, u = n === 1 || t === 1; G(() => { e.current && d && l && (u || e.current.scrollIntoView({ behavior: "auto", block: "nearest", inline: "nearest" }), e.current.focus(), c == null || c(!1)); }, [e, d, u, l, c]); const g = d ? 0 : -1, f = y(() => { o == null || o(t), s == null || s(n), c == null || c(!0); }, [o, s, c, t, n]); return { tabIndex: g, navigateToCell: f }; } function Tt() { return Z(De); } function Ht({ key: e, parse: t }) { const n = localStorage.getItem(e); return n ? (t ?? JSON.parse)(n) : void 0; } function Vt({ key: e, value: t, stringify: n }) { e !== void 0 && (t === void 0 ? localStorage.removeItem(e) : localStorage.setItem(e, (n ?? JSON.stringify)(t))); } function _t({ key: e, parse: t, stringify: n } = {}) { const [r, i] = $(void 0), [o, s] = $(void 0); return e !== o && (e !== void 0 && i(Ht({ key: e, parse: t })), s(e)), G(() => { Vt({ key: e, value: r, stringify: n }); }, [r, e, n]), [r, i]; } const Qe = oe({}); function Bt({ children: e, localStorageKey: t, numColumns: n, minWidth: r }) { if (!ne(r)) throw new Error(`Invalid minWidth: ${r}. It must be a positive number.`); if (!Number.isInteger(n) || n < 0) throw new Error(`Invalid numColumns: ${n}. It must be a positive integer.`); const [i, o] = $(0), s = y((a) => Math.floor(Math.max(a, r)), [r]), [l, c] = _t({ key: t, parse: u, stringify: d }); function d(a) { const b = a.map((I) => Le(I) ? { width: I.width } : void 0); return JSON.stringify(b); } function u(a) { const b = JSON.parse(a); return Array.isArray(b) ? b.map((I) => I == null || typeof I != "object" || !("width" in I) ? void 0 : { width: ne(I.width) ? s(I.width) : void 0 }) : []; } const g = y((a) => Number.isInteger(a) && a >= 0 && a < n, [n]), f = y((a) => { var b; return (b = l == null ? void 0 : l[a]) == null ? void 0 : b.width; }, [l]), h = y((a) => Ge(f(a)), [f]), S = y((a) => Le(l == null ? void 0 : l[a]), [l]), w = y(({ columnIndex: a, width: b }) => { !ne(b) || !g(a) || c((I) => { const A = [...I ?? []]; return A[a] = { ...A[a] ?? {}, width: s(b), measured: void 0 }, A; }); }, [s, g, c]), p = y(({ columnIndex: a, measured: b }) => { !ne(b) || !g(a) || c((I) => { const A = [...I ?? []]; return A[a] = { ...A[a] ?? {}, measured: s(b), width: void 0 }, Mt({ columnWidths: A, availableWidth: i, clamp: s, numColumns: n }); }); }, [i, s, g, n, c]), v = y(({ columnIndex: a }) => { g(a) && c((b) => { const I = [...b ?? []]; return I[a] = { ...I[a] ?? {}, measured: void 0, width: void 0 }, I; }); }, [g, c]), m = T(() => ({ getColumnWidth: f, getColumnStyle: h, isFixedColumn: S, setAvailableWidth: o, forceWidth: w, measureWidth: p, removeWidth: v }), [f, h, S, o, w, p, v]); return /* @__PURE__ */ x(Qe.Provider, { value: m, children: e }); } function Pe() { return Z(Qe); } function Ut() { return { data: { numRows: 0, header: [], rows: () => [] }, key: "default" }; } const Ye = oe(Ut()); function Te() { return crypto.randomUUID(); } function Ft({ children: e, data: t }) { const [n, r] = $(Te()), [i, o] = $(t); return t !== i && (r(Te()), o(t)), /* @__PURE__ */ x(Ye.Provider, { value: { data: t, key: n }, children: e }); } function Ze() { return Z(Ye); } function We({ value: e, onChange: t, defaultValue: n, disabled: r }) { const [i] = $(e), [o] = $(e !== void 0), [s] = $(r ?? !1), [l, c] = $(n), d = y((u) => { t == null || t(u), c(u); }, [t]); return s ? {} : o ? (e === void 0 && console.warn("The value is controlled (it has no local state) because the property was initially defined. It cannot be set to undefined now (it is set back to the initial value)."), { value: e ?? i, // read-only if onChange is undefined onChange: t }) : (e !== void 0 && console.warn("The value is uncontrolled (it only has a local state) because the property was initially undefined. It cannot be set to a value now and is ignored."), { value: l, onChange: d }); } const et = oe({}); function zt({ children: e, orderBy: t, onOrderByChange: n, disabled: r }) { const i = We({ value: t, onChange: n, defaultValue: [], disabled: r }); return /* @__PURE__ */ x(et.Provider, { value: { orderBy: i.value, onOrderByChange: i.onChange }, children: e }); } function Jt() { return Z(et); } const tt = oe({}); function qt({ children: e, selection: t, onSelectionChange: n }) { const r = We({ value: t, onChange: n, defaultValue: Ce(), disabled: t === void 0 && n === void 0 }), i = y((o, s) => { var d, u; const { key: l, shiftKey: c } = o; if (l === "Escape") (d = r.onChange) == null || d.call(r, Ce()); else if (l === "a" && (o.ctrlKey || o.metaKey)) o.preventDefault(), r.value && ((u = r.onChange) == null || u.call(r, { ranges: Je({ ranges: r.value.ranges, length: s }) })); else if (l === " " && c) { const { target: g } = o; if (!r.value || !r.onChange || !(g instanceof HTMLTableCellElement)) return; const f = Number(g.getAttribute("data-rowindex")); if (!(g.getAttribute("role") === "cell") || isNaN(f) || !Number.isInteger(f) || f < 0 || f >= s) return; o.preventDefault(), r.onChange({ ranges: Se({ ranges: r.value.ranges, index: f }), anchor: f }); } }, [r]); return /* @__PURE__ */ x(tt.Provider, { value: { selection: r.value, onSelectionChange: r.onChange, onTableKeyDown: i }, children: e }); } function Xt({ numRows: e }) { const t = Z(tt), { selection: n, onSelectionChange: r } = t, i = y(() => { if (!(!n || !r)) return () => { r({ ranges: Je({ ranges: n.ranges, length: e }), anchor: void 0 }); }; }, [r, e, n]); return { ...t, toggleAllRows: i() }; } function Re(e) { return typeof e == "string" ? e : typeof e == "number" || typeof e == "bigint" ? e.toLocaleString("en-US") : typeof e == "boolean" ? e.toString() : Array.isArray(e) ? `[ ${e.map((t) => He(Re(t), 2)).join(`, `)} ]` : e == null ? JSON.stringify(e) : e instanceof Date ? e.toISOString() : typeof e == "object" ? `{ ${Object.entries(e).filter((t) => t[1] !== void 0).map(([t, n]) => He(`${t}: ${Re(n)}`, 2)).join(`, `)} }` : JSON.stringify(e); } function He(e, t) { return e == null ? void 0 : e.split(` `).map((n) => " ".repeat(t) + n).join(` `); } function Gt(e, t) { let n = !1, r = !1; function i() { e(), r = !1, n = !0, setTimeout(() => { n = !1, r && i(); }, t); } return () => { n ? r = !0 : i(); }; } function Qt({ onDoubleClick: e, onMouseDown: t, onKeyDown: n, stringify: r, columnIndex: i, value: o, hasResolved: s, className: l, ariaColIndex: c, ariaRowIndex: d, dataRowIndex: u }) { const g = F(null), { tabIndex: f, navigateToCell: h } = ge({ ref: g, ariaColIndex: c, ariaRowIndex: d }), S = y((b) => { h(), t == null || t(b); }, [t, h]), w = y((b) => { h(), e == null || e(b); }, [e, h]), { getColumnStyle: p } = Pe(), v = p == null ? void 0 : p(i), m = T(() => r(o), [r, o]), a = T(() => { if (m !== void 0) { if (m.length > 400) return `${m.slice(0, 397)}…`; if (m.length > 100) return m; } }, [m]); return /* @__PURE__ */ x( "td", { ref: g, role: "cell", "aria-busy": !s, "aria-rowindex": d, "aria-colindex": c, "data-rowindex": u, tabIndex: f, onDoubleClick: w, onMouseDown: S, onKeyDown: n, style: v, className: l, title: a, children: m } ); } function fe({ children: e, selected: t, ariaRowIndex: n, title: r }) { return /* @__PURE__ */ x( "tr", { role: "row", "aria-rowindex": n, title: r, "aria-selected": t, children: e } ); } function be({ children: e, checked: t, onCheckboxPress: n, style: r, busy: i, ariaColIndex: o, ariaRowIndex: s, dataRowIndex: l }) { const c = F(null), { tabIndex: d, navigateToCell: u } = ge({ ref: c, ariaColIndex: o, ariaRowIndex: s }), g = y((p) => { u(), n == null || n(p.shiftKey); }, [n, u]), f = y((p) => { (p.key === "Enter" || p.key === " ") && (p.preventDefault(), p.stopPropagation(), n == null || n(p.shiftKey)); }, [n]), h = t !== void 0, S = n === void 0, w = y((p) => { p.preventDefault(); }, []); return /* @__PURE__ */ X( "th", { ref: c, scope: "row", role: "rowheader", style: r, onClick: g, onKeyDown: f, "aria-busy": i, "aria-checked": t, "aria-rowindex": s, "aria-colindex": o, "aria-disabled": S, tabIndex: d, "data-rowindex": l, children: [ /* @__PURE__ */ x("span", { children: e }), h && /* @__PURE__ */ x( "input", { type: "checkbox", onChange: w, readOnly: S, disabled: S, checked: t, role: "presentation", tabIndex: -1 } ) ] } ); } function Yt({ children: e, checked: t, onCheckboxPress: n, style: r, ariaColIndex: i, ariaRowIndex: o }, s) { const l = F(null); bt(s, () => ({ get offsetWidth() { var w; return ((w = l.current) == null ? void 0 : w.offsetWidth) ?? 0; } }), []); const { tabIndex: c, navigateToCell: d } = ge({ ref: l, ariaColIndex: i, ariaRowIndex: o }), u = y(() => { d(), n == null || n(); }, [n, d]), g = y((w) => { (w.key === "Enter" || w.key === " ") && (w.preventDefault(), w.stopPropagation(), n == null || n()); }, [n]), f = t !== void 0, h = n === void 0, S = y((w) => { w.preventDefault(); }, []); return /* @__PURE__ */ x( "td", { ref: l, style: r, onClick: u, onKeyDown: g, "aria-checked": t, "aria-rowindex": o, "aria-colindex": i, "aria-disabled": h, tabIndex: c, children: f ? /* @__PURE__ */ x( "input", { type: "checkbox", onChange: S, readOnly: h, disabled: h, checked: t, role: "presentation", tabIndex: -1 } ) : /* @__PURE__ */ x("span", { children: e }) } ); } const Zt = mt(Yt), Ve = 10, _e = 100; function Wt({ autoResize: e, forceWidth: t, width: n, tabIndex: r, navigateToCell: i }) { const [o, s] = $(void 0), [l, c] = $(!1), d = y(() => { o || e == null || e(), i == null || i(); }, [e, i, o]), u = y((a) => { a.stopPropagation(); }, []), g = y((a) => { if (i == null || i(), a.stopPropagation(), n === void 0) return; const { clientX: b, currentTarget: I, pointerId: A } = a; s({ clientX: b, pointerId: A, width: n }), "setPointerCapture" in I && I.setPointerCapture(A); }, [i, n]), f = y((a) => { !o || a.pointerId !== o.pointerId || t == null || t(o.width - o.clientX + a.clientX); }, [t, o]), h = y((a) => { a.stopPropagation(), s((b) => { if (!(b && a.pointerId === b.pointerId)) return b; }); }, []), S = y(() => { c(!0); }, []), w = y(() => { c(!1); }, []), p = y((a) => { if (l && ([" ", "ArrowRight", "ArrowLeft", "ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End"].includes(a.key) && a.preventDefault(), a.stopPropagation(), !o)) { if (a.key === "Escape") { c(!1), i == null || i(); return; } if (a.key === "Enter" || a.key === " ") { d(); return; } n !== void 0 && (a.key === "ArrowRight" || a.key === "ArrowUp" ? t == null || t(n + Ve) : a.key === "ArrowLeft" || a.key === "ArrowDown" ? t == null || t(n - Ve) : a.key === "PageUp" ? t == null || t(n + _e) : a.key === "PageDown" ? t == null || t(n - _e) : a.key === "Home" && (t == null || t(0))); } }, [d, o, t, n, l, i]), v = o !== void 0 || l, m = T(() => n === void 0 ? "No width set." : `Width set to ${n} pixels.`, [n]); return /* @__PURE__ */ x( "span", { role: "spinbutton", "aria-orientation": "vertical", "aria-busy": v, "aria-valuemin": 0, "aria-valuenow": n, "aria-valuetext": m, "aria-label": "Resize column", "aria-description": 'Press "Enter" or "Space" to autoresize the column (press again to unset the width). Press "Escape" to cancel resizing. Press "ArrowRight/ArrowUp" or "ArrowLeft/ArrowDown" to resize the column by 10 pixels. Press PageUp/PageDown to resize the column by 100 pixels.', onDoubleClick: d, onPointerDown: g, onPointerMove: f, onPointerUp: h, onClick: u, onFocus: S, onBlur: w, onKeyDown: p, tabIndex: r } ); } function en({ columnIndex: e, columnName: t, columnConfig: n, dataReady: r, direction: i, onClick: o, orderByIndex: s, orderBySize: l, ariaColIndex: c, ariaRowIndex: d, className: u, children: g }) { const f = F(null), { tabIndex: h, navigateToCell: S } = ge({ ref: f, ariaColIndex: c, ariaRowIndex: d }), w = y(() => { S(), o == null || o(); }, [o, S]), p = !!o, { getColumnStyle: v, isFixedColumn: m, getColumnWidth: a, measureWidth: b, forceWidth: I, removeWidth: A } = Pe(), Y = v == null ? void 0 : v(e), z = (m == null ? void 0 : m(e)) === !0 ? !0 : void 0, J = a == null ? void 0 : a(e), N = y((K) => { I == null || I({ columnIndex: e, width: K }); }, [I, e]); G(() => { const K = f.current; if (r && K && J === void 0) { const C = xe(K); if (isNaN(C)) return; b == null || b({ columnIndex: e, measured: C }); } }, [r, b, J, e]); const pe = y(() => { const K = f.current; if (K && b && I && A) { xt(() => { A({ columnIndex: e }); }); const C = xe(K); if (isNaN(C)) return; z && J === C ? b({ columnIndex: e, measured: C }) : I({ columnIndex: e, width: C }); } }, [b, I, A, e, z, J]), q = T(() => p ? s !== void 0 && s > 0 ? `Press to sort by ${t} in ascending order` : i === "ascending" ? `Press to sort by ${t} in descending order` : i === "descending" ? `Press to stop sorting by ${t}` : `Press to sort by ${t} in ascending order` : `The column ${t} cannot be sorted`, [p, t, i, s]), ie = y((K) => { K.target === f.current && (K.key === "Enter" || K.key === " ") && (K.preventDefault(), K.stopPropagation(), o == null || o()); }, [o]); return /* @__PURE__ */ X( "th", { ref: f, scope: "col", role: "columnheader", "aria-sort": i ?? (p ? "none" : void 0), "data-order-by-index": l !== void 0 ? s : void 0, "data-order-by-size": l, "aria-label": t, "aria-description": q, "aria-rowindex": d, "aria-colindex": c, tabIndex: h, title: q, onClick: w, onKeyDown: ie, style: Y, className: u, "data-fixed-width": z, children: [ (n == null ? void 0 : n.headerComponent) ?? g, /* @__PURE__ */ x( Wt, { forceWidth: N, autoResize: pe, width: J, tabIndex: h, navigateToCell: S } ) ] } ); } function tn({ header: e, orderBy: t, onOrderByChange: n, dataReady: r, ariaRowIndex: i, columnClassNames: o = [] }) { const s = y( (c) => { if (!(!n || !t)) return () => { n(jt(c, t)); }; }, [t, n] ), l = T(() => new Map((t ?? []).map(({ column: c, direction: d }, u) => [c, { direction: d, index: u }])), [t]); return e.map((c) => { var h, S; const { key: d, index: u, ...g } = c, f = u + 2; return ( // The ColumnHeader component width is controlled by the parent /* @__PURE__ */ x( en, { dataReady: r, direction: (h = l.get(d)) == null ? void 0 : h.direction, orderByIndex: (S = l.get(d)) == null ? void 0 : S.index, orderBySize: t == null ? void 0 : t.length, onClick: s(d), columnName: d, columnIndex: u, className: o[u], ariaColIndex: f, ariaRowIndex: i, columnConfig: g, children: d }, u ) ); }); } function nt(e) { return e === void 0 ? "" : (e + 1).toLocaleString("en-US"); } function nn(e, t) { const n = Object.keys(e.cells).length; if (n > 0 && n !== t) return `Row ${nt(e.index)} length ${n} does not match header length ${t}`; } const rn = "_hightable_1y6f4_1", on = "_table-scroll_1y6f4_16", sn = "_mock-row-label_1y6f4_140", an = "_styled_1y6f4_152", ln = "_top-border_1y6f4_174", ee = { hightable: rn, tableScroll: on, mockRowLabel: sn, styled: an, topBorder: ln }; function cn(e, t) { return T(() => { const n = new Set(e), r = e.map((i, o) => ({ key: i, index: o, ...(t == null ? void 0 : t[i]) ?? {} })); if (t) for (const i of Object.keys(t)) n.has(i) || console.warn( `[HighTable] columnConfiguration has unknown key “${i}”. It will be ignored.` ); return r; }, [e, t]); } const he = 33, un = 50, rt = 20, dn = 20, te = 2, fn = ":column:states"; function In(e) { return /* @__PURE__ */ x(Ft, { data: e.data, children: /* @__PURE__ */ x(hn, { ...e }) }); } function hn(e) { const { data: t, key: n } = Ze(), { cacheKey: r, orderBy: i, onOrderByChange: o, selection: s, onSelectionChange: l } = e, c = t.header.length + 1, d = t.numRows + 1; return ( /* important: key={key} ensures the local state is recreated if the data has changed */ /* @__PURE__ */ x(zt, { orderBy: i, onOrderByChange: o, disabled: !t.sortable, children: /* @__PURE__ */ x(qt, { selection: s, onSelectionChange: l, children: /* @__PURE__ */ x(Bt, { localStorageKey: r ? `${r}${fn}` : void 0, numColumns: t.header.length, minWidth: un, children: /* @__PURE__ */ x(Lt, { colCount: c, rowCount: d, rowPadding: e.padding ?? rt, children: /* @__PURE__ */ x(wn, { ...e }) }) }, n) }) }, n) ); } function wn({ overscan: e = dn, padding: t = rt, focus: n = !0, onDoubleClickCell: r, onMouseDownCell: i, onKeyDownCell: o, onError: s = console.error, stringify: l = Re, className: c = "", columnClassNames: d = [], styled: u = !0, columnConfiguration: g }) { const { data: f } = Ze(), [h, S] = $(void 0), [w, p] = $({ start: 0, end: 0 }), { enterCellsNavigation: v, setEnterCellsNavigation: m, onTableKeyDown: a, onScrollKeyDown: b, rowIndex: I, colIndex: A, focusFirstCell: Y } = Tt(), [z, J] = $({ rowIndex: I, colIndex: A }), [N, pe] = $(f.numRows), { setAvailableWidth: q } = Pe(), [ie, K] = $(() => /* @__PURE__ */ new Map()), { orderBy: C, onOrderByChange: ot } = Jt(), { selection: H, onSelectionChange: ve, toggleAllRows: it, onTableKeyDown: se } = Xt({ numRows: N }), ae = cn(f.header, g), st = y((R) => { a == null || a(R), se == null || se(R, N); }, [a, se, N]), ke = F(0), at = y(({ tableIndex: R, dataIndex: D }) => { if (H && ve && D !== void 0) return (k) => { E(k, H, ve, D); }; async function E(k, P, L, V) { if (!(k && P.anchor !== void 0)) { L(Pt({ selection: P, index: V })); return; } if (!C || C.length === 0) { L(kt({ selection: P, index: V })); return; } const j = ++ke.current, O = await At({ selection: P, tableIndex: R, orderBy: C, data: f, ranksMap: ie, setRanksMap: K }); j === ke.current && L(O); } }, [f, ve, C, ie, H]), lt = T(() => { if (H) return ze({ ranges: H.ranges, length: N }); }, [H, N]), ct = y((R) => { if (!(!H || R === void 0)) return Ie({ ranges: H.ranges, index: R }); }, [H]), le = (N + 1) * he, ut = h ? Math.max(0, h.offset - t) * he : 0, ye = F(null), B = F(null), me = F(0); G(() => { if (!h || !v && z.rowIndex === I && z.colIndex === A) return; m == null || m(!1), J({ rowIndex: I, colIndex: A }); const R = I - te, D = B.current; if (!D) return; let E = D.scrollTop; (R < h.offset || R >= h.offset + h.rows.length) && (E = R * he), E !== D.scrollTop && (D.scrollTop = E); }, [I, A, h, z, t, v, m]), G(() => { function R() { var U, de; const k = (U = B.current) == null ? void 0 : U.clientHeight, P = k === void 0 || k === 0 ? 100 : k, L = ((de = B.current) == null ? void 0 : de.scrollTop) ?? 0, V = Math.floor(N * L / le), M = Math.ceil(N * (L + P) / le), j = Math.max(0, V - e), O = Math.min(N, M + e); if (isNaN(j)) throw new Error(`invalid start row ${j}`); if (isNaN(O)) throw new Error(`invalid end row ${O}`); if (O - j > 1e3) throw new Error(`attempted to render too many rows ${O - j} table must be contained in a scrollable div`); p({ start: j, end: O }); } function D() { if (B.current && ye.current) { const k = Ct(B.current), P = xe(ye.current); q == null || q(k - P); } } R(), D(); const E = B.current; return E == null || E.addEventListener("scroll", R), window.addEventListener("resize", R), window.addEventListener("resize", D), () => { E == null || E.removeEventListener("scroll", R), window.removeEventListener("resize", R), window.removeEventListener("resize", D); }; }, [N, e, t, le, q]), G(() => { async function R() { const { start: D, end: E } = w, k = C ?? []; if (!(h && h.data === f && D === h.offset && E === h.offset + h.rows.length && $t(h.orderedBy, k))) { if (D === E) { S({ offset: D, rows: [], orderedBy: k, data: f }); return; } try { const P = ++me.current, L = f.rows({ start: D, end: E, orderBy: k }), V = Gt(() => { const M = []; for (const O of L) { const U = { cells: {} }; for (const [de, $e] of Object.entries(O.cells)) "resolved" in $e && (U.cells[de] = $e.resolved); "resolved" in O.index && (U.index = O.index.resolved), M.push(U); } S({ offset: D, rows: M, orderedBy: k, data: f }); }, 10); V(); for (const M of L) for (const j of [M.index, ...Object.values(M.cells)]) j.then(() => { me.current === P && V(); }).catch((O) => { me.current === P && typeof O == "object" && O !== null && "numRows" in O && typeof O.numRows == "number" && O.numRows >= 0 && O.numRows < N && Number.isInteger(O.numRows) && pe(O.numRows); }); await Promise.all(L.flatMap((M) => [M.index, ...Object.values(M.cells)])); } catch (P) { s(P); } } } R(); }, [f, s, C, h, w, N]); const dt = y((R, D) => { if (!(!r || D === void 0)) return (E) => { r(E, R, D); }; }, [r]), ft = y((R, D) => { if (!(!i || D === void 0)) return (E) => { i(E, R, D); }; }, [i]), ht = y((R, D) => { if (!(!o || D === void 0)) return (E) => { o(E, R, D); }; }, [o]); G(() => { n && (Y == null || Y()); }, [f, n, Y]); const ce = (h == null ? void 0 : h.offset) ?? 0, Oe = (h == null ? void 0 : h.rows.length) ?? 0, Ae = Array.from({ length: Math.min(t, ce) }, () => []), wt = Array.from({ length: Math.min(t, N - ce - Oe) }, () => []), ue = T(() => Math.ceil(Math.log10(N + 1)) * 4 + 22, [N]), W = T(() => Ge(ue), [ue]), gt = T(() => ({ "--column-header-height": `${he}px`, "--row-number-width": `${ue}px` }), [ue]), pt = y((R) => { R.target === B.current && (b == null || b(R)); }, [b]); if (!ae.length) return; const vt = ae.length + 1, yt = N + 1; return /* @__PURE__ */ X("div", { className: `${ee.hightable} ${u ? ee.styled : ""} ${c}`, children: [ /* @__PURE__ */ x("div", { className: ee.topBorder, role: "presentation" }), /* @__PURE__ */ x("div", { className: ee.tableScroll, ref: B, role: "group", "aria-labelledby": "caption", style: gt, onKeyDown: pt, tabIndex: 0, children: /* @__PURE__ */ x("div", { style: { height: `${le}px` }, children: /* @__PURE__ */ X( "table", { "aria-readonly": !0, "aria-colcount": vt, "aria-rowcount": yt, "aria-multiselectable": H !== void 0, role: "grid", style: { top: `${ut}px` }, onKeyDown: st, children: [ /* @__PURE__ */ x("caption", { id: "caption", hidden: !0, children: "Virtual-scroll table" }), /* @__PURE__ */ x("thead", { role: "rowgroup", children: /* @__PURE__ */ X(fe, { ariaRowIndex: 1, children: [ /* @__PURE__ */ x( Zt, { onCheckboxPress: it, checked: lt, style: W, ariaColIndex: 1, ariaRowIndex: 1, ref: ye, children: " " } ), /* @__PURE__ */ x( tn, { dataReady: h !== void 0, header: ae, orderBy: C, onOrderByChange: ot, columnClassNames: d, ariaRowIndex: 1 } ) ] }) }), /* @__PURE__ */ X("tbody", { role: "rowgroup", children: [ Ae.map((R, D) => { const E = ce - Ae.length + D, k = E + te; return /* @__PURE__ */ x(fe, { ariaRowIndex: k, children: /* @__PURE__ */ x(be, { style: W, ariaColIndex: 1, ariaRowIndex: k }) }, E); }), h == null ? void 0 : h.rows.map((R, D) => { const E = h.offset + D, k = C === void 0 || C.length === 0 ? E : void 0, P = R.index ?? k, L = ct(P), V = E + te; return /* @__PURE__ */ X( fe, { selected: L, ariaRowIndex: V, title: nn(R, ae.length), children: [ /* @__PURE__ */ x( be, { busy: P === void 0, style: W, onCheckboxPress: at({ tableIndex: E, dataIndex: P }), checked: L, showCheckBox: H !== void 0, ariaColIndex: 1, ariaRowIndex: V, dataRowIndex: P, children: nt(P) } ), f.header.map((M, j) => { const O = M in R.cells, U = R.cells[M]; return /* @__PURE__ */ x( Qt, { onDoubleClick: dt(j, P), onMouseDown: ft(j, P), onKeyDown: ht(j, P), stringify: l, value: U, columnIndex: j, hasResolved: O, className: d[j], ariaColIndex: j + te, ariaRowIndex: V, dataRowIndex: P }, j ); }) ] }, E ); }), wt.map((R, D) => { const E = ce + Oe + D, k = E + te; return /* @__PURE__ */ x(fe, { ariaRowIndex: k, children: /* @__PURE__ */ x(be, { style: W, ariaColIndex: 1, ariaRowIndex: k }) }, E); }) ] }) ] } ) }) }), /* @__PURE__ */ x("div", { className: ee.mockRowLabel, style: W, children: " " }) ] }); } function En(e) { const t = {}; let n = 0, r = 0; return { ...e, rows({ start: i, end: o, orderBy: s }) { const l = gn(s), c = t[l] ?? new Array(e.numRows); l in t || (t[l] = c); const d = n + r; d && !(d % 10) && console.log(`Cache hits: ${n} / ${n + r} (${(100 * n / (n + r)).toFixed(1)}%)`); let u, g = !1; for (let f = i; f <= o; f++) if (f < o && !c[f]) u ?? (u = f); else if (u !== void 0) { const h = f, S = e.rows({ start: u, end: h, orderBy: s }); for (let w = 0; w < h - u; w++) c[u + w] = S[w]; u = void 0, g = !0; } return g ? r++ : n++, c.slice(i, o).map((f) => { if (!f) throw new Error("Row not cached"); return f; }); } }; } function gn(e) { if (!e || e.length === 0) return ""; if (!(0 in e)) throw new Error("orderBy should have at least one element"); return JSON.stringify(e); } export { In as HighTable, Rn as arrayDataFrame, It as asyncRows, Et as awaitRow, bn as awaitRows, In as default, St as getGetColumn, je as resolvablePromise, Rt as resolvableRow, En as rowCache, xn as sortableDataFrame, Re as stringify, mn as wrapPromise, Ne as wrapResolved }; //# sourceMappingURL=HighTable.js.map