import Image from "next/image"; import { useState, useEffect, useRef, useCallback } from "react"; import { createPortal } from "react-dom"; import '@/styles/gallery.css'; type Props = { images: string[]; videos?: string[]; title: string; }; // 0 = inline default, 1 = inline enlarged, 2 = fullscreen overlay type EnlargeLevel = 0 | 1 | 2; export default function SimpleGallery({ images, videos = [], title }: Props) { const [active, setActive] = useState(0); const [enlargeLevel, setEnlargeLevel] = useState(0); const timerRef = useRef | null>(null); const videoRef = useRef(null); const totalMedia = images.length + videos.length; const isVideo = active >= images.length; const videoIndex = active - images.length; const currentSrc = isVideo ? videos[videoIndex] : images[active]; if (totalMedia === 0) return null; function handleEnlarge() { if (enlargeLevel === 0) { setEnlargeLevel(1); } else if (enlargeLevel === 1) { setEnlargeLevel(2); } } function handleShrink() { if (enlargeLevel === 2) { setEnlargeLevel(1); } else if (enlargeLevel === 1) { setEnlargeLevel(0); } } function handleFullscreenClose() { setEnlargeLevel(1); } function handleThumbnailClick(index: number) { setActive(index); } function expandButton() { return ( ); } function shrinkButton() { return ( ); } function inlineMedia() { const enlarged = enlargeLevel === 1; const imageStyle: React.CSSProperties = enlarged ? { maxWidth: "100%", maxHeight: "90vh", height: "auto", borderRadius: 6, objectFit: "contain", boxShadow: "0 8px 40px rgba(0,0,0,0.6)", transition: "all 0.3s ease", } : { width: "100%", borderRadius: 4, objectFit: "cover", maxHeight: 250, display: "block", transition: "all 0.8s ease", zIndex: 1, }; const videoStyle: React.CSSProperties = enlarged ? { maxWidth: "100%", maxHeight: "90vh", objectFit: "contain" as const, borderRadius: 6, boxShadow: "0 8px 40px rgba(0,0,0,0.6)", transition: "all 0.3s ease", } : { width: "100%", borderRadius: 4, maxHeight: 250, display: "block", transition: "all 0.8s ease", }; return (
{isVideo ? (
); } function fullscreenOverlay() { if (enlargeLevel !== 2) return null; return createPortal(
{isVideo ? (
, // portal target document.body ); } function thumbnailStrip() { if (totalMedia <= 1) return null; return (
{images.map((src, i) => ( {`${title} handleThumbnailClick(i)} style={{ width: 48, height: 36, objectFit: "cover", borderRadius: 3, cursor: "pointer", border: `2px solid ${i === active ? "var(--accent)" : "var(--border)"}`, opacity: i === active ? 1 : 0.6, transition: "all 0.15s ease", pointerEvents: "auto", }} /> ))} {videos.map((_, i) => { const mediaIndex = images.length + i; const isActive = mediaIndex === active; return ( ); })}
); } useEffect(() => { return () => { if (timerRef.current) clearTimeout(timerRef.current); }; }, []); // close fullscreen on Escape useEffect(() => { if (enlargeLevel === 0) return; function onKeyDown(e: KeyboardEvent) { if (e.key === "Escape" && enlargeLevel === 2) handleFullscreenClose(); if (e.key === "Escape" && enlargeLevel === 1) handleShrink(); } window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }, [enlargeLevel]); return ( <>
{inlineMedia()}
{thumbnailStrip()}
{fullscreenOverlay()} ); }