45 lines
1.2 KiB
TypeScript
45 lines
1.2 KiB
TypeScript
/**
|
|
* Renders a product's artwork. Seeded products carry an image path
|
|
* (`/art/product-*.webp`); admin-created products may have no image, in which
|
|
* case the previous emoji/placeholder fallback is kept so nothing regresses.
|
|
*/
|
|
export function ProductImage({
|
|
image,
|
|
alt,
|
|
className = '',
|
|
eager = false,
|
|
}: {
|
|
image?: string | null;
|
|
alt: string;
|
|
className?: string;
|
|
eager?: boolean;
|
|
}) {
|
|
if (image && image.startsWith('/')) {
|
|
return (
|
|
<div
|
|
className={`overflow-hidden rounded-xl border ${className}`}
|
|
style={{ borderColor: 'var(--border)', background: 'var(--surface-muted)' }}
|
|
>
|
|
{/* eslint-disable-next-line @next/next/no-img-element -- static /public art; keeps both stacks dependency-free */}
|
|
<img
|
|
src={image}
|
|
alt={alt}
|
|
loading={eager ? 'eager' : 'lazy'}
|
|
decoding="async"
|
|
className="h-full w-full object-cover"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
return (
|
|
<div
|
|
className={`flex items-center justify-center rounded-xl border ${className}`}
|
|
style={{ borderColor: 'var(--border)', background: 'var(--surface-muted)' }}
|
|
>
|
|
<span aria-hidden className="text-5xl">
|
|
{image ?? '📦'}
|
|
</span>
|
|
</div>
|
|
);
|
|
}
|