Final
This commit is contained in:
176
Pottery-website/components/InstagramFeed.tsx
Normal file
176
Pottery-website/components/InstagramFeed.tsx
Normal file
@@ -0,0 +1,176 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
instgrm?: {
|
||||
Embeds: {
|
||||
process: () => void;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const posts = [
|
||||
'https://www.instagram.com/p/DSOFijljukL/',
|
||||
'https://www.instagram.com/p/DSGh7rMjVes/',
|
||||
'https://www.instagram.com/p/DRIIUECj4f6/',
|
||||
'https://www.instagram.com/p/DJOvmvcIoo7/',
|
||||
'https://www.instagram.com/p/DJOu5b7IL4t/',
|
||||
'https://www.instagram.com/p/DIQnhO0oJgw/',
|
||||
'https://www.instagram.com/p/DIJUVqbI4EH/',
|
||||
'https://www.instagram.com/p/DHlvDNyIDRa/',
|
||||
'https://www.instagram.com/p/DHlub_iojwv/',
|
||||
'https://www.instagram.com/p/DJOvdVLIZpM/',
|
||||
];
|
||||
|
||||
const InstagramFeed: React.FC = () => {
|
||||
const [selectedPost, setSelectedPost] = useState<string | null>(null);
|
||||
|
||||
// Double the list for seamless infinite marquee scroll
|
||||
const duplicatedPosts = [...posts, ...posts];
|
||||
|
||||
useEffect(() => {
|
||||
// Process Instagram embeds whenever the component mounts or the lightbox opens
|
||||
if (window.instgrm) {
|
||||
window.instgrm.Embeds.process();
|
||||
} else {
|
||||
const script = document.createElement('script');
|
||||
script.src = '//www.instagram.com/embed.js';
|
||||
script.async = true;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
}, [selectedPost]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="py-24 px-6 md:px-12 bg-stone-50 dark:bg-stone-900 overflow-hidden">
|
||||
<div className="max-w-[1400px] mx-auto">
|
||||
<span className="block font-body text-xs uppercase tracking-[0.3em] text-stone-400 mb-6">
|
||||
Follow Along
|
||||
</span>
|
||||
<div className="flex items-end justify-between mb-16 px-2">
|
||||
<h2 className="font-display text-4xl md:text-5xl text-text-main dark:text-white leading-none">
|
||||
From the Studio
|
||||
</h2>
|
||||
<a
|
||||
href="https://www.instagram.com/knuth.ceramics"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-xs font-bold uppercase tracking-widest text-stone-500 hover:text-stone-900 dark:hover:text-white transition-colors"
|
||||
>
|
||||
@knuth.ceramics →
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Infinite Carousel */}
|
||||
<div className="relative group overflow-hidden">
|
||||
<style>{`
|
||||
@keyframes marquee {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(-${posts.length * 342}px); /* 326px width + 16px gap */ }
|
||||
}
|
||||
.animate-marquee {
|
||||
animation: marquee 50s linear infinite;
|
||||
}
|
||||
.animate-marquee:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
`}</style>
|
||||
<div className="flex gap-4 animate-marquee w-max py-4">
|
||||
{duplicatedPosts.map((permalink, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="relative flex-shrink-0 w-[326px] overflow-hidden rounded-[8px] group/item cursor-pointer bg-white"
|
||||
>
|
||||
{/* Invisible Overlay to capture clicks.
|
||||
Because iframes block events, we put a div above it.
|
||||
On hover it reveals a subtle mask to indicate interactivity. */}
|
||||
<div
|
||||
className="absolute inset-0 z-10 bg-black/0 group-hover/item:bg-black/50 transition-colors duration-300 flex flex-col items-center justify-center opacity-0 group-hover/item:opacity-100"
|
||||
onClick={() => setSelectedPost(permalink)}
|
||||
>
|
||||
<p className="text-white font-display text-lg px-4 text-center font-bold drop-shadow-md">
|
||||
View Post
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* The Instagram Embed itself.
|
||||
By omitting data-instgrm-captioned we hide the caption/hashtags directly. */}
|
||||
<div className="pointer-events-none">
|
||||
<blockquote
|
||||
className="instagram-media"
|
||||
data-instgrm-permalink={`${permalink}?utm_source=ig_embed&utm_campaign=loading`}
|
||||
data-instgrm-version="14"
|
||||
style={{
|
||||
background: '#FFF',
|
||||
border: 0,
|
||||
margin: 0,
|
||||
maxWidth: '540px',
|
||||
minWidth: '326px',
|
||||
padding: 0,
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Lightbox Modal */}
|
||||
<AnimatePresence>
|
||||
{selectedPost && (
|
||||
<motion.div
|
||||
className="fixed inset-0 bg-black/90 z-50 flex items-center justify-center p-4 overflow-y-auto pt-[100px]"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
onClick={() => setSelectedPost(null)}
|
||||
>
|
||||
<motion.div
|
||||
className="relative max-w-lg w-full bg-white dark:bg-stone-900 rounded-xl overflow-hidden my-auto"
|
||||
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()}
|
||||
>
|
||||
{/* Close button inside modal container */}
|
||||
<button
|
||||
className="absolute top-2 right-2 text-stone-500 bg-white border border-stone-200 shadow-md rounded-full w-8 h-8 flex items-center justify-center hover:bg-stone-100 transition-colors z-[60]"
|
||||
onClick={() => setSelectedPost(null)}
|
||||
>
|
||||
<span className="font-bold">×</span>
|
||||
</button>
|
||||
|
||||
{/* Instagram Embed WITH caption shown in the Lightbox */}
|
||||
<div className="w-full bg-white mt-12 pb-4 px-2">
|
||||
<blockquote
|
||||
className="instagram-media"
|
||||
data-instgrm-captioned
|
||||
data-instgrm-permalink={`${selectedPost}?utm_source=ig_embed&utm_campaign=loading`}
|
||||
data-instgrm-version="14"
|
||||
style={{
|
||||
background: '#FFF',
|
||||
border: 0,
|
||||
boxShadow: 'none',
|
||||
margin: '0',
|
||||
maxWidth: '540px',
|
||||
minWidth: '326px',
|
||||
padding: 0,
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InstagramFeed;
|
||||
Reference in New Issue
Block a user