mirror of
https://github.com/walkxcode/dashboard-icons.git
synced 2025-06-28 07:20:21 +08:00
feat(web): Implement virtualized icon grid
This commit is contained in:
parent
3499605fb7
commit
f3829f533b
@ -16,7 +16,7 @@ export function IconCard({
|
||||
return (
|
||||
<MagicCard className="rounded-md shadow-md">
|
||||
<Link prefetch={false} href={`/icons/${name}`} className="group flex flex-col items-center p-3 sm:p-4 cursor-pointer">
|
||||
<div className="relative h-12 w-12 sm:h-16 sm:w-16 mb-2">
|
||||
<div className="relative h-16 w-16 mb-2">
|
||||
<Image
|
||||
src={`${BASE_URL}/${iconData.base}/${name}.${iconData.base}`}
|
||||
alt={`${name} icon`}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import type { Icon } from "@/types/icons"
|
||||
|
||||
import { useWindowVirtualizer } from "@tanstack/react-virtual"
|
||||
import { useEffect, useMemo, useRef, useState } from "react"
|
||||
import { IconCard } from "./icon-card"
|
||||
|
||||
interface IconsGridProps {
|
||||
@ -16,3 +18,68 @@ export function IconsGrid({ filteredIcons, matchedAliases }: IconsGridProps) {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function VirtualizedIconsGrid({ filteredIcons, matchedAliases }: IconsGridProps) {
|
||||
const listRef = useRef<HTMLDivElement | null>(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 (
|
||||
<div ref={listRef} className="mt-2">
|
||||
<div
|
||||
style={{
|
||||
height: `${rowVirtualizer.getTotalSize()}px`,
|
||||
width: "100%",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
|
||||
const rowStart = virtualRow.index * columnCount
|
||||
const rowEnd = Math.min(rowStart + columnCount, filteredIcons.length)
|
||||
const rowIcons = filteredIcons.slice(rowStart, rowEnd)
|
||||
|
||||
return (
|
||||
<div
|
||||
key={virtualRow.key}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
minHeight: 124,
|
||||
width: "100%",
|
||||
transform: `translateY(${virtualRow.start - rowVirtualizer.options.scrollMargin}px)`,
|
||||
}}
|
||||
className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-4"
|
||||
>
|
||||
{rowIcons.map(({ name, data }) => (
|
||||
<IconCard key={name} name={name} data={data} matchedAlias={matchedAliases[name]} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -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) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<IconsGrid filteredIcons={filteredIcons} matchedAliases={matchedAliases} />
|
||||
<VirtualizedIconsGrid filteredIcons={filteredIcons} matchedAliases={matchedAliases} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
Loading…
x
Reference in New Issue
Block a user