feat(icons): implement theme-aware icon variants in search and details components

This commit is contained in:
Bjorn Lammers 2025-04-17 03:02:30 +02:00 committed by Thomas Camlong
parent bf78bc6a24
commit 62ab677ee3
No known key found for this signature in database
GPG Key ID: A678F374F428457B
2 changed files with 41 additions and 3 deletions

View File

@ -10,6 +10,7 @@ import Image from "next/image"
import Link from "next/link" import Link from "next/link"
import { usePathname, useRouter, useSearchParams } from "next/navigation" import { usePathname, useRouter, useSearchParams } from "next/navigation"
import { useCallback, useEffect, useRef, useState } from "react" import { useCallback, useEffect, useRef, useState } from "react"
import { useTheme } from "next-themes"
export function IconSearch({ icons }: IconSearchProps) { export function IconSearch({ icons }: IconSearchProps) {
const searchParams = useSearchParams() const searchParams = useSearchParams()
@ -18,6 +19,7 @@ export function IconSearch({ icons }: IconSearchProps) {
const pathname = usePathname() const pathname = usePathname()
const [searchQuery, setSearchQuery] = useState(initialQuery ?? "") const [searchQuery, setSearchQuery] = useState(initialQuery ?? "")
const timeoutRef = useRef<NodeJS.Timeout | null>(null) const timeoutRef = useRef<NodeJS.Timeout | null>(null)
const { resolvedTheme } = useTheme()
const [filteredIcons, setFilteredIcons] = useState(() => { const [filteredIcons, setFilteredIcons] = useState(() => {
if (!initialQuery?.trim()) return icons if (!initialQuery?.trim()) return icons
@ -79,6 +81,23 @@ export function IconSearch({ icons }: IconSearchProps) {
} }
}, []) }, [])
// Helper function to get the appropriate icon variant based on theme
const getIconVariant = (name: string, data: any) => {
// Check if the icon has theme variants and use appropriate one
if (data.colors) {
// If in dark mode and a light variant exists, use the light variant
if (resolvedTheme === 'dark' && data.colors.light) {
return data.colors.light;
}
// If in light mode and a dark variant exists, use the dark variant
else if (resolvedTheme === 'light' && data.colors.dark) {
return data.colors.dark;
}
}
// Fall back to the default name if no appropriate variant
return name;
}
if (!searchParams) return null if (!searchParams) return null
return ( return (
@ -133,7 +152,7 @@ export function IconSearch({ icons }: IconSearchProps) {
<div className="relative h-12 w-12 sm:h-16 sm:w-16 mb-2"> <div className="relative h-12 w-12 sm:h-16 sm:w-16 mb-2">
<Image <Image
src={`${BASE_URL}/${data.base}/${name}.${data.base}`} src={`${BASE_URL}/${data.base}/${getIconVariant(name, data)}.${data.base}`}
alt={`${name} icon`} alt={`${name} icon`}
fill fill
className="object-contain p-1 group-hover:scale-110 transition-transform duration-300" className="object-contain p-1 group-hover:scale-110 transition-transform duration-300"

View File

@ -13,6 +13,7 @@ import Link from "next/link"
import { useState } from "react" import { useState } from "react"
import { toast } from "sonner" import { toast } from "sonner"
import { Carbon } from "./carbon" import { Carbon } from "./carbon"
import { useTheme } from "next-themes"
export type IconDetailsProps = { export type IconDetailsProps = {
icon: string icon: string
@ -21,6 +22,7 @@ export type IconDetailsProps = {
} }
export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) { export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) {
const { resolvedTheme } = useTheme()
const authorName = authorData.name || authorData.login || "" const authorName = authorData.name || authorData.login || ""
const iconColorVariants = iconData.colors const iconColorVariants = iconData.colors
const formattedDate = new Date(iconData.update.timestamp).toLocaleDateString("en-GB", { const formattedDate = new Date(iconData.update.timestamp).toLocaleDateString("en-GB", {
@ -42,6 +44,23 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) {
const availableFormats = getAvailableFormats() const availableFormats = getAvailableFormats()
const [copiedVariants, setCopiedVariants] = useState<Record<string, boolean>>({}) const [copiedVariants, setCopiedVariants] = useState<Record<string, boolean>>({})
// Helper function to get the appropriate icon variant based on theme
const getIconVariant = (iconName: string) => {
// Check if the icon has theme variants
if (iconColorVariants) {
// If in dark mode and a light variant exists, use the light variant
if (resolvedTheme === 'dark' && iconColorVariants.light) {
return iconColorVariants.light;
}
// If in light mode and a dark variant exists, use the dark variant
else if (resolvedTheme === 'light' && iconColorVariants.dark) {
return iconColorVariants.dark;
}
}
// Fall back to the default name if no appropriate variant
return iconName;
}
const handleCopy = (url: string, variantKey: string) => { const handleCopy = (url: string, variantKey: string) => {
navigator.clipboard.writeText(url) navigator.clipboard.writeText(url)
setCopiedVariants((prev) => ({ setCopiedVariants((prev) => ({
@ -168,7 +187,7 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) {
<div className="flex flex-col items-center"> <div className="flex flex-col items-center">
<div className="relative w-32 h-32 bg-background/90 rounded-xl overflow-hidden border flex items-center justify-center p-3 mb-4"> <div className="relative w-32 h-32 bg-background/90 rounded-xl overflow-hidden border flex items-center justify-center p-3 mb-4">
<Image <Image
src={`${BASE_URL}/${iconData.base}/${icon}.${iconData.base}`} src={`${BASE_URL}/${iconData.base}/${getIconVariant(icon)}.${iconData.base}`}
width={96} width={96}
height={96} height={96}
alt={icon} alt={icon}
@ -332,7 +351,7 @@ export function IconDetails({ icon, iconData, authorData }: IconDetailsProps) {
<h3 className="text-sm font-semibold text-muted-foreground">Source</h3> <h3 className="text-sm font-semibold text-muted-foreground">Source</h3>
<Button variant="outline" className="w-full" asChild> <Button variant="outline" className="w-full" asChild>
<Link <Link
href={`${REPO_PATH}/tree/main/${iconData.base}/${icon}.${iconData.base}`} href={`${REPO_PATH}/blob/main/meta/${icon}.json`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >