"use client"; import { memo, useCallback, useRef, useState } from "react"; import { Check, Copy, Download, Eye, Heart } from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import posthog from "posthog-js"; import type { IconEntry } from "@/lib/icons"; import { useFavoritesStore } from "@/lib/stores/favorites-store"; import { cn } from "@/lib/utils"; import { NewBadge } from "@/components/icons/new-badge"; interface IconCardProps { icon: IconEntry; onSelect: (icon: IconEntry) => void; compact?: boolean; } export const IconCard = memo(function IconCard({ icon, onSelect, compact = false, }: IconCardProps) { const [copied, setCopied] = useState(false); const router = useRouter(); // Track the slug we last prefetched, not a boolean, so virtualised/windowed // parents that reuse IconCard instances with different icons still prefetch // when the prop changes. const prefetchedSlug = useRef(null); const toggleFavorite = useFavoritesStore((s) => s.toggleFavorite); const isFavorite = useFavoritesStore((s) => s.favorites.includes(icon.slug)); const handleHoverPrefetch = useCallback(() => { if (prefetchedSlug.current === icon.slug) return; prefetchedSlug.current = icon.slug; router.prefetch(`/icon/${icon.slug}`); }, [router, icon.slug]); const handleCopy = useCallback( async () => { try { const res = await fetch(icon.variants.default); const svg = await res.text(); await navigator.clipboard.writeText(svg); setCopied(true); setTimeout(() => setCopied(false), 1500); } catch { await navigator.clipboard.writeText( `https://thesvg.org${icon.variants.default}` ); setCopied(true); setTimeout(() => setCopied(false), 1500); } posthog.capture("icon_copied", { icon_slug: icon.slug, icon_title: icon.title, format: "svg", source: "card", categories: icon.categories, }); }, [icon.variants.default, icon.slug, icon.title, icon.categories] ); const handleDownload = useCallback( async () => { try { const res = await fetch(icon.variants.default); const blob = await res.blob(); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `${icon.slug}.svg`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } catch { window.open(icon.variants.default, "_blank"); } posthog.capture("icon_downloaded", { icon_slug: icon.slug, icon_title: icon.title, variant: "default", file_type: "svg", source: "card", categories: icon.categories, }); }, [icon.variants.default, icon.slug, icon.title, icon.categories] ); const handleFavorite = useCallback( () => { toggleFavorite(icon.slug); }, [icon.slug, toggleFavorite] ); const handlePreview = useCallback( () => { onSelect(icon); }, [icon, onSelect] ); const primaryCategory = icon.categories[0]; // Theme-aware variant: show light variant on light bg, dark on dark bg const lightSrc = icon.variants.light || icon.variants.default; const darkSrc = icon.variants.dark || icon.variants.default; const needsThemeSwap = lightSrc !== darkSrc; /* ── Compact: icon-only grid ── */ if (compact) { return (
{needsThemeSwap ? ( <> ) : ( )}
{icon.title}
); } /* ── Default: modern spacious card ── */ return (
{/* Favorite toggle */} {/* Copy toast - announced to screen readers via role="status" */}
{copied && ( Copied! )}
{/* Icon preview area - clicking navigates to detail */}
{needsThemeSwap ? ( <> ) : ( )}
{/* Info section */}
{icon.title} {primaryCategory || icon.slug}
{/* Action bar - real buttons, keyboard reachable */}
); });