// src/list.js (function(global) { const { Component, NewState, Util, Hash } = global; const VirtualScroll = (options = {}) => { const config = { itemHeight: 50, buffer: 5, ...options } return (container) => { if (!container.state.list) container.state.list = [] if (!container.state._renderedList) container.state._renderedList = [] let _lastScrollTop = 0 let _ticking = false const update = () => { const list = container.state.list || [] const scrollTop = container.scrollTop const containerHeight = container.clientHeight const itemHeight = config.itemHeight const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - config.buffer) const endIndex = Math.min(list.length, Math.ceil((scrollTop + containerHeight) / itemHeight) + config.buffer) const rendered = [] for (let i = startIndex; i < endIndex; i++) { rendered.push({ ...list[i], _top: i * itemHeight, _index: i }) } container.state._renderedList = rendered const spacerPost = container.querySelector('.dt-spacer-post') if (spacerPost) { spacerPost.style.height = (list.length * itemHeight - (endIndex * itemHeight)) + 'px' spacerPost.style.display = 'block' } const spacerPrev = container.querySelector('.dt-spacer-prev') if (spacerPrev) { spacerPrev.style.height = (startIndex * itemHeight) + 'px' spacerPrev.style.display = 'block' } } container.addEventListener('scroll', () => { if (!_ticking) { window.requestAnimationFrame(() => { update() _ticking = false }) _ticking = true } }) container.state.__watch('list', update) window.addEventListener('resize', update) Promise.resolve().then(update) } } Component.register('FastList', container => { const itemHeights = new Map() const groupHeights = new Map() let groupItemCount = 1 container.state.renderedList = [] const avg = Util.newAvg() let containerPaddingTop = 0 let containerRowGap = 0 let topMargin = 0 const update = () => { const list = container.state.list || [] const groups = container.state.groups || [] const scrollTop = container.scrollTop const viewHeight = container.clientHeight const itemHeight = container.state.itemHeight || 40 let currentTop = 0 let startIndex = -1 let endIndex = list.length for (let i = 0; i < list.length; i++) { const h = itemHeights.get(list[i].id) || itemHeight if (startIndex === -1 && currentTop + h > scrollTop - 200) startIndex = i if (startIndex !== -1 && currentTop > scrollTop + viewHeight + 200) { endIndex = i break } currentTop += h } if (startIndex === -1) startIndex = 0 container.state.renderedList = list.slice(startIndex, endIndex).map((item, i) => ({ ...item, _index: startIndex + i })) const prevH = list.slice(0, startIndex).reduce((s, item) => s + (itemHeights.get(item.id) || itemHeight), 0) const postH = list.slice(endIndex).reduce((s, item) => s + (itemHeights.get(item.id) || itemHeight), 0) const prev = container.querySelector('.list-spacer-prev') const post = container.querySelector('.list-spacer-post') if (prev) prev.style.height = prevH + 'px' if (post) post.style.height = postH + 'px' } container.addEventListener('scroll', update) container.state.__watch('list', update) Promise.resolve().then(update) }, Util.makeDom(/*html*/`
`)) global.VirtualScroll = VirtualScroll; })(globalThis);