Skip to content

Commit 8a08585

Browse files
committed
feat: add virtualize utility
1 parent 65316af commit 8a08585

File tree

4 files changed

+511
-0
lines changed

4 files changed

+511
-0
lines changed

src/utils/virtualize.d.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
export interface VirtualizeConfig<
2+
T extends Record<string, unknown> = Record<string, unknown>,
3+
> {
4+
/** Full array of items to virtualize */
5+
items: T[];
6+
/** Height of each item in pixels */
7+
itemHeight: number;
8+
/** Visible container height in pixels */
9+
containerHeight: number;
10+
/** Current scroll position */
11+
scrollTop: number;
12+
/** Extra items to render above/below viewport (default: 3) */
13+
overscan?: number;
14+
/** Cap maximum rendered items */
15+
maxItems?: number;
16+
/** Minimum items before virtualization activates (default: 100) */
17+
threshold?: number;
18+
}
19+
20+
export interface VirtualizeResult<
21+
T extends Record<string, unknown> = Record<string, unknown>,
22+
> {
23+
/** Items to render in the current viewport */
24+
visibleItems: T[];
25+
/** Index of first visible item */
26+
startIndex: number;
27+
/** Index after last visible item */
28+
endIndex: number;
29+
/** Y offset for positioning visible items */
30+
offsetY: number;
31+
/** Total height of all items */
32+
totalHeight: number;
33+
/** Whether virtualization is active */
34+
isVirtualized: boolean;
35+
}
36+
37+
export function virtualize<
38+
T extends Record<string, unknown> = Record<string, unknown>,
39+
>(config: VirtualizeConfig<T>): VirtualizeResult<T>;

src/utils/virtualize.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Virtualizes a list to render only visible items for performance.
3+
*
4+
* @template {Record<string, unknown>} T The type of items in the array (must be a Record)
5+
* @param {Object} config
6+
* @param {T[]} config.items - Full array of items to virtualize
7+
* @param {number} config.itemHeight - Height of each item in pixels
8+
* @param {number} config.containerHeight - Visible container height in pixels
9+
* @param {number} config.scrollTop - Current scroll position
10+
* @param {number} [config.overscan=3] - Extra items to render above/below viewport
11+
* @param {number} [config.maxItems] - Cap maximum rendered items
12+
* @param {number} [config.threshold=100] - Minimum items before virtualization activates
13+
*
14+
* @returns {{
15+
* visibleItems: T[],
16+
* startIndex: number,
17+
* endIndex: number,
18+
* offsetY: number,
19+
* totalHeight: number,
20+
* isVirtualized: boolean
21+
* }}
22+
*/
23+
export function virtualize({
24+
items,
25+
itemHeight,
26+
containerHeight,
27+
scrollTop,
28+
overscan = 3,
29+
maxItems = undefined,
30+
threshold = 100,
31+
}) {
32+
// Auto-disable if below threshold
33+
if (items.length < threshold) {
34+
return {
35+
visibleItems: items,
36+
startIndex: 0,
37+
endIndex: items.length,
38+
offsetY: 0,
39+
totalHeight: items.length * itemHeight,
40+
isVirtualized: false,
41+
};
42+
}
43+
44+
const totalHeight = items.length * itemHeight;
45+
46+
// Calculate visible range
47+
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
48+
let endIndex = Math.min(
49+
items.length,
50+
Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan,
51+
);
52+
53+
// Apply maxItems cap if specified
54+
if (maxItems && endIndex - startIndex > maxItems) {
55+
endIndex = startIndex + maxItems;
56+
}
57+
58+
const offsetY = startIndex * itemHeight;
59+
60+
return {
61+
visibleItems: items.slice(startIndex, endIndex),
62+
startIndex,
63+
endIndex,
64+
offsetY,
65+
totalHeight,
66+
isVirtualized: true,
67+
};
68+
}

0 commit comments

Comments
 (0)