'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
setFailed(true)}
style={style}
className={cn('absolute inset-0 h-full w-full', fit === 'cover' ? 'object-cover' : 'object-contain', imgClassName)}
/>
);
}