Export modal now shows a loading indicator and doesn't freeze the UI. Large list modals use virtual scrolling for smooth performance.
54 lines
1.5 KiB
TypeScript
54 lines
1.5 KiB
TypeScript
'use client'
|
|
|
|
import { useRef } from 'react'
|
|
import { useVirtualizer } from '@tanstack/react-virtual'
|
|
|
|
interface VirtualListProps<T> {
|
|
items: T[]
|
|
estimateSize: number
|
|
className?: string
|
|
renderItem: (item: T, index: number) => React.ReactNode
|
|
}
|
|
|
|
export default function VirtualList<T>({ items, estimateSize, className, renderItem }: VirtualListProps<T>) {
|
|
const parentRef = useRef<HTMLDivElement>(null)
|
|
|
|
const virtualizer = useVirtualizer({
|
|
count: items.length,
|
|
getScrollElement: () => parentRef.current,
|
|
estimateSize: () => estimateSize,
|
|
overscan: 10,
|
|
})
|
|
|
|
// For small lists (< 50 items), render directly without virtualization
|
|
if (items.length < 50) {
|
|
return (
|
|
<div className={className}>
|
|
{items.map((item, index) => renderItem(item, index))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div ref={parentRef} className={className} style={{ overflow: 'auto' }}>
|
|
<div style={{ height: `${virtualizer.getTotalSize()}px`, width: '100%', position: 'relative' }}>
|
|
{virtualizer.getVirtualItems().map((virtualRow) => (
|
|
<div
|
|
key={virtualRow.key}
|
|
style={{
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
width: '100%',
|
|
height: `${virtualRow.size}px`,
|
|
transform: `translateY(${virtualRow.start}px)`,
|
|
}}
|
|
>
|
|
{renderItem(items[virtualRow.index], virtualRow.index)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|