Files
klz-cables.com/components/Lightbox.tsx
Marc Mintel 3cab376cd1
All checks were successful
Build & Deploy KLZ Cables / build-and-deploy (push) Successful in 3m31s
lightbox
2026-01-27 00:21:03 +01:00

105 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import React, { useEffect, useState, useCallback } from 'react';
import Image from 'next/image';
import { createPortal } from 'react-dom';
interface LightboxProps {
isOpen: boolean;
images: string[];
initialIndex: number;
onClose: () => void;
}
export default function Lightbox({ isOpen, images, initialIndex, onClose }: LightboxProps) {
const [currentIndex, setCurrentIndex] = useState(initialIndex);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
return () => setMounted(false);
}, []);
const prevImage = useCallback(() => {
setCurrentIndex((prev) => (prev === 0 ? images.length - 1 : prev - 1));
}, [images.length]);
const nextImage = useCallback(() => {
setCurrentIndex((prev) => (prev === images.length - 1 ? 0 : prev + 1));
}, [images.length]);
useEffect(() => {
if (!isOpen) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
if (e.key === 'ArrowLeft') prevImage();
if (e.key === 'ArrowRight') nextImage();
};
// Lock scroll
const originalStyle = window.getComputedStyle(document.body).overflow;
document.body.style.overflow = 'hidden';
window.addEventListener('keydown', handleKeyDown);
return () => {
document.body.style.overflow = originalStyle;
window.removeEventListener('keydown', handleKeyDown);
};
}, [isOpen, onClose, prevImage, nextImage]);
if (!isOpen || !mounted) return null;
return createPortal(
<div
className="fixed inset-0 z-[99999] bg-black/95 backdrop-blur-sm flex items-center justify-center p-4 animate-in fade-in duration-300"
role="dialog"
aria-modal="true"
>
<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>
{/* Backdrop click to close */}
<div
className="absolute inset-0 z-[9998]"
onClick={onClose}
/>
</div>,
document.body
);
}