"use client"; import Image from "next/image"; import { useEffect, useState } from "react"; import { createPortal } from "react-dom"; import useEmblaCarousel from "embla-carousel-react"; import { Icon } from "@iconify/react"; interface Props { src: string; alt: string; width: number; height: number; className?: string; images?: string[]; } export default function ImageViewer({ src, alt, width, height, className, images = [] }: Props) { const [isOpen, setIsOpen] = useState(false); const [isVisible, setIsVisible] = useState(false); const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true, duration: 15 }); const [selectedIndex, setSelectedIndex] = useState(0); const [scrollSnaps, setScrollSnaps] = useState([]); const close = () => { setIsVisible(false); setTimeout(() => { setIsOpen(false); }, 300); }; useEffect(() => { if (isOpen) { // slight delay to trigger animation setTimeout(() => setIsVisible(true), 10); } }, [isOpen]); useEffect(() => { if (!emblaApi) return; // Keep order of images whilst opening at src prop const index = images.indexOf(src); if (index !== -1) { emblaApi.scrollTo(index, true); setSelectedIndex(index); } setScrollSnaps(emblaApi.scrollSnapList()); emblaApi.on("select", () => setSelectedIndex(emblaApi.selectedScrollSnap())); }, [emblaApi, images, src]); // Handle keyboard events useEffect(() => { if (!isOpen || !emblaApi) return; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "ArrowLeft") emblaApi.scrollPrev(); else if (event.key === "ArrowRight") emblaApi.scrollNext(); else if (event.key === "Escape") close(); }; window.addEventListener("keydown", handleKeyDown); return () => { window.removeEventListener("keydown", handleKeyDown); }; }, [isOpen, emblaApi]); const imagesMap = images.length === 0 ? [src] : images; return ( <> {/* not inserting pixelated image-rendering here because i thought it looked a bit weird */} {alt} setIsOpen(true)} className={`cursor-pointer ${className}`} /> {isOpen && createPortal(
{imagesMap.map((image, index) => (
{alt}
))}
{images.length > 1 && ( <> {/* Carousel counter */}
{selectedIndex + 1} / {images.length}
{/* Carousel buttons */} {/* Prev button */} {/* Next button */} {/* Carousel snaps */}
{scrollSnaps.map((_, index) => (
)}
, document.body, )} ); }