hotschpotsh/Pottery-website/components/GallerySection.tsx

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;