'use client'; import { SmartImage, ImagePlaceholder, type PlaceholderGlyph } from '@/components/ui/smart-image'; import { useCallback, useEffect, useRef, useState } from 'react'; import { ChevronLeft, ChevronRight, Maximize2, X } from 'lucide-react'; import { cn } from '@/lib/format'; export interface GalleryImage { url: string; caption?: string | null; /** cached /img sources (added by the server component) */ src?: string | null; srcSet?: string | null; full?: string | null; } /** * Hero image with a full-screen, swipeable gallery (scroll-snap + keyboard). Images are shown * `object-contain` on a neutral surface so cards, watches and boxes keep their proportions. */ export function AssetGallery({ images, alt, className, priority = true, glyph = 'box' }: { images: GalleryImage[]; alt: string; className?: string; priority?: boolean; glyph?: PlaceholderGlyph }) { const [open, setOpen] = useState(false); const [index, setIndex] = useState(0); const track = useRef(null); const hero = images[0]; const go = useCallback( (i: number) => { const n = images.length; const next = ((i % n) + n) % n; setIndex(next); track.current?.children[next]?.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }); }, [images.length], ); useEffect(() => { if (!open) return; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setOpen(false); if (e.key === 'ArrowRight') go(index + 1); if (e.key === 'ArrowLeft') go(index - 1); }; document.addEventListener('keydown', onKey); document.body.style.overflow = 'hidden'; return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = ''; }; }, [open, index, go]); if (!hero) { return (
); } return ( <> {open ? (
{index + 1} / {images.length}
{ const el = e.currentTarget; const i = Math.round(el.scrollLeft / el.clientWidth); if (i !== index) setIndex(i); }} > {images.map((im, i) => (
{im.caption ?
{im.caption}
: null}
))}
{images.length > 1 ? (
{images.slice(0, 12).map((_, i) => ( ))}
) : (
)}
) : null} ); }