feature/searchbar-enhance #17

Merged
ilia merged 48 commits from feature/searchbar-enhance into main 2026-01-18 14:04:53 +00:00
Showing only changes of commit 86adec01a0 - Show all commits

View File

@@ -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
* <script lang="ts">
* const virtualizer = createVirtualizer(() => ({
* count: 1000,
* estimateSize: (i) => i % 3 === 0 ? 100 : 50,
* overscan: 5,
* getItemKey: (i) => `item-${i}`
* }));
* </script>
*
* <div use:virtualizer.container style="height: 500px; overflow: auto;">
* <div style="height: {virtualizer.totalSize}px;">
* {#each virtualizer.items as item (item.key)}
* <div
* use:virtualizer.measureElement
* data-index={item.index}
* style="position: absolute; top: {item.start}px; height: {item.size}px;"
* >
* Item {item.index}
* </div>
* {/each}
* </div>
* </div>
* ```
*/
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<typeof createVirtualizer>;