All checks were successful
Build & Deploy KLZ Cables / build-and-deploy (push) Successful in 3m39s
92 lines
2.9 KiB
TypeScript
92 lines
2.9 KiB
TypeScript
'use client';
|
||
|
||
import React, { useEffect } from 'react';
|
||
import Image from 'next/image';
|
||
|
||
interface LightboxProps {
|
||
isOpen: boolean;
|
||
images: string[];
|
||
initialIndex: number;
|
||
onClose: () => void;
|
||
}
|
||
|
||
export default function Lightbox({ isOpen, images, initialIndex, onClose }: LightboxProps) {
|
||
const [currentIndex, setCurrentIndex] = React.useState(initialIndex);
|
||
|
||
useEffect(() => {
|
||
if (isOpen) {
|
||
document.body.style.overflow = 'hidden';
|
||
} else {
|
||
document.body.style.overflow = 'unset';
|
||
}
|
||
|
||
return () => {
|
||
document.body.style.overflow = 'unset';
|
||
};
|
||
}, [isOpen]);
|
||
|
||
if (!isOpen) return null;
|
||
|
||
const prevImage = () => {
|
||
setCurrentIndex((prev) => (prev === 0 ? images.length - 1 : prev - 1));
|
||
};
|
||
|
||
const nextImage = () => {
|
||
setCurrentIndex((prev) => (prev === images.length - 1 ? 0 : prev + 1));
|
||
};
|
||
|
||
return (
|
||
<div className="fixed inset-0 z-[9999] bg-black/90 flex items-center justify-center p-4 animate-in fade-in duration-300">
|
||
<button
|
||
onClick={onClose}
|
||
className="absolute top-6 right-6 text-white text-3xl hover:text-[#011dff] transition-colors z-[10000] rounded-full w-12 h-12 flex items-center justify-center hover:bg-white/10"
|
||
aria-label="Close lightbox"
|
||
>
|
||
×
|
||
</button>
|
||
|
||
<button
|
||
onClick={prevImage}
|
||
className="absolute left-6 top-1/2 -translate-y-1/2 text-white text-4xl hover:text-[#011dff] transition-colors w-12 h-12 flex items-center justify-center hover:bg-white/10 rounded-full z-[10000]"
|
||
aria-label="Previous image"
|
||
>
|
||
‹
|
||
</button>
|
||
|
||
<button
|
||
onClick={nextImage}
|
||
className="absolute right-6 top-1/2 -translate-y-1/2 text-white text-4xl hover:text-[#011dff] transition-colors w-12 h-12 flex items-center justify-center hover:bg-white/10 rounded-full z-[10000]"
|
||
aria-label="Next image"
|
||
>
|
||
›
|
||
</button>
|
||
|
||
<div className="relative max-w-6xl max-h-[90vh] w-full h-full flex items-center justify-center animate-in slide-in-from-bottom-4 duration-500">
|
||
<Image
|
||
src={images[currentIndex]}
|
||
alt={`Gallery image ${currentIndex + 1}`}
|
||
fill
|
||
className="object-contain max-h-[90vh] max-w-full shadow-2xl"
|
||
unoptimized
|
||
/>
|
||
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 bg-black/60 backdrop-blur-md text-white px-6 py-3 rounded-full text-sm font-medium border border-white/10">
|
||
{currentIndex + 1} / {images.length}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Keyboard navigation */}
|
||
{isOpen && (
|
||
<div
|
||
className="fixed inset-0 z-[9998]"
|
||
tabIndex={-1}
|
||
onKeyDown={(e) => {
|
||
if (e.key === 'Escape') onClose();
|
||
if (e.key === 'ArrowLeft') prevImage();
|
||
if (e.key === 'ArrowRight') nextImage();
|
||
}}
|
||
onClick={onClose}
|
||
/>
|
||
)}
|
||
</div>
|
||
);
|
||
} |