import Image from "next/image"; import Link from "next/link"; type ClampOpts = { maxIntrinsic: number }; /** * Clamps intrinsic w/h while preserving aspect ratio. * This helps prevent Next/Image from treating a tiny thumbnail as a large asset. */ function clampDims(w: number, h: number, { maxIntrinsic }: ClampOpts) { const W = Math.max(1, w); const H = Math.max(1, h); const maxSide = Math.max(W, H); if (maxSide <= maxIntrinsic) return { w: W, h: H }; const scale = maxIntrinsic / maxSide; return { w: Math.max(1, Math.round(W * scale)), h: Math.max(1, Math.round(H * scale)), }; } export type ArtworkImageCardProps = { href: string; src: string; alt: string; /** * Provide real dimensions if you have them. * For tile mode, we still pass intrinsic sizes, but clamp them. */ width?: number; height?: number; /** * tile: uses `fill` + container aspect ratio (best for masonry/grid) * hero: uses width/height when available (best for single page) */ mode?: "tile" | "hero"; /** * For tile mode only. * Example: "4 / 3" or `${w} / ${h}` */ aspectRatio?: string; className?: string; imageClassName?: string; /** * Controls intrinsic clamp for thumbnails: * you asked: “max the double of 160” => 320 default. */ maxThumbIntrinsic?: number; sizes?: string; priority?: boolean; style?: React.CSSProperties; linkClassName?: string; }; export function ArtworkImageCard({ href, src, alt, width, height, mode = "tile", aspectRatio = "4 / 3", className, imageClassName, maxThumbIntrinsic = 320, sizes, priority, style, }: ArtworkImageCardProps) { const w = width ?? 0; const h = height ?? 0; const isHero = mode === "hero"; const hasDims = w > 0 && h > 0; // Clamp only for non-hero (thumbnail-like) usage const clamped = !isHero && hasDims ? clampDims(w, h, { maxIntrinsic: maxThumbIntrinsic }) : null; return (