179 lines
8.1 KiB
TypeScript
179 lines
8.1 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { GALLERY_IMAGES } from '../constants';
|
|
|
|
interface GalleryImage {
|
|
src: string;
|
|
likes: number;
|
|
comments: number;
|
|
caption: string;
|
|
}
|
|
|
|
const GallerySection: React.FC = () => {
|
|
const [selectedImage, setSelectedImage] = useState<GalleryImage | null>(null);
|
|
|
|
// Double the images for seamless infinite scroll
|
|
const duplicatedImages = [...GALLERY_IMAGES, ...GALLERY_IMAGES] as GalleryImage[];
|
|
|
|
return (
|
|
<>
|
|
<section className="py-20 bg-white dark:bg-background-dark overflow-hidden">
|
|
<div className="max-w-[1920px] mx-auto px-4">
|
|
<motion.div
|
|
className="flex justify-between items-center mb-8 px-2"
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ duration: 0.6 }}
|
|
>
|
|
<div className="flex items-center gap-4">
|
|
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-purple-500 via-pink-500 to-orange-400 p-[2px]">
|
|
<div className="w-full h-full rounded-full bg-white dark:bg-background-dark flex items-center justify-center">
|
|
<span className="font-display text-lg">H</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h4 className="font-display text-xl text-text-main dark:text-white">@hotchpotsh_ceramics</h4>
|
|
<p className="text-xs text-text-muted">24.8k followers</p>
|
|
</div>
|
|
</div>
|
|
<a className="px-6 py-2 border border-text-main dark:border-white text-xs uppercase tracking-widest text-text-main dark:text-white hover:bg-text-main hover:text-white dark:hover:bg-white dark:hover:text-black transition-all duration-300 rounded-full" href="#">
|
|
Follow
|
|
</a>
|
|
</motion.div>
|
|
|
|
{/* Infinite Carousel */}
|
|
<div className="relative group overflow-hidden">
|
|
<style>{`
|
|
@keyframes marquee {
|
|
0% { transform: translateX(0); }
|
|
100% { transform: translateX(-${GALLERY_IMAGES.length * 304}px); }
|
|
}
|
|
.animate-marquee {
|
|
animation: marquee 40s linear infinite;
|
|
}
|
|
.animate-marquee:hover {
|
|
animation-play-state: paused;
|
|
}
|
|
`}</style>
|
|
<div className="flex gap-4 animate-marquee w-max py-4">
|
|
{duplicatedImages.map((img, idx) => (
|
|
<motion.div
|
|
key={idx}
|
|
className="relative flex-shrink-0 w-72 h-72 overflow-hidden cursor-pointer rounded-lg"
|
|
whileHover={{ scale: 1.02 }}
|
|
onClick={() => setSelectedImage(img)}
|
|
>
|
|
<img
|
|
alt={img.caption}
|
|
className="w-full h-full object-cover"
|
|
src={img.src}
|
|
/>
|
|
{/* Instagram-style hover overlay */}
|
|
<motion.div
|
|
className="absolute inset-0 bg-black/50 flex items-center justify-center gap-8 text-white"
|
|
initial={{ opacity: 0 }}
|
|
whileHover={{ opacity: 1 }}
|
|
transition={{ duration: 0.2 }}
|
|
>
|
|
<div className="flex items-center gap-2">
|
|
<span className="material-symbols-outlined" style={{ fontVariationSettings: "'FILL' 1" }}>favorite</span>
|
|
<span className="font-bold">{img.likes.toLocaleString()}</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<span className="material-symbols-outlined">chat_bubble</span>
|
|
<span className="font-bold">{img.comments}</span>
|
|
</div>
|
|
</motion.div>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Lightbox Modal */}
|
|
<AnimatePresence>
|
|
{
|
|
selectedImage && (
|
|
<motion.div
|
|
className="fixed inset-0 bg-black/90 z-50 flex items-center justify-center p-4"
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
onClick={() => setSelectedImage(null)}
|
|
>
|
|
<motion.div
|
|
className="relative max-w-4xl w-full bg-white dark:bg-stone-900 rounded-xl overflow-hidden flex flex-col md:flex-row"
|
|
initial={{ scale: 0.9, opacity: 0 }}
|
|
animate={{ scale: 1, opacity: 1 }}
|
|
exit={{ scale: 0.9, opacity: 0 }}
|
|
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
{/* Image */}
|
|
<div className="md:w-2/3 aspect-square md:aspect-auto">
|
|
<img
|
|
src={selectedImage.src}
|
|
alt={selectedImage.caption}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
</div>
|
|
|
|
{/* Side panel */}
|
|
<div className="md:w-1/3 p-6 flex flex-col">
|
|
{/* Header */}
|
|
<div className="flex items-center gap-3 pb-4 border-b border-stone-200 dark:border-stone-700">
|
|
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-purple-500 via-pink-500 to-orange-400 p-[2px]">
|
|
<div className="w-full h-full rounded-full bg-white dark:bg-stone-900 flex items-center justify-center">
|
|
<span className="font-display text-sm">H</span>
|
|
</div>
|
|
</div>
|
|
<span className="font-bold text-sm text-text-main dark:text-white">hotchpotsh_ceramics</span>
|
|
</div>
|
|
|
|
{/* Caption */}
|
|
<div className="flex-1 py-4">
|
|
<p className="text-sm text-text-main dark:text-white">
|
|
<span className="font-bold">hotchpotsh_ceramics</span> {selectedImage.caption}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Actions */}
|
|
<div className="border-t border-stone-200 dark:border-stone-700 pt-4">
|
|
<div className="flex gap-4 mb-4">
|
|
<button className="hover:scale-110 transition-transform">
|
|
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white" style={{ fontVariationSettings: "'FILL' 1" }}>favorite</span>
|
|
</button>
|
|
<button className="hover:scale-110 transition-transform">
|
|
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white">chat_bubble</span>
|
|
</button>
|
|
<button className="hover:scale-110 transition-transform">
|
|
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white">send</span>
|
|
</button>
|
|
<button className="hover:scale-110 transition-transform ml-auto">
|
|
<span className="material-symbols-outlined text-2xl text-text-main dark:text-white">bookmark</span>
|
|
</button>
|
|
</div>
|
|
<p className="text-sm font-bold text-text-main dark:text-white">{selectedImage.likes.toLocaleString()} likes</p>
|
|
<p className="text-xs text-text-muted mt-1">{selectedImage.comments} comments</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Close button */}
|
|
<button
|
|
className="absolute top-4 right-4 text-white bg-black/50 rounded-full p-2 hover:bg-black/70 transition-colors"
|
|
onClick={() => setSelectedImage(null)}
|
|
>
|
|
<span className="material-symbols-outlined">close</span>
|
|
</button>
|
|
</motion.div>
|
|
</motion.div>
|
|
)
|
|
}
|
|
</AnimatePresence >
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default GallerySection; |