import { useState, useEffect, useRef } from 'react'; import { X, ChevronLeft, ChevronRight } from 'lucide-react'; import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'; import { VisuallyHidden } from '@radix-ui/react-visually-hidden'; import { Button } from '@/components/ui/button'; import { useIsMobile } from '@/hooks/use-mobile'; interface PhotoModalProps { photos: Array<{ id: string; url: string; filename?: string; caption?: string; }>; initialIndex: number; isOpen: boolean; onClose: () => void; } export function PhotoModal({ photos, initialIndex, isOpen, onClose }: PhotoModalProps) { const isMobile = useIsMobile(); const [currentIndex, setCurrentIndex] = useState(initialIndex); const [touchStart, setTouchStart] = useState(null); const [touchEnd, setTouchEnd] = useState(null); const imageRef = useRef(null); // Safety check: ensure photos array exists and is not empty if (!photos || photos.length === 0) { return null; } // Clamp currentIndex to valid bounds const safeIndex = Math.max(0, Math.min(currentIndex, photos.length - 1)); const currentPhoto = photos[safeIndex]; // Early return if currentPhoto is undefined if (!currentPhoto) { return null; } // Minimum swipe distance (in px) const minSwipeDistance = 50; const goToPrevious = () => { setCurrentIndex((prev) => (prev > 0 ? prev - 1 : photos.length - 1)); }; const goToNext = () => { setCurrentIndex((prev) => (prev < photos.length - 1 ? prev + 1 : 0)); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'ArrowLeft') goToPrevious(); if (e.key === 'ArrowRight') goToNext(); if (e.key === 'Escape') onClose(); }; const onTouchStart = (e: React.TouchEvent) => { setTouchEnd(null); setTouchStart(e.targetTouches[0].clientX); }; const onTouchMove = (e: React.TouchEvent) => { setTouchEnd(e.targetTouches[0].clientX); }; const onTouchEnd = () => { if (!touchStart || !touchEnd) return; const distance = touchStart - touchEnd; const isLeftSwipe = distance > minSwipeDistance; const isRightSwipe = distance < -minSwipeDistance; if (isLeftSwipe) { goToNext(); } else if (isRightSwipe) { goToPrevious(); } }; useEffect(() => { setCurrentIndex(initialIndex); }, [initialIndex]); return ( button]:hidden ${isMobile ? 'max-h-screen h-screen' : 'max-h-[90vh]'}`} aria-describedby={undefined}> Photo Viewer
{/* Close button */} {/* Header */}
{currentPhoto?.caption && (

{currentPhoto.caption}

)} {photos.length > 1 && (

{safeIndex + 1} of {photos.length}

)}
{/* Image */}
{currentPhoto?.caption
{/* Navigation */} {photos.length > 1 && !isMobile && ( <> )} {/* Mobile navigation dots */} {photos.length > 1 && isMobile && (
{photos.map((_, idx) => (
)}
); }