import { Component, NewState, Util, RefreshState } from "@web/state"; import { VirtualScroll } from "@web/base"; Component.register("DataTable", (container) => { const vs = VirtualScroll(); if (!container.state) container.state = NewState({}); const state = container.state; Object.assign(state, { list: [], fields: [], renderedList: [], prevHeight: 0, postHeight: 0, _listStartIndex: 0, selStartR: -1, selStartF: -1, selEndR: -1, selEndF: -1, multiSelections: [], isSelecting: false }); const refresh = () => { const scrollEl = container.querySelector(".dt-body"); if (!scrollEl) return; const res = vs.calc(scrollEl, state.list); if (res) { res.renderedList.forEach((item, i) => { if (item && !item.__watch) { const wrapped = NewState(item); res.renderedList[i] = wrapped; state.list[res.listStartIndex + i] = wrapped; } }); Object.assign(state, { prevHeight: res.prevHeight, postHeight: res.postHeight, _listStartIndex: res.listStartIndex, renderedList: res.renderedList }); } }; container.refresh = refresh; state.__watch("fields", (fields) => { if (!fields) return; const leftOffsets = [], rightOffsets = []; let lSum = 0; fields.forEach((f, i) => { if (f.pinned === "left") { leftOffsets[i] = lSum; lSum += f.width || 150; } }); fields.forEach((f, i) => { if (f.pinned === "right") { let rs = 0; for (let j = i + 1; j < fields.length; j++) { if (fields[j].pinned === "right") rs += fields[j].width || 150; } rightOffsets[i] = rs; } }); state._leftOffsets = leftOffsets; state._rightOffsets = rightOffsets; }); state.__watch("list", (list) => { state._listStartIndex = 0; const scrollEl = container.querySelector(".dt-body"); state.renderedList = vs.reset(list, scrollEl || { clientHeight: 800 }) || []; if (scrollEl) { vs.init(list, refresh); requestAnimationFrame(refresh); } }); container.onItemUpdate = (rIdx, node) => vs.update(rIdx + state._listStartIndex, node); container.getOffset = (index, side) => (state._leftOffsets || [])[index] || (state._rightOffsets || [])[index] || 0; container.isCellSelected = (r, f) => { const rMin = Math.min(state.selStartR, state.selEndR), rMax = Math.max(state.selStartR, state.selEndR); const fMin = Math.min(state.selStartF, state.selEndF), fMax = Math.max(state.selStartF, state.selEndF); if (r >= rMin && r <= rMax && f >= fMin && f <= fMax) return true; return state.multiSelections.some((s) => r >= s.r1 && r <= s.r2 && f >= s.f1 && f <= s.f2); }; container.clearAllActive = (keepSelection = false) => { state.list.forEach((row) => { if (row && row.__watch) { if (row._editingF !== null) row._editingF = null; if (row._activeF !== null) row._activeF = null; } }); if (!keepSelection) { state.selStartR = -1; state.multiSelections = []; } }; container.startSelect = (r, f, e) => { const alreadySelected = container.isCellSelected(r, f); if (state.editingCell) state.editingCell = null; if (e.shiftKey && state.selStartR !== -1) { state.selEndR = r; state.selEndF = f; } else { if (!alreadySelected) { if (!e.ctrlKey && !e.metaKey) container.clearAllActive(); else if (state.selStartR !== -1) { state.multiSelections.push({ r1: Math.min(state.selStartR, state.selEndR), r2: Math.max(state.selStartR, state.selEndR), f1: Math.min(state.selStartF, state.selEndF), f2: Math.max(state.selStartF, state.selEndF) }); } state.selStartR = state.selEndR = r; state.selStartF = state.selEndF = f; } state.isSelecting = true; if (state.list[r] && state.list[r].__watch) state.list[r]._activeF = f; } }; container.updateSelect = (r, f) => state.isSelecting && (state.selEndR = r, state.selEndF = f); container.endSelect = () => state.isSelecting = false; container.editCell = (row, f, fIdx) => { var _a; const rMin = Math.min(state.selStartR, state.selEndR), rMax = Math.max(state.selStartR, state.selEndR); const fMin = Math.min(state.selStartF, state.selEndF), fMax = Math.max(state.selStartF, state.selEndF); const rIdx = state.list.indexOf(row); const isInRange = state.selStartR !== -1 && rIdx >= rMin && rIdx <= rMax && fIdx >= fMin && fIdx <= fMax; const count = isInRange ? rMax - rMin + 1 : 0; if (count > 1 && ((_a = globalThis.UI) == null ? void 0 : _a.toast)) { UI.toast(`{#Bulk Editing {num} rows... || ${count}#}`); } container.clearAllActive(true); row._editingF = f.id; row._activeF = fIdx; if (count > 1) { const unwatch = row.__watch(f.id, (val) => { for (let r = rMin; r <= rMax; r++) { const cur = state.list[r]; if (cur !== row) { const wrapped = cur.__watch ? cur : NewState(cur); state.list[r] = wrapped; wrapped[f.id] = val; } } unwatch(); }); } }; const escapeTSV = (val) => { const str = String(val ?? ""); return str.includes(" ") || str.includes("\n") || str.includes('"') ? '"' + str.replace(/"/g, '""') + '"' : str; }; const parseTSV = (text) => { const rows = []; let curRow = [], curCell = "", inQuote = false; for (let i = 0; i < text.length; i++) { const c = text[i], next = text[i + 1]; if (inQuote) { if (c === '"' && next === '"') { curCell += '"'; i++; } else if (c === '"') inQuote = false; else curCell += c; } else { if (c === '"') inQuote = true; else if (c === " ") { curRow.push(curCell); curCell = ""; } else if (c === "\n") { curRow.push(curCell); rows.push(curRow); curRow = []; curCell = ""; } else if (c !== "\r") curCell += c; } } curRow.push(curCell); rows.push(curRow); return rows; }; container.copy = async () => { const rMin = Math.min(state.selStartR, state.selEndR), rMax = Math.max(state.selStartR, state.selEndR); const fMin = Math.min(state.selStartF, state.selEndF), fMax = Math.max(state.selStartF, state.selEndF); if (rMin === -1) return; const text = state.list.slice(rMin, rMax + 1).map( (row) => state.fields.slice(fMin, fMax + 1).map((f) => escapeTSV(row[f.id])).join(" ") ).join("\n"); await navigator.clipboard.writeText(text); }; container.paste = async () => { const text = await navigator.clipboard.readText(); const rows = parseTSV(text); const rStart = Math.min(state.selStartR, state.selEndR); const fStart = Math.min(state.selStartF, state.selEndF); if (rStart === -1) return; rows.forEach((rowData, i) => { let row = state.list[rStart + i]; if (row) { if (!row.__watch) { row = NewState(row); state.list[rStart + i] = row; } rowData.forEach((val, j) => { const field = state.fields[fStart + j]; if (field) { if (typeof row[field.id] === "boolean") row[field.id] = val.toLowerCase() === "true"; else if (typeof row[field.id] === "number") row[field.id] = Number(val); else row[field.id] = val; } }); } }); }; container.addEventListener("keydown", (e) => { if (e.ctrlKey || e.metaKey) { if (e.key === "c") { e.preventDefault(); container.copy(); } if (e.key === "v") { e.preventDefault(); container.paste(); } } }); const onGlobalMouseDown = (e) => !container.contains(e.target) && container.clearAllActive(); document.addEventListener("mousedown", onGlobalMouseDown); window.addEventListener("mouseup", container.endSelect); container._onUnload = () => { document.removeEventListener("mousedown", onGlobalMouseDown); window.removeEventListener("mouseup", container.endSelect); }; }, Util.makeDom( /*html*/ `
` )); if (typeof document !== "undefined") RefreshState(document.documentElement);