163 lines
6.2 KiB
TypeScript
163 lines
6.2 KiB
TypeScript
"use client";
|
|
|
|
import { useTranslations } from 'next-intl';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { Instagram } from '@/components/InstagramIcon';
|
|
import { useState, useEffect } from 'react';
|
|
import { X, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
|
|
export default function Gallery() {
|
|
const t = useTranslations('Gallery');
|
|
|
|
const images = Array.from({ length: 12 }).map((_, i) => ({
|
|
id: i,
|
|
src: `https://picsum.photos/600/${400 + (i % 5) * 50}?random=${30 + i}`,
|
|
alt: `Gallery ${i + 1}`,
|
|
}));
|
|
|
|
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
|
|
|
|
// Close lightbox on escape key
|
|
useEffect(() => {
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
if (e.key === 'Escape') setSelectedIndex(null);
|
|
if (e.key === 'ArrowRight' && selectedIndex !== null) {
|
|
setSelectedIndex((prev) => (prev !== null ? (prev + 1) % images.length : null));
|
|
}
|
|
if (e.key === 'ArrowLeft' && selectedIndex !== null) {
|
|
setSelectedIndex((prev) => (prev !== null ? (prev - 1 + images.length) % images.length : null));
|
|
}
|
|
};
|
|
window.addEventListener('keydown', handleKeyDown);
|
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
}, [selectedIndex, images.length]);
|
|
|
|
return (
|
|
<section id="gallery" className="py-24 bg-white text-midnight">
|
|
<div className="container mx-auto px-6 lg:px-12">
|
|
|
|
{/* Header */}
|
|
<div className="flex flex-col md:flex-row justify-between items-end mb-10">
|
|
<motion.div
|
|
initial={{ opacity: 0, x: -20 }}
|
|
whileInView={{ opacity: 1, x: 0 }}
|
|
viewport={{ once: true }}
|
|
>
|
|
<span className="text-xs tracking-[0.4em] uppercase font-black text-coral mb-3 block">
|
|
— Fotoğraflar
|
|
</span>
|
|
<h2 className="font-heading text-5xl md:text-6xl font-black text-midnight">
|
|
{t('title')}
|
|
</h2>
|
|
</motion.div>
|
|
|
|
<motion.a
|
|
initial={{ opacity: 0, x: 20 }}
|
|
whileInView={{ opacity: 1, x: 0 }}
|
|
viewport={{ once: true }}
|
|
href="https://www.instagram.com/kozmosbeach/"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-center gap-2 px-5 py-2.5 rounded-full border border-coral/30 text-coral font-bold text-sm hover:bg-coral hover:text-white transition-all"
|
|
>
|
|
<Instagram size={15} />
|
|
{t('follow_instagram')}
|
|
</motion.a>
|
|
</div>
|
|
|
|
{/* Masonry grid */}
|
|
<div className="columns-1 sm:columns-2 md:columns-3 lg:columns-4 gap-3 space-y-3">
|
|
{images.map((img, index) => (
|
|
<motion.div
|
|
key={img.id}
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ delay: (index % 4) * 0.07 }}
|
|
onClick={() => setSelectedIndex(index)}
|
|
className="relative rounded-2xl overflow-hidden group break-inside-avoid cursor-pointer"
|
|
>
|
|
<img
|
|
src={img.src}
|
|
alt={img.alt}
|
|
className="w-full h-auto object-cover transition-transform duration-700 group-hover:scale-105"
|
|
loading="lazy"
|
|
/>
|
|
{/* Coral gradient overlay */}
|
|
<div
|
|
className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
|
style={{
|
|
background:
|
|
'linear-gradient(135deg, rgba(255,90,54,0.55), rgba(0,191,165,0.4))',
|
|
}}
|
|
/>
|
|
{/* Plus icon */}
|
|
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
|
<span className="text-white text-4xl font-thin select-none">+</span>
|
|
</div>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Lightbox Overlay */}
|
|
<AnimatePresence>
|
|
{selectedIndex !== null && (
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
className="fixed inset-0 z-[100] flex items-center justify-center bg-midnight/95 backdrop-blur-md"
|
|
>
|
|
{/* Close Button */}
|
|
<button
|
|
onClick={() => setSelectedIndex(null)}
|
|
className="absolute top-6 right-6 z-[110] text-white/70 hover:text-white transition-colors"
|
|
>
|
|
<X size={36} />
|
|
</button>
|
|
|
|
{/* Left/Right Navigation */}
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
setSelectedIndex((selectedIndex - 1 + images.length) % images.length);
|
|
}}
|
|
className="absolute left-4 md:left-10 z-[110] p-3 rounded-full bg-white/10 text-white hover:bg-white/20 transition-all"
|
|
>
|
|
<ChevronLeft size={30} />
|
|
</button>
|
|
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
setSelectedIndex((selectedIndex + 1) % images.length);
|
|
}}
|
|
className="absolute right-4 md:right-10 z-[110] p-3 rounded-full bg-white/10 text-white hover:bg-white/20 transition-all"
|
|
>
|
|
<ChevronRight size={30} />
|
|
</button>
|
|
|
|
{/* Image */}
|
|
<motion.div
|
|
key={selectedIndex}
|
|
initial={{ opacity: 0, scale: 0.9 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
exit={{ opacity: 0, scale: 0.9 }}
|
|
transition={{ type: "spring", damping: 25, stiffness: 300 }}
|
|
className="relative max-w-5xl w-full h-[80vh] px-4 md:px-24"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<img
|
|
src={images[selectedIndex].src}
|
|
alt={images[selectedIndex].alt}
|
|
className="w-full h-full object-contain drop-shadow-2xl"
|
|
/>
|
|
</motion.div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</section>
|
|
);
|
|
}
|