"use client"; import React, { useState, useRef, useEffect, useCallback } from "react"; import { motion, AnimatePresence } from "motion/react"; import { X, ZoomIn, ZoomOut, RotateCcw, Maximize2, Minimize2, ChevronRight, ChevronLeft, Move } from "lucide-react"; interface ProductImageZoomModalProps { isOpen: boolean; onClose: () => void; images: string[]; activeImage: string; onSelectImage: (image: string) => void; productName: string; } export default function ProductImageZoomModal({ isOpen, onClose, images, activeImage, onSelectImage, productName, }: ProductImageZoomModalProps) { const [scale, setScale] = useState(1); const [position, setPosition] = useState({ x: 0, y: 0 }); const [isDragging, setIsDragging] = useState(false); const [dragStart, setDragStart] = useState({ x: 0, y: 0 }); const [isFullscreen, setIsFullscreen] = useState(false); const containerRef = useRef(null); // Reset zoom & pan when image changes or modal opens const resetZoom = useCallback(() => { setScale(1); setPosition({ x: 0, y: 0 }); }, []); useEffect(() => { if (isOpen) { resetZoom(); document.body.style.overflow = "hidden"; } else { document.body.style.overflow = ""; } return () => { document.body.style.overflow = ""; }; }, [isOpen, activeImage, resetZoom]); const navigateImage = useCallback((direction: number) => { if (!images || images.length <= 1) return; const currentIndex = images.indexOf(activeImage); if (currentIndex === -1) return; let nextIndex = (currentIndex + direction) % images.length; if (nextIndex < 0) nextIndex = images.length - 1; onSelectImage(images[nextIndex]); resetZoom(); }, [images, activeImage, onSelectImage, resetZoom]); // Keyboard navigation & zoom shortcuts useEffect(() => { if (!isOpen) return; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { onClose(); } else if (e.key === "+" || e.key === "=") { setScale((prev) => Math.min(prev + 0.5, 4)); } else if (e.key === "-") { setScale((prev) => { const next = Math.max(prev - 0.5, 1); if (next === 1) setPosition({ x: 0, y: 0 }); return next; }); } else if (e.key === "0") { resetZoom(); } else if (e.key === "ArrowLeft") { navigateImage(1); } else if (e.key === "ArrowRight") { navigateImage(-1); } }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [isOpen, onClose, navigateImage, resetZoom]); const handleZoomIn = () => { setScale((prev) => Math.min(prev + 0.5, 4)); }; const handleZoomOut = () => { setScale((prev) => { const next = Math.max(prev - 0.5, 1); if (next === 1) setPosition({ x: 0, y: 0 }); return next; }); }; const handleWheel = (e: React.WheelEvent) => { e.preventDefault(); if (e.deltaY < 0) { setScale((prev) => Math.min(prev + 0.25, 4)); } else { setScale((prev) => { const next = Math.max(prev - 0.25, 1); if (next === 1) setPosition({ x: 0, y: 0 }); return next; }); } }; const handleMouseDown = (e: React.MouseEvent) => { if (scale <= 1) return; setIsDragging(true); setDragStart({ x: e.clientX - position.x, y: e.clientY - position.y }); }; const handleMouseMove = (e: React.MouseEvent) => { if (!isDragging || scale <= 1) return; setPosition({ x: e.clientX - dragStart.x, y: e.clientY - dragStart.y, }); }; const handleMouseUp = () => { setIsDragging(false); }; // Touch drag for mobile const handleTouchStart = (e: React.TouchEvent) => { if (scale <= 1 || e.touches.length !== 1) return; setIsDragging(true); setDragStart({ x: e.touches[0].clientX - position.x, y: e.touches[0].clientY - position.y, }); }; const handleTouchMove = (e: React.TouchEvent) => { if (!isDragging || scale <= 1 || e.touches.length !== 1) return; setPosition({ x: e.touches[0].clientX - dragStart.x, y: e.touches[0].clientY - dragStart.y, }); }; const handleTouchEnd = () => { setIsDragging(false); }; const toggleFullscreen = () => { if (!document.fullscreenElement) { containerRef.current?.requestFullscreen?.().catch(() => {}); setIsFullscreen(true); } else { document.exitFullscreen?.().catch(() => {}); setIsFullscreen(false); } }; if (!isOpen) return null; const currentImg = activeImage || images[0]; return (
{/* Top Control Bar */}
{productName} {images.length > 1 ? `${images.indexOf(currentImg) + 1} از ${images.length}` : 'تصویر با کیفیت اصلی'}
{/* Floating Zoom & Tool Controls */}
{Math.round(scale * 100)}% {scale > 1 && ( )}
{/* Center Viewer Canvas with Pan and Zoom */}
{/* Navigation Next / Prev Buttons */} {images.length > 1 && ( <> )} {/* Interactive Zoomable Image Element */}
1 ? "cursor-grab duration-150" : "cursor-zoom-in duration-200" }`} style={{ transform: `translate(${position.x}px, ${position.y}px) scale(${scale})`, transformOrigin: "center center", }} onClick={(e) => { if (scale === 1) { setScale(2); } }} > {/* Raw unoptimized img element for 100% original full-resolution & clarity */} {productName}
{/* Drag instruction helper for zoomed view */} {scale > 1 && (
برای جابجایی بکشید (Drag) • برای زوم اسکرول کنید
)}
{/* Bottom Thumbnail Strip */} {images.length > 1 && (
{images.map((img, idx) => { const isSelected = img === currentImg; return ( ); })}
)}
); }