107 lines
4.8 KiB
TypeScript
107 lines
4.8 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { GlassMagnifier } from 'react-image-magnifiers';
|
|
|
|
interface ZoomableImageProps {
|
|
src: string;
|
|
alt: string;
|
|
className?: string;
|
|
magnifierSize?: string;
|
|
zoomLevel?: number;
|
|
children?: React.ReactNode;
|
|
disabled?: boolean; // New prop to prevent zoom on click
|
|
}
|
|
|
|
export const ZoomableImage: React.FC<ZoomableImageProps> = ({
|
|
src,
|
|
alt,
|
|
className = "",
|
|
magnifierSize = "30%",
|
|
zoomLevel = 2.5,
|
|
children,
|
|
disabled = false
|
|
}) => {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
const toggleModal = (e: React.MouseEvent) => {
|
|
if (disabled) return; // Don't open if disabled
|
|
e.stopPropagation();
|
|
setIsOpen(!isOpen);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{/* Thumbnail trigger */}
|
|
<div
|
|
className={`cursor-zoom-in relative group ${className}`}
|
|
onClick={toggleModal}
|
|
title="Click to zoom"
|
|
>
|
|
<img
|
|
src={src}
|
|
alt={alt}
|
|
className="w-full h-full object-cover" // Changed to object-cover to match Home usage expectation or passed className? Actually lets keep original but fix usage if needed. Wait, original was w-full h-auto object-contain.
|
|
/>
|
|
{children}
|
|
{!children && (
|
|
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-colors flex items-center justify-center opacity-0 group-hover:opacity-100">
|
|
<span className="bg-white/80 text-stone-900 p-2 rounded-full shadow-lg backdrop-blur-sm">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607ZM10.5 7.5v6m3-3h-6" />
|
|
</svg>
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Modal */}
|
|
{isOpen && (
|
|
<div
|
|
className="fixed inset-0 z-[9999] bg-stone-900/95 backdrop-blur-md flex items-center justify-center p-8 animate-in fade-in duration-200"
|
|
onClick={() => setIsOpen(false)}
|
|
>
|
|
<div className="relative w-full h-full max-w-7xl flex items-center justify-center" onClick={(e) => e.stopPropagation()}>
|
|
<button
|
|
onClick={() => setIsOpen(false)}
|
|
className="absolute -top-6 -right-6 md:top-0 md:-right-12 text-white/50 hover:text-white transition-colors p-2 z-50"
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-8 h-8">
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12" />
|
|
</svg>
|
|
</button>
|
|
|
|
<div className="w-full h-full flex items-center justify-center rounded-lg overflow-hidden">
|
|
<GlassMagnifier
|
|
imageSrc={src}
|
|
imageAlt={alt}
|
|
magnifierSize={magnifierSize}
|
|
magnifierBorderSize={2}
|
|
magnifierBorderColor="rgba(255, 255, 255, 0.5)"
|
|
square={false}
|
|
allowOverflow={true}
|
|
style={{
|
|
width: 'auto',
|
|
height: 'auto',
|
|
maxWidth: '100%',
|
|
maxHeight: '90vh',
|
|
display: 'block'
|
|
}}
|
|
imageStyle={{
|
|
width: 'auto',
|
|
height: 'auto',
|
|
maxWidth: '100%',
|
|
maxHeight: '90vh',
|
|
objectFit: 'contain'
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 bg-black/50 text-white px-4 py-2 rounded-full text-xs font-medium backdrop-blur-md pointer-events-none z-50">
|
|
Hover to magnify • Click outside to close
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
};
|