"use client"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import type { IconEntry } from "@/lib/icons"; import { IconCard } from "./icon-card"; import { IconDetail } from "./icon-detail"; import { cn } from "@/lib/utils"; type ViewMode = "compact" | "comfortable"; interface IconGridProps { icons: IconEntry[]; view?: ViewMode; } const INITIAL_COUNT = 60; const LOAD_MORE_COUNT = 60; export function IconGrid({ icons, view = "comfortable" }: IconGridProps) { const [visibleCount, setVisibleCount] = useState(INITIAL_COUNT); const [selectedIcon, setSelectedIcon] = useState(null); const sentinelRef = useRef(null); const loadingRef = useRef(false); const visibleIcons = useMemo( () => icons.slice(0, visibleCount), [icons, visibleCount] ); const hasMore = visibleCount < icons.length; // Intersection observer for infinite scroll. // The parent passes a stable `key` derived from the active query+category so // React remounts this component with fresh state when filters change — no // secondary setState loop is needed to reset visibleCount. useEffect(() => { const sentinel = sentinelRef.current; if (!sentinel) return; const total = icons.length; const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting && !loadingRef.current) { loadingRef.current = true; setVisibleCount((prev) => { const next = Math.min(prev + LOAD_MORE_COUNT, total); setTimeout(() => { loadingRef.current = false; }, 200); return next; }); } }, { rootMargin: "400px" } ); observer.observe(sentinel); return () => observer.disconnect(); // icons.length is the only sentinel-rebuild trigger; visibleCount changes // are handled inside the callback via closure over `total`. }, [icons.length]); const handleSelect = useCallback((icon: IconEntry) => { setSelectedIcon(icon); }, []); const handleClose = useCallback(() => { setSelectedIcon(null); }, []); if (icons.length === 0) { return (

No icons found

Try a different search term or category

Submit this icon
); } return ( <>
{visibleIcons.map((icon) => ( ))}
{/* Infinite scroll sentinel */} {hasMore && (
)} ); }