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, perf: { refreshTime: 0, refreshCount: 0, scrollCount: 0, totalNodes: 0 } }) /* PERFORMANCE_TELEMETRY_START - THIS BLOCK IS FOR TESTING AND SHOULD BE REMOVED IN PRODUCTION */ if (!window.__statePerformanceTelemetry) { window.__statePerformanceTelemetry = { scanCount: 0, reuseCount: 0, moveCount: 0 }; } /* PERFORMANCE_TELEMETRY_END */ container.refresh = () => { const start = performance.now() // Optimization: Expand the virtual viewport to 1.6x height to create a buffer // that prevents "white flashes" during extremely fast scrolling. const virtualContainer = { clientHeight: container.clientHeight * 1.6, scrollTop: container.scrollTop }; const res = vs.calc(virtualContainer, state.list) if (res) { /* PERFORMANCE_TELEMETRY_START */ const frameStartScan = window.__statePerformanceTelemetry.scanCount; const frameStartMove = window.__statePerformanceTelemetry.moveCount; const frameStartReuse = window.__statePerformanceTelemetry.reuseCount; /* PERFORMANCE_TELEMETRY_END */ state.perf.refreshCount++ Object.assign(state, { prevHeight: res.prevHeight, postHeight: res.postHeight, _listStartIndex: res.listStartIndex, _renderedList: res.renderedList }) const elapsed = performance.now() - start; state.perf.refreshTime += elapsed; state.perf.totalNodes += res.renderedList.length; /* PERFORMANCE_TELEMETRY_START */ const frameScans = window.__statePerformanceTelemetry.scanCount - frameStartScan; const frameMoves = window.__statePerformanceTelemetry.moveCount - frameStartMove; const frameReuses = window.__statePerformanceTelemetry.reuseCount - frameStartReuse; if (frameScans > 0 || elapsed > 2) { console.log(`[DataTable Frame] Time: ${elapsed.toFixed(2)}ms, Scans: ${frameScans}, Moves: ${frameMoves}, Reuses: ${frameReuses}, Rows: ${res.renderedList.length}`); } /* PERFORMANCE_TELEMETRY_END */ } } container.onItemUpdate = (index, node) => { // vs.update(index + (state._listStartIndex || 0), node) } state.__watch('list', list => { state._listStartIndex = 0 state._renderedList = vs.reset(list, container) || [] setTimeout(() => { if (state.list === list) vs.init(list, container.refresh) }) }) 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) }) container.onScroll = () => { state.perf.scrollCount++ container.refresh() } }, Util.makeDom(/*html*/`
`), Util.makeDom(/*html*/` `)) if (typeof document !== 'undefined') RefreshState(document.documentElement)