interface EngineConfig { itemHeight: number; viewportHeight: number; bufferSize?: number; totalItems?: number; } interface VisibleRange { start: number; end: number; } interface FetchMoreCallback { (): Promise; } interface EngineState { scrollTop: number; visibleRange: VisibleRange; loadedItems: number; isLoading: boolean; } declare class Engine { private config; private windowManager; private prefetchManager; private requestQueue; private intelligentScrollDetector; private networkDetector; private networkAwarePrefetchManager; private networkAwareRequestQueue; private adaptiveBufferCalculator; private performanceOptimizer; private memoryManager; private gpuAccelerator; private state; private fetchMoreCallback; private totalItems; constructor(config: EngineConfig); /** * Update scroll position and recalculate visible range with intelligent detection */ updateScrollPosition(scrollTop: number): Promise; /** * Get the current visible range */ getVisibleRange(): VisibleRange; /** * Check if more items should be fetched with intelligent and network-aware detection */ shouldFetchMore(): Promise; /** * Fetch more items with network awareness */ fetchMore(): Promise; /** * Set the fetchMore callback function */ setFetchMoreCallback(callback: FetchMoreCallback): void; /** * Update total items count */ updateTotalItems(count: number): void; /** * Get current engine state */ getState(): EngineState; /** * Update viewport dimensions */ updateDimensions(viewportHeight: number, itemHeight: number): void; /** * Cleanup resources */ cleanup(): void; } class LazyScroll { constructor(container, config) { this.container = container; this.config = { itemHeight: config.itemHeight || 50, viewportHeight: config.viewportHeight || 400, bufferSize: config.bufferSize || 5, fetchMore: config.fetchMore || (() => Promise.resolve([])) }; this.engine = new Engine(this.config); this.engine.setFetchMoreCallback(this.config.fetchMore); this.visibleRange = { start: 0, end: 0 }; this.isLoading = false; this.items = []; this.visibleItems = []; this.scrollHandler = this.onScroll.bind(this); this.container.addEventListener('scroll', this.scrollHandler, { passive: true }); } onScroll() { const scrollTop = this.container.scrollTop; this.engine.updateScrollPosition(scrollTop); const state = this.engine.getState(); this.visibleRange = state.visibleRange; this.isLoading = state.isLoading; this.render(); } setItems(items) { this.items = items; this.render(); } render() { // Calculate paddings const topPadding = this.visibleRange.start * this.config.itemHeight; const bottomPadding = Math.max(0, (this.items.length - this.visibleRange.end) * this.config.itemHeight); // Get visible items this.visibleItems = this.items.slice(this.visibleRange.start, this.visibleRange.end); // Clear container except for paddings and content const existingContent = this.container.querySelector('.lazy-scroll-content'); if (existingContent) { existingContent.remove(); } // Create content wrapper const contentWrapper = document.createElement('div'); contentWrapper.className = 'lazy-scroll-content'; // Add top padding const topPaddingDiv = document.createElement('div'); topPaddingDiv.style.height = `${topPadding}px`; contentWrapper.appendChild(topPaddingDiv); // Add visible items this.visibleItems.forEach((item, index) => { const itemElement = this.createItemElement(item, this.visibleRange.start + index); contentWrapper.appendChild(itemElement); }); // Add bottom padding const bottomPaddingDiv = document.createElement('div'); bottomPaddingDiv.style.height = `${bottomPadding}px`; contentWrapper.appendChild(bottomPaddingDiv); // Add loading indicator if needed if (this.isLoading) { const loadingElement = document.createElement('div'); loadingElement.className = 'lazy-loading'; loadingElement.textContent = 'Loading more items...'; contentWrapper.appendChild(loadingElement); } this.container.appendChild(contentWrapper); } createItemElement(item, index) { const itemElement = document.createElement('div'); itemElement.style.height = `${this.config.itemHeight}px`; itemElement.className = 'lazy-item'; // Default content - can be customized itemElement.textContent = `Item ${index}: ${item.text || item.id || 'Content'}`; return itemElement; } updateConfig(newConfig) { if (newConfig.itemHeight !== undefined) this.config.itemHeight = newConfig.itemHeight; if (newConfig.viewportHeight !== undefined) this.config.viewportHeight = newConfig.viewportHeight; if (newConfig.bufferSize !== undefined) this.config.bufferSize = newConfig.bufferSize; if (newConfig.fetchMore !== undefined) { this.config.fetchMore = newConfig.fetchMore; this.engine.setFetchMoreCallback(newConfig.fetchMore); } // Re-render with new config this.render(); } destroy() { this.container.removeEventListener('scroll', this.scrollHandler); this.engine.cleanup(); } // Public methods getVisibleRange() { return { ...this.visibleRange }; } refresh() { this.onScroll(); } } // Factory function for easier usage function createLazyScroll(container, config) { return new LazyScroll(container, config); } class LazyScrollElement extends HTMLElement { constructor() { super(); this.shadow = this.attachShadow({ mode: 'open' }); // Default values this.itemHeight = 50; this.viewportHeight = 400; this.bufferSize = 5; this.items = []; // Styles const style = document.createElement('style'); style.textContent = ` :host { display: block; } .lazy-scroll-container { position: relative; overflow-y: auto; } .lazy-item { width: 100%; } .lazy-loading { text-align: center; padding: 20px; color: #666; } `; this.shadow.appendChild(style); } static get observedAttributes() { return ['item-height', 'viewport-height', 'buffer-size']; } attributeChangedCallback(name, oldValue, newValue) { switch (name) { case 'item-height': this.itemHeight = parseInt(newValue) || 50; break; case 'viewport-height': this.viewportHeight = parseInt(newValue) || 400; break; case 'buffer-size': this.bufferSize = parseInt(newValue) || 5; break; } if (this.lazyScrollInstance) { this.lazyScrollInstance.updateConfig({ itemHeight: this.itemHeight, viewportHeight: this.viewportHeight, bufferSize: this.bufferSize }); } } connectedCallback() { // Create container this.container = document.createElement('div'); this.container.className = 'lazy-scroll-container'; this.container.style.height = `${this.viewportHeight}px`; this.container.style.overflowY = 'auto'; this.shadow.appendChild(this.container); // Get fetchMore function from attribute or slot const fetchMoreAttr = this.getAttribute('fetch-more'); let fetchMoreFn = () => Promise.resolve([]); if (fetchMoreAttr) { try { // If fetchMore is a function name in global scope fetchMoreFn = window[fetchMoreAttr] || (() => Promise.resolve([])); } catch (e) { console.warn('Could not find fetchMore function:', fetchMoreAttr); } } // Initialize lazy scroll this.lazyScrollInstance = createLazyScroll(this.container, { itemHeight: this.itemHeight, viewportHeight: this.viewportHeight, bufferSize: this.bufferSize, fetchMore: fetchMoreFn }); // Handle items from light DOM this.updateItemsFromSlot(); // Observe child changes this.mutationObserver = new MutationObserver(() => { this.updateItemsFromSlot(); }); this.mutationObserver.observe(this, { childList: true, subtree: true }); } disconnectedCallback() { if (this.lazyScrollInstance) { this.lazyScrollInstance.destroy(); } if (this.mutationObserver) { this.mutationObserver.disconnect(); } } updateItemsFromSlot() { // Get items from light DOM or data attribute const itemsData = this.getAttribute('items'); if (itemsData) { try { const items = JSON.parse(itemsData); if (this.lazyScrollInstance) { this.lazyScrollInstance.setItems(items); } } catch (e) { console.warn('Could not parse items data:', itemsData); } } } // Public methods setItems(items) { if (this.lazyScrollInstance) { this.lazyScrollInstance.setItems(items); } } refresh() { if (this.lazyScrollInstance) { this.lazyScrollInstance.refresh(); } } getVisibleRange() { if (this.lazyScrollInstance) { return this.lazyScrollInstance.getVisibleRange(); } return { start: 0, end: 0 }; } } // Register the custom element customElements.define('lazy-scroll-element', LazyScrollElement); export { LazyScroll, LazyScrollElement, createLazyScroll };