import React, { useState, useEffect } from 'react'; import { X, ChevronLeft, ChevronRight, Download, ZoomIn, ZoomOut } from 'lucide-react'; import type { Photo } from '../../types'; import { useDownloadPhoto } from '../../hooks/useGallery'; import { AuthenticatedImage } from '../common'; interface PhotoLightboxProps { photos: Photo[]; initialIndex: number; onClose: () => void; slug: string; } export const PhotoLightbox: React.FC = ({ photos, initialIndex, onClose, slug, }) => { const [currentIndex, setCurrentIndex] = useState(initialIndex); const [zoom, setZoom] = useState(1); const [isDragging, setIsDragging] = useState(false); const [dragStart, setDragStart] = useState({ x: 0, y: 0 }); const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); const [touchDistance, setTouchDistance] = useState(null); const downloadPhotoMutation = useDownloadPhoto(); const currentPhoto = photos[currentIndex]; useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { switch (e.key) { case 'Escape': onClose(); break; case 'ArrowLeft': goToPrevious(); break; case 'ArrowRight': goToNext(); break; case '+': case '=': handleZoomIn(); break; case '-': case '_': handleZoomOut(); break; case 'd': case 'D': handleDownload(); break; } }; document.addEventListener('keydown', handleKeyDown); document.body.style.overflow = 'hidden'; return () => { document.removeEventListener('keydown', handleKeyDown); document.body.style.overflow = ''; }; }, [currentIndex]); const goToPrevious = () => { setCurrentIndex((prev) => (prev > 0 ? prev - 1 : photos.length - 1)); resetZoom(); }; const goToNext = () => { setCurrentIndex((prev) => (prev < photos.length - 1 ? prev + 1 : 0)); resetZoom(); }; const resetZoom = () => { setZoom(1); setDragOffset({ x: 0, y: 0 }); }; const handleZoomIn = () => { setZoom((prev) => Math.min(prev + 0.5, 3)); }; const handleZoomOut = () => { setZoom((prev) => Math.max(prev - 0.5, 1)); if (zoom - 0.5 <= 1) { setDragOffset({ x: 0, y: 0 }); } }; const handleDownload = () => { downloadPhotoMutation.mutate({ slug, photoId: currentPhoto.id, filename: currentPhoto.filename, }); }; const handleMouseDown = (e: React.MouseEvent) => { if (zoom > 1) { setIsDragging(true); setDragStart({ x: e.clientX - dragOffset.x, y: e.clientY - dragOffset.y }); } }; const handleMouseMove = (e: React.MouseEvent) => { if (isDragging && zoom > 1) { setDragOffset({ x: e.clientX - dragStart.x, y: e.clientY - dragStart.y, }); } }; const handleMouseUp = () => { setIsDragging(false); }; const handleImageClick = (e: React.MouseEvent) => { // Only close if clicking the background, not the image if (e.target === e.currentTarget) { onClose(); } }; // Touch event handlers for pinch-to-zoom const handleTouchStart = (e: React.TouchEvent) => { if (e.touches.length === 2) { const touch1 = e.touches[0]; const touch2 = e.touches[1]; const distance = Math.hypot( touch2.clientX - touch1.clientX, touch2.clientY - touch1.clientY ); setTouchDistance(distance); } }; const handleTouchMove = (e: React.TouchEvent) => { if (e.touches.length === 2 && touchDistance !== null) { const touch1 = e.touches[0]; const touch2 = e.touches[1]; const newDistance = Math.hypot( touch2.clientX - touch1.clientX, touch2.clientY - touch1.clientY ); const scale = newDistance / touchDistance; const newZoom = Math.max(1, Math.min(3, zoom * scale)); setZoom(newZoom); setTouchDistance(newDistance); } }; const handleTouchEnd = () => { setTouchDistance(null); }; return (
{/* Close button */} {/* Navigation buttons */} {/* Bottom toolbar */}

{currentIndex + 1} / {photos.length}

{Math.round(zoom * 100)}%
{/* Image container */}
1 ? (isDragging ? 'grabbing' : 'grab') : 'default' }} >
{/* Touch/swipe indicators for mobile */}
Swipe to navigate
); };