import { Component, NewState, Util, RefreshState } from '@web/state' import { createPerfMonitor } from './perf.js' import { createScrollManager } from './scroll.js' import { createSelectionManager } from './selection.js' Component.register('DataTable', container => { if (!container.state) container.state = NewState({}) const state = container.state Object.assign(state, { list: [], fields: [], _renderedList: [], prevHeight: 0, postHeight: 0, _listStartIndex: 0, selectedRowCount: 0 }) const perf = createPerfMonitor(); state.perf = perf.stats; const selection = createSelectionManager(container, state); const scroll = createScrollManager(container, state, (renderedCount) => { selection.applySelectionUI(); }); container.refresh = () => { const frameStart = perf.startFrame(); scroll.refresh(); perf.endFrame(frameStart, state._renderedList.length); }; container.onScroll = () => { perf.onScroll(); container.refresh(); }; container.onItemUpdate = (index, node) => scroll.updateRowHeight(index, node); state.__watch('list', list => { scroll.init(); scroll.reset(list); }) state.__watch('fields', fields => { if (!fields) return const gridTemplate = fields.map(f => `var(--w-${f.id}, ${f.width || 150}px)`).join(' ') container.style.setProperty('--dt-grid-template', gridTemplate) }) // Expose selection methods to template container.startSelect = selection.startSelect; container.updateSelect = selection.updateSelect; container.deleteSelected = selection.deleteSelected; // Copy & Paste (simplified) const escapeTSV = val => { const str = String(val ?? '') return (str.includes('\t') || str.includes('\n') || str.includes('"')) ? '"' + str.replace(/"/g, '""') + '"' : str } container.copy = async () => { const bounds = selection.getSelectionBounds(); if (!bounds) return; const text = state.list.slice(bounds.minRow, bounds.maxRow + 1).map(row => state.fields.slice(bounds.minCol, bounds.maxCol + 1).map(f => escapeTSV(row[f.id])).join('\t') ).join('\n') await navigator.clipboard.writeText(text) } container.addEventListener('keydown', e => { if (e.ctrlKey || e.metaKey) { if (e.key === 'c') { e.preventDefault(); container.copy() } } }) const onGlobalMouseUp = () => selection.endSelect() const onGlobalMouseDown = e => { if (!container.contains(e.target)) selection.clearAllActive() } window.addEventListener('mouseup', onGlobalMouseUp) document.addEventListener('mousedown', onGlobalMouseDown) container._onUnload = () => { document.removeEventListener('mousedown', onGlobalMouseDown) window.removeEventListener('mouseup', onGlobalMouseUp) } }, Util.makeDom(/*html*/`
`), Util.makeDom(/*html*/` `)) if (typeof document !== 'undefined') RefreshState(document.documentElement)