"use client"; import { useState } from "react"; import Image from "next/image"; import { motion, AnimatePresence } from "framer-motion"; import { X, ChevronLeft, ChevronRight, ZoomIn } from "lucide-react"; import { cn } from "@/lib/utils"; // ============================================================================= // GALLERY // Image gallery with lightbox // ============================================================================= interface GalleryImage { src: string; alt: string; width?: number; height?: number; caption?: string; category?: string; } interface GalleryProps { images: GalleryImage[]; columns?: 2 | 3 | 4; gap?: "sm" | "md" | "lg"; variant?: "grid" | "masonry"; lightbox?: boolean; className?: string; } export function Gallery({ images, columns = 3, gap = "md", variant = "grid", lightbox = true, className, }: GalleryProps) { const [selectedIndex, setSelectedIndex] = useState(null); const colStyles = { 2: "columns-1 md:columns-2", 3: "columns-1 md:columns-2 lg:columns-3", 4: "columns-1 md:columns-2 lg:columns-4", }; const gapStyles = { sm: "gap-2", md: "gap-4", lg: "gap-6", }; const gridColStyles = { 2: "grid-cols-1 md:grid-cols-2", 3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3", 4: "grid-cols-1 md:grid-cols-2 lg:grid-cols-4", }; const openLightbox = (index: number) => { if (lightbox) { setSelectedIndex(index); document.body.style.overflow = "hidden"; } }; const closeLightbox = () => { setSelectedIndex(null); document.body.style.overflow = ""; }; const goNext = () => { if (selectedIndex !== null) { setSelectedIndex((selectedIndex + 1) % images.length); } }; const goPrev = () => { if (selectedIndex !== null) { setSelectedIndex((selectedIndex - 1 + images.length) % images.length); } }; if (variant === "masonry") { return ( <>
{images.map((image, index) => (
openLightbox(index)} > {image.alt}
{image.caption && (

{image.caption}

)}
))}
{lightbox && ( )} ); } // Grid variant return ( <>
{images.map((image, index) => (
openLightbox(index)} > {image.alt}
))}
{lightbox && ( )} ); } // ============================================================================= // LIGHTBOX // Fullscreen image viewer // ============================================================================= interface LightboxProps { images: GalleryImage[]; selectedIndex: number | null; onClose: () => void; onNext: () => void; onPrev: () => void; } function Lightbox({ images, selectedIndex, onClose, onNext, onPrev, }: LightboxProps) { if (selectedIndex === null) return null; const image = images[selectedIndex]; return ( {/* Close button */} {/* Navigation */} {images.length > 1 && ( <> )} {/* Image */} e.stopPropagation()} > {image.alt} {image.caption && (

{image.caption}

)}
{/* Counter */}
{selectedIndex + 1} / {images.length}
); } // ============================================================================= // FILTERED GALLERY // Gallery with category filters // ============================================================================= interface FilteredGalleryProps { images: GalleryImage[]; columns?: 2 | 3 | 4; className?: string; } export function FilteredGallery({ images, columns = 3, className, }: FilteredGalleryProps) { const [activeCategory, setActiveCategory] = useState(null); const categories = Array.from( new Set(images.map((img) => img.category).filter(Boolean)) ) as string[]; const filteredImages = activeCategory ? images.filter((img) => img.category === activeCategory) : images; return (
{/* Filters */} {categories.length > 0 && (
{categories.map((category) => ( ))}
)} {/* Gallery */}
); }