nyuchimzizi
Mzizi — an open-architecture project of the Bundu Foundation, operated and developed by Nyuchi. Built on the Five African Minerals palette.
Built by Nyuchi Africav4.0.39
Virtualized list for large datasets using CSS transforms.
View the full component source code below.
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
interface VirtualListProps<T> extends Omit<React.ComponentProps<"div">, "children"> {
items: T[]
renderItem: (item: T, index: number) => React.ReactNode
itemHeight: number
overscan?: number
}
function VirtualList<T>({
className,
loading = false,
items,
renderItem,
itemHeight,
overscan = 5,
...props
}: VirtualListProps<T>) {
if (loading) return (<div data-slot="virtual-list" data-portal="https://design.nyuchi.com/components/virtual-list" data-loading className={cn("animate-pulse space-y-2", className)}>{Array.from({length:8}).map((_,i) => (<div key={i} className="h-12 rounded-[var(--radius-md,12px)] bg-muted" />))}</div>)
const containerRef = React.useRef<HTMLDivElement>(null)
const [scrollTop, setScrollTop] = React.useState(0)
const [containerHeight, setContainerHeight] = React.useState(0)
React.useEffect(() => {
const container = containerRef.current
if (!container) return
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
setContainerHeight(entry.contentRect.height)
}
})
observer.observe(container)
return () => observer.disconnect()
}, [])
const handleScroll = React.useCallback((e: React.UIEvent<HTMLDivElement>) => {
setScrollTop(e.currentTarget.scrollTop)
}, [])
const totalHeight = items.length * itemHeight
const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan)
const endIndex = Math.min(
items.length,
Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan
)
const visibleItems = React.useMemo(() => {
const result: React.ReactNode[] = []
for (let i = startIndex; i < endIndex; i++) {
result.push(
<div
key={i}
data-slot="virtual-list-item"
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: itemHeight,
transform: `translateY(${i * itemHeight}px)`,
}}
>
{renderItem(items[i], i)}
</div>
)
}
return result
}, [items, startIndex, endIndex, itemHeight, renderItem])
return (
<div
ref={containerRef}
data-slot="virtual-list"
onScroll={handleScroll}
className={cn("relative overflow-auto", className)}
{...props}
>
<div data-slot="virtual-list-inner" style={{ height: totalHeight, position: "relative" }}>
{visibleItems}
</div>
</div>
)
}
export { VirtualList }
export type { VirtualListProps }
npx shadcn@latest add https://mzizi.dev/api/v1/ui/virtual-listFetch this component's metadata and source code from the registry API.
/api/v1/ui/virtual-listcomponents/ui/virtual-list.tsx