'use client'; import { useState, type CSSProperties } from 'react'; import { cn } from '@/lib/format'; import type { PlaceholderGlyph } from '@/lib/image-glyph'; export { glyphForFamily, type PlaceholderGlyph } from '@/lib/image-glyph'; /** * Plain, lazy `` bound to the /img cache (src + srcSet computed on the server) with a * designed placeholder — never the browser's broken-image glyph. Falls back on load error. */ export interface SmartImageProps { src: string | null; srcSet?: string | null; sizes?: string; alt: string; fit?: 'contain' | 'cover'; priority?: boolean; className?: string; imgClassName?: string; style?: CSSProperties; placeholder?: { label?: string | null; glyph?: PlaceholderGlyph }; /** small thumbnail: glyph only */ compact?: boolean; } function Glyph({ kind, className }: { kind: PlaceholderGlyph; className?: string }) { const p = { fill: 'none', stroke: 'currentColor', strokeWidth: 1.4, strokeLinecap: 'round' as const, strokeLinejoin: 'round' as const }; switch (kind) { case 'card': return ( ); case 'comic': return ( ); case 'watch': return ( ); case 'brick': return ( ); case 'game': return ( ); case 'sneaker': return ( ); case 'coin': return ( ); case 'bottle': return ( ); case 'car': return ( ); case 'toy': return ( ); case 'book': return ( ); case 'art': return ( ); default: return ( ); } } export function ImagePlaceholder({ label, glyph = 'box', compact = false, className }: { label?: string | null; glyph?: PlaceholderGlyph; compact?: boolean; className?: string }) { return ( {!compact && label ? {label} : null} ); } export function SmartImage({ src, srcSet, sizes, alt, fit = 'contain', priority = false, className, imgClassName, style, placeholder, compact = false }: SmartImageProps) { const [failed, setFailed] = useState(false); if (!src || failed) return ; return ( // eslint-disable-next-line @next/next/no-img-element {alt} setFailed(true)} style={style} className={cn('absolute inset-0 h-full w-full', fit === 'cover' ? 'object-cover' : 'object-contain', imgClassName)} /> ); }