diff --git a/svg/afordin-light.svg b/svg/afordin-light.svg deleted file mode 100644 index 7da3ecd1..00000000 --- a/svg/afordin-light.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/web/src/components/icon-details.tsx b/web/src/components/icon-details.tsx index 28cda419..63b8bf81 100644 --- a/web/src/components/icon-details.tsx +++ b/web/src/components/icon-details.tsx @@ -8,8 +8,7 @@ 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 { useTheme } from "next-themes" +import { Check, Copy, Download, FileType, Github, Moon, PaletteIcon, Sun, Type } from "lucide-react" import Image from "next/image" import Link from "next/link" import { useCallback, useState } from "react" @@ -24,14 +23,312 @@ export type IconDetailsProps = { authorData: AuthorData } +function IconVariant({ + format, + iconName, + theme, + isWordmark = false, + iconData, + onCopy, + onDownload, + copiedVariants +}: { + format: string + iconName: string + theme?: "light" | "dark" + isWordmark?: boolean + iconData: Icon + onCopy: (url: string, variantKey: string, event?: React.MouseEvent) => void + onDownload: (event: React.MouseEvent, url: string, filename: string) => void + copiedVariants: Record +}) { + let variantName = ''; + + if (isWordmark) { + if (theme && iconData.wordmark && typeof iconData.wordmark !== 'string') { + variantName = iconData.wordmark[theme] || `${iconName}_wordmark_${theme}`; + } else if (iconData.wordmark && typeof iconData.wordmark === 'string') { + variantName = iconData.wordmark; + } else { + variantName = `${iconName}_wordmark`; + } + } else { + if (theme && iconData.colors) { + variantName = iconData.colors[theme] || `${iconName}_${theme}`; + } else { + variantName = iconName; + } + } + + const imageUrl = `${BASE_URL}/${format}/${variantName}.${format}`; + const githubUrl = `${REPO_PATH}/tree/main/${format}/${variantName}.${format}`; + const variantKey = `${format}-${theme || "default"}${isWordmark ? "-wordmark" : ""}`; + const isCopied = copiedVariants[variantKey] || false; + const btnCopied = copiedVariants[`btn-${variantKey}`] || false; + + return ( + + +
+ + + onCopy(imageUrl, variantKey, e)} + > +
+ + + {isCopied && ( + + + + )} + + + {`${iconName} + + + +

Click to copy direct URL to clipboard

+
+ + +

{format.toUpperCase()}

+ +
+ + + + + +

Download icon file

+
+
+ + + + + + +

Copy direct URL to clipboard

+
+
+ + + + + + +

View on GitHub

+
+
+
+
+ + + ) +} + +function IconVariantsSection({ + availableFormats, + icon, + theme, + isWordmark = false, + iconData, + handleCopy, + handleDownload, + copiedVariants, + title, + iconElement +}: { + availableFormats: string[] + icon: string + theme?: "light" | "dark" + isWordmark?: boolean + iconData: Icon + handleCopy: (url: string, variantKey: string, event?: React.MouseEvent) => void + handleDownload: (event: React.MouseEvent, url: string, filename: string) => void + copiedVariants: Record + title: string + iconElement: React.ReactNode +}) { + return ( +
+

+ {iconElement} + {title} +

+
+ {availableFormats.map((format) => ( + + ))} +
+
+ ) +} + +function WordmarkSection({ + iconData, + icon, + availableFormats, + handleCopy, + handleDownload, + copiedVariants +}: { + iconData: Icon + icon: string + availableFormats: string[] + handleCopy: (url: string, variantKey: string, event?: React.MouseEvent) => void + handleDownload: (event: React.MouseEvent, url: string, filename: string) => void + copiedVariants: Record +}) { + if (!iconData.wordmark) return null; + + const isStringWordmark = typeof iconData.wordmark === 'string'; + const hasLightDarkVariants = !isStringWordmark && (iconData.wordmark.light || iconData.wordmark.dark); + + return ( +
+

+ + Wordmark variants +

+ + {(isStringWordmark || !hasLightDarkVariants) && ( +
+ {availableFormats.map((format) => ( + + ))} +
+ )} + + {hasLightDarkVariants && ( +
+ {iconData.wordmark.light && ( +
+

+ + Light +

+
+ {availableFormats.map((format) => ( + + ))} +
+
+ )} + + {iconData.wordmark.dark && ( +
+

+ + Dark +

+
+ {availableFormats.map((format) => ( + + ))} +
+
+ )} +
+ )} +
+ ) +} + export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) { const authorName = authorData.name || authorData.login || "" const iconColorVariants = iconData.colors + const iconWordmarkVariants = iconData.wordmark const formattedDate = new Date(iconData.update.timestamp).toLocaleDateString("en-GB", { day: "numeric", month: "long", year: "numeric", }) + const getAvailableFormats = () => { switch (iconData.base) { case "svg": @@ -46,7 +343,6 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) { const availableFormats = getAvailableFormats() const [copiedVariants, setCopiedVariants] = useState>({}) - // Launch confetti from the pointer position const launchConfetti = useCallback((originX?: number, originY?: number) => { const defaults = { startVelocity: 15, @@ -57,7 +353,6 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) { colors: ["#ff0a54", "#ff477e", "#ff7096", "#ff85a1", "#fbb1bd", "#f9bec7"], } - // If we have origin coordinates, use them if (originX !== undefined && originY !== undefined) { confetti({ ...defaults, @@ -68,7 +363,6 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) { }, }) } else { - // Default to center of screen confetti({ ...defaults, particleCount: 50, @@ -90,7 +384,6 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) { })) }, 2000) - // Launch confetti from click position or center of screen if (event) { launchConfetti(event.clientX, event.clientY) } else { @@ -104,27 +397,18 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) { 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) @@ -141,120 +425,9 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) { } } - 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 */}
@@ -287,7 +460,7 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) { {authorName ? authorName.slice(0, 2).toUpperCase() : "??"} - {authorData.html_url ? ( + {authorData.html_url && ( {authorName} - ) : ( + )} + {!authorData.html_url && ( {authorName} )}
@@ -348,11 +522,15 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) {

Available in{" "} - {availableFormats.length > 1 - ? `${availableFormats.length} formats (${availableFormats.map((f) => f.toUpperCase()).join(", ")})` - : `${availableFormats[0].toUpperCase()} format`}{" "} + {availableFormats.length > 1 && ( + `${availableFormats.length} formats (${availableFormats.map((f) => f.toUpperCase()).join(", ")})` + )} + {availableFormats.length === 1 && ( + `${availableFormats[0].toUpperCase()} format` + )}{" "} with a base format of {iconData.base.toUpperCase()}. {iconData.colors && " Includes both light and dark theme variants for better integration with different UI designs."} + {iconData.wordmark && " Wordmark variants are also available for enhanced branding options."}

Use the {icon} icon in your web applications, dashboards, or documentation to enhance visual communication and user @@ -365,7 +543,6 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) {

- {/* Middle Column: Icon variants */}
@@ -373,37 +550,61 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) { 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"))} -
-
-
- )} +
+ } + /> + + {iconData.colors && ( + <> + } + /> + + } + /> + + )} + + {iconData.wordmark && ( + + )} +
- {/* Right Column: Technical details */}
@@ -445,6 +646,28 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) {
)} + {iconData.wordmark && ( +
+

Wordmark variants

+
+ {iconData.wordmark.light && ( +
+ + Light: + {iconData.wordmark.light} +
+ )} + {iconData.wordmark.dark && ( +
+ + Dark: + {iconData.wordmark.dark} +
+ )} +
+
+ )} +

Source