// ============================================================================ // Stylescape | Infinite Scroll Manager // ============================================================================ // Automatically loads more content when user scrolls near the bottom. // Supports data-ss-infinite-scroll attributes for declarative configuration. // ============================================================================ /** * Configuration options for InfiniteScrollManager */ export interface InfiniteScrollOptions { /** Distance from bottom (in pixels) to trigger loading */ threshold?: number; /** Callback function to load more content */ loadMoreCallback: () => void; /** Scroll container (default: window) */ container?: HTMLElement | Window; /** Throttle delay in milliseconds */ throttle?: number; /** Enable debug logging */ debug?: boolean; } /** * Infinite scroll manager for lazy loading content. * Triggers a callback when user scrolls near the bottom of the container. * * @example JavaScript * ```typescript * const infiniteScroll = new InfiniteScrollManager({ * threshold: 300, * loadMoreCallback: async () => { * const items = await fetchMoreItems() * appendItems(items) * } * }) * * // Pause/resume loading * infiniteScroll.pause() * infiniteScroll.resume() * * // Cleanup * infiniteScroll.destroy() * ``` * * @example HTML with data-ss * ```html *
*
...
* *
* ``` */ export class InfiniteScrollManager { /** Distance from bottom to trigger loading */ private threshold: number; /** Callback to load more content */ private loadMoreCallback: () => void; /** Scroll container element */ private container: HTMLElement | Window; /** Whether scrolling is actively monitored */ private isActive: boolean; /** Enable debug logging */ private debug: boolean; /** Timestamp of last scroll check */ private lastCheck: number; /** Throttle interval in milliseconds */ private throttleMs: number; /** * Creates a new InfiniteScrollManager instance. * * @param options - Configuration options */ constructor({ threshold = 300, loadMoreCallback, container = window, throttle = 200, debug = false, }: InfiniteScrollOptions) { this.threshold = threshold; this.loadMoreCallback = loadMoreCallback; this.container = container; this.debug = debug; this.isActive = true; this.lastCheck = 0; this.throttleMs = throttle; this.attach(); if (this.debug) console.log("InfiniteScrollManager initialized"); } /** * Attaches the scroll event listener to the container. */ private attach(): void { this.container.addEventListener("scroll", this.handleScroll); } /** * Handles scroll events with throttling. * Triggers the load callback when threshold is reached. */ private handleScroll = (): void => { if (!this.isActive) return; const now = Date.now(); if (now - this.lastCheck < this.throttleMs) return; this.lastCheck = now; const scrollPos = this.container instanceof Window ? window.scrollY + window.innerHeight : (this.container as HTMLElement).scrollTop + (this.container as HTMLElement).clientHeight; const maxScroll = this.container instanceof Window ? document.body.offsetHeight : (this.container as HTMLElement).scrollHeight; if (scrollPos >= maxScroll - this.threshold) { if (this.debug) console.log("Reached bottom, loading more content..."); this.loadMoreCallback(); } }; /** * Pauses infinite scroll monitoring. * Call when loading to prevent duplicate requests. */ public pause(): void { this.isActive = false; if (this.debug) console.log("InfiniteScrollManager paused"); } /** * Resumes infinite scroll monitoring. * Call after loading completes. */ public resume(): void { this.isActive = true; if (this.debug) console.log("InfiniteScrollManager resumed"); } /** * Destroys the manager and removes event listeners. */ public destroy(): void { this.container.removeEventListener("scroll", this.handleScroll); if (this.debug) console.log("InfiniteScrollManager destroyed"); } } // const infiniteScroll = new InfiniteScrollManager({ // threshold: 200, // loadMoreCallback: () => { // console.log("Loading more content...") // // your load logic here // }, // debug: true // })