"use client"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; import { BASE_URL, REPO_PATH } from "@/constants"; import type { AuthorData, Icon } from "@/types/icons"; import confetti from "canvas-confetti"; import { motion } from "framer-motion"; import { Check, Copy, Download, FileType, Github, Moon, PaletteIcon, Sun, } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; import { useCallback, useState } from "react"; import { toast } from "sonner"; import { Carbon } from "./carbon"; import { MagicCard } from "./magicui/magic-card"; export type IconDetailsProps = { icon: string; iconData: Icon; authorData: AuthorData; }; export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) { const authorName = authorData.name || authorData.login || ""; const iconColorVariants = iconData.colors; const formattedDate = new Date(iconData.update.timestamp).toLocaleDateString( "en-GB", { day: "numeric", month: "long", year: "numeric", }, ); const getAvailableFormats = () => { switch (iconData.base) { case "svg": return ["svg", "png", "webp"]; case "png": return ["png", "webp"]; default: return [iconData.base]; } }; const availableFormats = getAvailableFormats(); const [copiedVariants, setCopiedVariants] = useState>( {}, ); // Launch confetti from the pointer position const launchConfetti = useCallback((originX?: number, originY?: number) => { const defaults = { startVelocity: 15, spread: 180, ticks: 50, zIndex: 0, disableForReducedMotion: true, colors: [ "#ff0a54", "#ff477e", "#ff7096", "#ff85a1", "#fbb1bd", "#f9bec7", ], }; // If we have origin coordinates, use them if (originX !== undefined && originY !== undefined) { confetti({ ...defaults, particleCount: 50, origin: { x: originX / window.innerWidth, y: originY / window.innerHeight, }, }); } else { // Default to center of screen confetti({ ...defaults, particleCount: 50, origin: { x: 0.5, y: 0.5 }, }); } }, []); const handleCopy = ( url: string, variantKey: string, event?: React.MouseEvent, ) => { navigator.clipboard.writeText(url); setCopiedVariants((prev) => ({ ...prev, [variantKey]: true, })); setTimeout(() => { setCopiedVariants((prev) => ({ ...prev, [variantKey]: false, })); }, 2000); // Launch confetti from click position or center of screen if (event) { launchConfetti(event.clientX, event.clientY); } else { launchConfetti(); } toast.success("URL copied", { description: "The icon URL has been copied to your clipboard. Ready to use!", }); }; const handleDownload = async ( event: React.MouseEvent, url: string, filename: string, ) => { event.preventDefault(); // Launch confetti from download button position launchConfetti(event.clientX, event.clientY); try { // Show loading toast toast.loading("Preparing download..."); // Fetch the file first as a blob const response = await fetch(url); const blob = await response.blob(); // Create a blob URL and use it for download const blobUrl = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = blobUrl; link.download = filename; document.body.appendChild(link); link.click(); // Clean up document.body.removeChild(link); setTimeout(() => URL.revokeObjectURL(blobUrl), 100); toast.dismiss(); toast.success("Download started", { description: "Your icon file is being downloaded and will be saved to your device.", }); } catch (error) { console.error("Download error:", error); toast.dismiss(); toast.error("Download failed", { description: "There was an error downloading the file. Please try again.", }); } }; const renderVariant = ( format: string, iconName: string, theme?: "light" | "dark", ) => { const variantName = theme && iconColorVariants?.[theme] ? iconColorVariants[theme] : iconName; const imageUrl = `${BASE_URL}/${format}/${variantName}.${format}`; const githubUrl = `${REPO_PATH}/tree/main/${format}/${iconName}.${format}`; const variantKey = `${format}-${theme || "default"}`; const isCopied = copiedVariants[variantKey] || false; return (
handleCopy(imageUrl, variantKey, e)} >
{`${iconName}

Click to copy direct URL to clipboard

{format.toUpperCase()}

Download icon file

Copy direct URL to clipboard

View on GitHub

); }; return (
{/* Left Column: Icon Info and Author */}
{icon}
{icon}

Updated on:{" "} {formattedDate}

By:

{authorName ? authorName.slice(0, 2).toUpperCase() : "??"} {authorData.html_url ? ( {authorName} ) : ( {authorName} )}
{iconData.categories && iconData.categories.length > 0 && (

Categories

{iconData.categories.map((category) => ( {category .split("-") .map( (word) => word.charAt(0).toUpperCase() + word.slice(1), ) .join(" ")} ))}
)} {iconData.aliases && iconData.aliases.length > 0 && (

Aliases

{iconData.aliases.map((alias) => ( {alias} ))}

These aliases can be used to find this icon in search results.

)}
{/* Middle Column: Icon variants */}
Icon variants Click on any icon to copy its URL to your clipboard {!iconData.colors ? (
{availableFormats.map((format) => renderVariant(format, icon), )}
) : (

Light theme

{availableFormats.map((format) => renderVariant(format, icon, "light"), )}

Dark theme

{availableFormats.map((format) => renderVariant(format, icon, "dark"), )}
)}
{/* Right Column: Technical details */}
Technical details

Base format

{iconData.base.toUpperCase()}

Available formats

{availableFormats.map((format) => (
{format.toUpperCase()}
))}
{iconData.colors && (

Color variants

{Object.entries(iconData.colors).map( ([theme, variant]) => (
{theme}: {variant}
), )}
)}

Source

); }