Advertisement
lu6id

image-posts.tsx

Oct 21st, 2024
248
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. 'use client'
  2.  
  3. import React, { useState, useEffect, useRef } from 'react'
  4. import { useSwipeable } from 'react-swipeable'
  5. import { ChevronLeft, ChevronRight } from 'lucide-react'
  6. import { motion, AnimatePresence } from 'framer-motion'
  7.  
  8. const LinkedinPost = ({ imageUrl, scale }: { imageUrl: string; scale: number }) => {
  9.   return (
  10.     <div
  11.       className={`card bg-base-100 shadow-xl overflow-hidden rounded-xl transition-all duration-300 ease-in-out`}
  12.       style={{
  13.         width: `${16 * scale}rem`,
  14.         height: `${20 * scale}rem`,
  15.       }}
  16.     >
  17.       <figure className="w-full h-full">
  18.         <img
  19.           src={imageUrl}
  20.           alt="LinkedIn Post"
  21.           className="w-full h-full object-cover"
  22.         />
  23.       </figure>
  24.     </div>
  25.   );
  26. };
  27.  
  28. export default function Component({
  29.   imageUrls = [
  30.     'https://i.pinimg.com/originals/ed/bf/0e/edbf0e545f2271a78539bd98f6ad5d46.jpg',
  31.     'https://i.pinimg.com/originals/f9/aa/7d/f9aa7d94a2f1943d77298b47ac58436d.jpg',
  32.     'https://i.pinimg.com/originals/9e/9c/83/9e9c83a0adaddfa5a17f011f085e2f4a.jpg',
  33.     'https://i.pinimg.com/originals/c5/f6/3b/c5f63b9f3e0a440d1865fd436c8b200d.jpg',
  34.     'https://i.pinimg.com/originals/b7/94/2d/b7942dd1574208c52367dd2ac4b98e9b.jpg',
  35.     'https://i.pinimg.com/originals/ba/fa/e8/bafae89ed636c0ad27397bd3e7080928.jpg',
  36.     'https://i.pinimg.com/originals/b8/ea/3e/b8ea3e03c2f6400cfb937a08767512e5.jpg',
  37.     'https://i.pinimg.com/originals/66/76/94/6676940d87c357f2c9453776c99f3365.jpg',
  38.     'https://i.pinimg.com/originals/f1/26/37/f12637ae012e4eb535026c7972369427.jpg',
  39.     'https://i.pinimg.com/originals/bb/85/be/bb85be6a6f012df2acbdef627f6fc613.jpg',
  40.     'https://i.pinimg.com/originals/a3/f9/9f/a3f99f8b8fa473e698f12ebc09406f59.jpg',
  41.     'https://i.pinimg.com/originals/8b/ef/d9/8befd98a4b8654649d6f4ef448b6470d.jpg',
  42.     'https://i.pinimg.com/originals/38/ea/d7/38ead79eb780667a0703e49075eba848.jpg',
  43.     'https://i.pinimg.com/originals/3d/70/73/3d70735cccdb05697be4180ca8c755a7.jpg',
  44.     'https://i.pinimg.com/originals/09/6e/17/096e172c0481c310dc53b1e970055489.jpg',
  45.     'https://i.pinimg.com/originals/07/58/91/07589144688de9c80e24e973229ee312.jpg',
  46.     'https://i.pinimg.com/originals/36/90/40/3690403b15b5ec4f1d74622167b15d12.jpg'
  47.   ]
  48. }: { imageUrls?: string[] }) {
  49.   const [activeIndex, setActiveIndex] = useState(0)
  50.   const [visiblePosts, setVisiblePosts] = useState<string[]>([])
  51.   const carouselRef = useRef<HTMLDivElement>(null)
  52.  
  53.   useEffect(() => {
  54.     const updateVisiblePosts = () => {
  55.       const windowWidth = window.innerWidth
  56.       let visibleCount = 5
  57.       if (windowWidth < 640) visibleCount = 1
  58.       else if (windowWidth < 1024) visibleCount = 3
  59.  
  60.       const newVisiblePosts = []
  61.       for (let i = 0; i < visibleCount; i++) {
  62.         const index = (activeIndex + i - Math.floor(visibleCount / 2) + imageUrls.length) % imageUrls.length
  63.         newVisiblePosts.push(imageUrls[index])
  64.       }
  65.       setVisiblePosts(newVisiblePosts)
  66.     }
  67.  
  68.     updateVisiblePosts()
  69.     window.addEventListener('resize', updateVisiblePosts)
  70.     return () => window.removeEventListener('resize', updateVisiblePosts)
  71.   }, [activeIndex, imageUrls])
  72.  
  73.   useEffect(() => {
  74.     const interval = setInterval(() => {
  75.       setActiveIndex((prevIndex) => (prevIndex + 1) % imageUrls.length)
  76.     }, 5000)
  77.     return () => clearInterval(interval)
  78.   }, [imageUrls.length])
  79.  
  80.   const handlers = useSwipeable({
  81.     onSwipedLeft: () => setActiveIndex((prevIndex) => (prevIndex + 1) % imageUrls.length),
  82.     onSwipedRight: () => setActiveIndex((prevIndex) => (prevIndex - 1 + imageUrls.length) % imageUrls.length),
  83.   })
  84.  
  85.   const goToIndex = (index: number) => {
  86.     setActiveIndex(index)
  87.   }
  88.  
  89.   const renderPaginationIndicators = () => {
  90.     const totalIndicators = Math.min(5, imageUrls.length)
  91.     const startIndex = Math.max(0, Math.min(activeIndex - Math.floor(totalIndicators / 2), imageUrls.length - totalIndicators))
  92.     return Array.from({ length: totalIndicators }, (_, i) => {
  93.       const index = (startIndex + i) % imageUrls.length
  94.       return (
  95.         <motion.div
  96.           key={index}
  97.           initial={{ scale: 0.8 }}
  98.           animate={{ scale: index === activeIndex ? 1.2 : 1 }}
  99.           transition={{ duration: 0.2 }}
  100.         >
  101.           <button
  102.             className={`btn btn-circle btn-xs ${index === activeIndex ? 'btn-primary' : 'btn-ghost'}`}
  103.             onClick={() => goToIndex(index)}
  104.             aria-label={`Go to slide ${index + 1}`}
  105.           />
  106.         </motion.div>
  107.       )
  108.     })
  109.   }
  110.  
  111.   const getScale = (index: number) => {
  112.     const middleIndex = Math.floor(visiblePosts.length / 2)
  113.     if (index === middleIndex) return 1.2
  114.     if (index === middleIndex - 1 || index === middleIndex + 1) return 1
  115.     return 0.8
  116.   }
  117.  
  118.   return (
  119.     <div className="w-full max-w-6xl mx-auto p-8 pb-14 bg-gradient-to-r from-base-200 to-base-300 rounded-3xl shadow-lg" {...handlers}>
  120.       <div className="relative overflow-hidden">
  121.         <div
  122.           ref={carouselRef}
  123.           className="flex justify-center items-center"
  124.         >
  125.           <AnimatePresence initial={false}>
  126.             {visiblePosts.map((imageUrl, index) => (
  127.               <motion.div
  128.                 key={`${activeIndex}-${index}`}
  129.                 className="mx-2"
  130.                 initial={{ opacity: 0, x: index === 0 ? -100 : index === visiblePosts.length - 1 ? 100 : 0 }}
  131.                 animate={{ opacity: 1, x: 0 }}
  132.                 exit={{ opacity: 0, x: index === 0 ? 100 : index === visiblePosts.length - 1 ? -100 : 0 }}
  133.                 transition={{ duration: 0.5 }}
  134.               >
  135.                 <LinkedinPost imageUrl={imageUrl} scale={getScale(index)} />
  136.               </motion.div>
  137.             ))}
  138.           </AnimatePresence>
  139.         </div>
  140.         <button
  141.           className="btn btn-circle btn-sm absolute top-1/2 left-4 -translate-y-1/2 bg-base-100 hover:bg-base-200"
  142.           onClick={() => setActiveIndex((prevIndex) => (prevIndex - 1 + imageUrls.length) % imageUrls.length)}
  143.           aria-label="Previous slide"
  144.         >
  145.           <ChevronLeft className="h-4 w-4" />
  146.         </button>
  147.         <button
  148.           className="btn btn-circle btn-sm absolute top-1/2 right-4 -translate-y-1/2 bg-base-100 hover:bg-base-200"
  149.           onClick={() => setActiveIndex((prevIndex) => (prevIndex + 1) % imageUrls.length)}
  150.           aria-label="Next slide"
  151.         >
  152.           <ChevronRight className="h-4 w-4" />
  153.         </button>
  154.       </div>
  155.       <div className="flex justify-center mt-6 space-x-2">
  156.         {renderPaginationIndicators()}
  157.       </div>
  158.     </div>
  159.   )
  160. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement