import React from 'react'; import Box from '@mui/material/Box'; import type { SxProps, Theme } from '@mui/material/styles'; // ─── Types ─────────────────────────────────────────────────────────────────── /** A single image in the gallery */ export interface GalleryImage { /** Image URL */ src: string; /** Alt text for accessibility */ alt: string; } /** Props for the FA ImageGallery component */ export interface ImageGalleryProps { /** Array of images to display */ images: GalleryImage[]; /** Height of the hero image area */ heroHeight?: number | { xs?: number; sm?: number; md?: number; lg?: number }; /** Height of each thumbnail (width is 4:3 ratio) */ thumbnailHeight?: number; /** MUI sx prop for style overrides */ sx?: SxProps; } // ─── Component ─────────────────────────────────────────────────────────────── /** * Image gallery with hero display and thumbnail strip. * * Shows a large hero image with a row of clickable thumbnails below. * Hovering a thumbnail previews it in the hero; clicking locks the * selection. First image is selected by default. * * Used on venue detail, coffin detail, and other product pages. * * Usage: * ```tsx * * ``` */ export const ImageGallery = React.forwardRef( ({ images, heroHeight = { xs: 280, md: 420 }, thumbnailHeight = 64, sx }, ref) => { const thumbnailWidth = Math.round(thumbnailHeight * (4 / 3)); const [selectedIndex, setSelectedIndex] = React.useState(0); const [hoverIndex, setHoverIndex] = React.useState(null); // The image shown in the hero: hovered thumbnail takes priority over selected const displayIndex = hoverIndex ?? selectedIndex; const displayImage = images[displayIndex] ?? images[0]; if (!images.length) return null; // Single image — no thumbnails needed if (images.length === 1) { return ( ); } return ( {/* Hero image */} {/* Thumbnail strip */} {images.map((image, index) => ( setSelectedIndex(index)} onMouseEnter={() => setHoverIndex(index)} onMouseLeave={() => setHoverIndex(null)} sx={{ width: thumbnailWidth, height: thumbnailHeight, flexShrink: 0, borderRadius: 1, backgroundImage: `url(${image.src})`, backgroundSize: 'cover', backgroundPosition: 'center', backgroundColor: 'var(--fa-color-surface-subtle)', cursor: 'pointer', border: '2px solid', borderColor: index === selectedIndex ? 'primary.main' : 'transparent', opacity: index === selectedIndex ? 1 : 0.7, transition: 'border-color 150ms ease-in-out, opacity 150ms ease-in-out', '&:hover': { opacity: 1, borderColor: index === selectedIndex ? 'primary.main' : 'var(--fa-color-border-default)', }, }} aria-label={image.alt} aria-current={index === selectedIndex ? 'true' : undefined} tabIndex={0} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setSelectedIndex(index); } }} /> ))} ); }, ); ImageGallery.displayName = 'ImageGallery'; export default ImageGallery;