UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

146 lines (143 loc) 5.03 kB
'use client'; import { jsx, jsxs } from 'react/jsx-runtime'; import { cn } from '../../lib/utilsComprehensive.js'; import { Loader2 } from 'lucide-react'; import { useState, useRef, useMemo, useCallback, useEffect } from 'react'; import '../../primitives/GlassCore.js'; import '../../primitives/glass/GlassAdvanced.js'; import '../../primitives/OptimizedGlassCore.js'; import '../../primitives/glass/OptimizedGlassAdvanced.js'; import '../../primitives/MotionNative.js'; import { MotionFramer } from '../../primitives/motion/MotionFramer.js'; /** * GlassVirtualList component * High-performance virtualized list that only renders visible items */ const GlassVirtualList = ({ items, height, itemHeight, estimatedItemHeight = 50, overscan = 5, smoothScroll = true, loading = false, onEndReached, endThreshold = 100, onScrollChange, className, ...props }) => { const [scrollTop, setScrollTop] = useState(0); const [containerHeight, setContainerHeight] = useState(height); const containerRef = useRef(null); const contentRef = useRef(null); // Calculate total height const totalHeight = useMemo(() => { if (itemHeight) { return (items?.length || 0) * itemHeight; } return items.reduce((sum, item) => sum + (item?.height || estimatedItemHeight), 0); }, [items, itemHeight, estimatedItemHeight]); // Get visible range const visibleRange = useMemo(() => { const itemSize = itemHeight || estimatedItemHeight; const start = Math.floor(scrollTop / itemSize); const end = Math.min((items?.length || 1) - 1, Math.ceil((scrollTop + containerHeight) / itemSize)); return { start: Math.max(0, start - overscan), end: Math.min(items.length - 1, end + overscan) }; }, [scrollTop, containerHeight, itemHeight, estimatedItemHeight, items.length, overscan]); // Get visible items const visibleItems = useMemo(() => { return items.slice(visibleRange.start, visibleRange.end + 1); }, [items, visibleRange]); // Calculate offset const offsetY = useMemo(() => { if (itemHeight) { return visibleRange.start * itemHeight; } let offset = 0; for (let i = 0; i < visibleRange.start; i++) { offset += items[i]?.height || estimatedItemHeight; } return offset; }, [visibleRange.start, itemHeight, items, estimatedItemHeight]); // Handle scroll const handleScroll = useCallback(e => { const scrollTop = e.currentTarget.scrollTop; setScrollTop(scrollTop); onScrollChange?.(scrollTop); // Check if near end for infinite scroll if (onEndReached && scrollTop + containerHeight >= totalHeight - endThreshold) { onEndReached(); } }, [onScrollChange, onEndReached, containerHeight, totalHeight, endThreshold]); // Handle resize useEffect(() => { const handleResize = () => { if (containerRef.current) { setContainerHeight(containerRef.current.clientHeight); } }; handleResize(); window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); // Update container height when prop changes useEffect(() => { setContainerHeight(height); }, [height]); return jsx(MotionFramer, { "data-glass-component": true, preset: "fadeIn", className: "glass-w-full", children: jsxs("div", { ref: containerRef, className: cn("relative overflow-auto", smoothScroll && "scroll-smooth", className), style: { height: containerHeight }, onScroll: handleScroll, ...props, children: [jsx("div", { ref: contentRef, className: 'relative', style: { height: totalHeight }, children: jsx("div", { className: 'absolute top-0 left-0 right-0', style: { transform: `translateY(${offsetY}px)` }, children: visibleItems.map((item, index) => { const ItemComponent = item?.component; visibleRange.start + index; return jsx("div", { style: { height: itemHeight || item?.height || estimatedItemHeight }, children: jsx(ItemComponent, { ...(item?.props || {}) }) }, item?.id); }) }) }), loading && jsx("div", { className: 'absolute bottom-4 left-1/2 transform -translate-x-1/2', children: jsxs("div", { className: "glass-flex glass-items-center glass-gap-2 glass-px-4 glass-py-2 glass-surface-dark/80 glass-glass-glass-backdrop-blur-md glass-contrast-guard glass-radius-full glass-contrast-guard", children: [jsx(Loader2, { className: 'w-4 h-4 animate-spin' }), jsx("span", { className: 'text-primary glass-text-sm', children: "Loading..." })] }) })] }) }); }; export { GlassVirtualList, GlassVirtualList as default }; //# sourceMappingURL=GlassVirtualList.js.map