blob: 0b657cf4e314cbf424927bc0c0325803251d6266 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
import React, { useState, useEffect } from 'react';
interface ImageItems {
images: Array<{
src: string;
alt?: string;
id?: string | number;
}>;
}
export default function ImageGalleryGrid({ images }: ImageItems) {
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
const closeModal = () => setSelectedIndex(null);
const prev = (e?: React.MouseEvent) => {
e?.stopPropagation();
setSelectedIndex((i) => (i !== null && i > 0 ? i - 1 : i));
};
const next = (e?: React.MouseEvent) => {
e?.stopPropagation();
setSelectedIndex((i) => (i !== null && i < images.length - 1 ? i + 1 : i));
};
useEffect(() => {
function handleKeyDown(event: KeyboardEvent) {
if (selectedIndex === null) return;
if (event.key === 'ArrowLeft') {
prev();
} else if (event.key === 'ArrowRight') {
next();
}
}
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [selectedIndex, images.length]);
return (
<>
<div className="grid gap-2 grid-cols-4 max-sm:grid-cols-3 p-4">
{images.map((image, idx) => (
<button
key={image.id ?? image.src}
className="relative overflow-hidden rounded-xl shadow-lg aspect-square group cursor-pointer"
onClick={() => setSelectedIndex(idx)}
>
<img
src={image.src}
alt={image.alt || 'Gallery image'}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
/>
</button>
))}
</div>
{/* Overlay */}
{selectedIndex !== null && (
<div
className="fixed inset-0 bg-white/50 dark:bg-black/50 backdrop-blur-sm flex items-center justify-center z-50"
onClick={closeModal}
role="dialog"
aria-modal="true"
>
{/* Prev arrow */}
<button
onClick={prev}
className="absolute left-4 text-black dark:text-white text-4xl disabled:opacity-30"
disabled={selectedIndex === 0}
>
←
</button>
<img
src={images[selectedIndex].src}
alt={images[selectedIndex].alt || 'Enlarged gallery'}
className="w-2/3 h-2/3 object-contain rounded-lg"
/>
{/* Next arrow */}
<button
onClick={next}
className="absolute right-4 text-black dark:text-white text-4xl disabled:opacity-30"
disabled={selectedIndex === images.length - 1}
aria-label='Next image'
>
→
</button>
<button
onClick={(e) => {
e.stopPropagation();
closeModal();
}}
className="absolute top-4 right-4 text-black dark:text-white text-3xl"
>
×
</button>
</div>
)}
</>
);
}
|