From f3829f533b95111db8da002d28d3186ab0c67187 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Fri, 25 Apr 2025 00:38:46 +0200 Subject: [PATCH] feat(web): Implement virtualized icon grid --- web/src/components/icon-card.tsx | 2 +- web/src/components/icon-grid.tsx | 67 ++++++++++++++++++++++++++++++ web/src/components/icon-search.tsx | 4 +- 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/web/src/components/icon-card.tsx b/web/src/components/icon-card.tsx index bcd02316..05e19e72 100644 --- a/web/src/components/icon-card.tsx +++ b/web/src/components/icon-card.tsx @@ -16,7 +16,7 @@ export function IconCard({ return ( -
+
{`${name} ) } + +export function VirtualizedIconsGrid({ filteredIcons, matchedAliases }: IconsGridProps) { + const listRef = useRef(null) + const [windowWidth, setWindowWidth] = useState(typeof window !== "undefined" ? window.innerWidth : 1280) + + useEffect(() => { + const handleResize = () => { + setWindowWidth(window.innerWidth) + } + window.addEventListener("resize", handleResize) + return () => window.removeEventListener("resize", handleResize) + }, []) + + const columnCount = useMemo(() => { + if (windowWidth >= 1280) return 8 // xl + if (windowWidth >= 1024) return 6 // lg + if (windowWidth >= 768) return 4 // md + if (windowWidth >= 640) return 3 // sm + return 2 // default + }, [windowWidth]) + + const rowCount = Math.ceil(filteredIcons.length / columnCount) + const rowVirtualizer = useWindowVirtualizer({ + count: rowCount, + estimateSize: () => 140, + overscan: 5, + }) + + return ( +
+
+ {rowVirtualizer.getVirtualItems().map((virtualRow) => { + const rowStart = virtualRow.index * columnCount + const rowEnd = Math.min(rowStart + columnCount, filteredIcons.length) + const rowIcons = filteredIcons.slice(rowStart, rowEnd) + + return ( +
+ {rowIcons.map(({ name, data }) => ( + + ))} +
+ ) + })} +
+
+ ) +} diff --git a/web/src/components/icon-search.tsx b/web/src/components/icon-search.tsx index 375716ba..3fdc666c 100644 --- a/web/src/components/icon-search.tsx +++ b/web/src/components/icon-search.tsx @@ -1,6 +1,6 @@ "use client" -import { IconsGrid } from "@/components/icon-grid" +import { VirtualizedIconsGrid } from "@/components/icon-grid" import { IconSubmissionContent } from "@/components/icon-submission-form" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" @@ -432,7 +432,7 @@ export function IconSearch({ icons }: IconSearchProps) {
- + )}