diff --git a/src/shared/lib/helpers/createVirtualizer/createVirtualizer.svelte.ts b/src/shared/lib/helpers/createVirtualizer/createVirtualizer.svelte.ts index 5d0fcec..3482292 100644 --- a/src/shared/lib/helpers/createVirtualizer/createVirtualizer.svelte.ts +++ b/src/shared/lib/helpers/createVirtualizer/createVirtualizer.svelte.ts @@ -1,3 +1,84 @@ +/** + * Represents a virtualized list item with layout information. + * + * Used to render visible items with absolute positioning based on computed offsets. + */ +export interface VirtualItem { + /** Index of the item in the data array */ + index: number; + /** Offset from the top of the list in pixels */ + start: number; + /** Height/size of the item in pixels */ + size: number; + /** End position in pixels (start + size) */ + end: number; + /** Unique key for the item (for Svelte's {#each} keying) */ + key: string | number; +} + +/** + * Configuration options for {@link createVirtualizer}. + * + * Options are reactive - pass them through a function getter to enable updates. + */ +export interface VirtualizerOptions { + /** Total number of items in the data array */ + count: number; + /** + * Function to estimate the size of an item at a given index. + * Used for initial layout before actual measurements are available. + */ + estimateSize: (index: number) => number; + /** Number of extra items to render outside viewport for smoother scrolling (default: 5) */ + overscan?: number; + /** + * Function to get the key of an item at a given index. + * Defaults to using the index directly. Useful for stable keys when items reorder. + */ + getItemKey?: (index: number) => string | number; + /** + * Optional margin in pixels for scroll calculations. + * Can be useful for handling sticky headers or other UI elements. + */ + scrollMargin?: number; +} + +/** + * Creates a reactive virtualizer for efficiently rendering large lists by only rendering visible items. + * + * Uses Svelte 5 runes ($state, $derived) for reactive state management and optimizes rendering + * through scroll position tracking and item height measurement. Supports dynamic item heights + * and programmatic scrolling. + * + * @param optionsGetter - Function that returns reactive virtualizer options + * @returns Virtualizer instance with computed properties and action functions + * + * @example + * ```svelte + * + * + *
+ *
+ * {#each virtualizer.items as item (item.key)} + *
+ * Item {item.index} + *
+ * {/each} + *
+ *
+ * ``` + */ export function createVirtualizer(optionsGetter: () => VirtualizerOptions) { // Reactive State let scrollOffset = $state(0); @@ -71,6 +152,15 @@ export function createVirtualizer(optionsGetter: () => VirtualizerOptions) { }); // Svelte Actions (The DOM Interface) + + /** + * Svelte action to attach to the scrollable container element. + * + * Sets up scroll tracking, container height monitoring, and cleanup on destroy. + * + * @param node - The DOM element to attach to (should be the scrollable container) + * @returns Object with destroy method for cleanup + */ function container(node: HTMLElement) { elementRef = node; containerHeight = node.offsetHeight; @@ -95,6 +185,15 @@ export function createVirtualizer(optionsGetter: () => VirtualizerOptions) { }; } + /** + * Svelte action to measure individual item elements for dynamic height support. + * + * Attaches a ResizeObserver to track actual element height and updates + * measured sizes when dimensions change. Requires `data-index` attribute on the element. + * + * @param node - The DOM element to measure (should have `data-index` attribute) + * @returns Object with destroy method for cleanup + */ function measureElement(node: HTMLElement) { // Use a ResizeObserver on individual items for dynamic height support const resizeObserver = new ResizeObserver(([entry]) => { @@ -116,6 +215,18 @@ export function createVirtualizer(optionsGetter: () => VirtualizerOptions) { } // Programmatic Scroll + + /** + * Scrolls the container to bring the specified item into view. + * + * @param index - Index of the item to scroll to + * @param align - Scroll alignment: 'start', 'center', 'end', or 'auto' (default) + * + * @example + * ```ts + * virtualizer.scrollToIndex(50, 'center'); // Scroll to item 50 and center it + * ``` + */ function scrollToIndex(index: number, align: 'start' | 'center' | 'end' | 'auto' = 'auto') { if (!elementRef || index < 0 || index >= options.count) return; @@ -130,42 +241,27 @@ export function createVirtualizer(optionsGetter: () => VirtualizerOptions) { } return { + /** Computed array of visible items to render (reactive) */ get items() { return items; }, + /** Total height of all items in pixels (reactive) */ get totalSize() { return totalSize; }, + /** Svelte action for the scrollable container element */ container, + /** Svelte action for measuring individual item elements */ measureElement, + /** Programmatic scroll method to scroll to a specific item */ scrollToIndex, }; } -export interface VirtualItem { - /** Index of the item in the data array */ - index: number; - /** Offset from the top of the list */ - start: number; - /** Height of the item */ - size: number; - /** End position (start + size) */ - end: number; - /** Unique key for the item (for Svelte's {#each} keying) */ - key: string | number; -} - -export interface VirtualizerOptions { - /** Total number of items in the data array */ - count: number; - /** Function to estimate the size of an item at a given index */ - estimateSize: (index: number) => number; - /** Number of extra items to render outside viewport (default: 5) */ - overscan?: number; - /** Function to get the key of an item at a given index (defaults to index) */ - getItemKey?: (index: number) => string | number; - /** Optional margin in pixels for scroll calculations */ - scrollMargin?: number; -} - +/** + * Virtualizer instance returned by {@link createVirtualizer}. + * + * Provides reactive computed properties for visible items and total size, + * along with action functions for DOM integration and element measurement. + */ export type Virtualizer = ReturnType;