Article Section Slider

Hover Links to see the slides change with a fade


Copy code
"use client"

import { useState } from "react"
import { motion, AnimatePresence } from "framer-motion"
import Image from "next/image"

const slides = [
  {
    app: {
      label: "The App slide",
      src: "/images/work3.jpg",
      description:
        "The App Slide - eprehenderit hic accusantium, facere cumque maiores rem tempore voluptates accusamus atque veritatis, tenetur excepturi corporis sint natus!",
    },
    web: {
      label: "The Web slide",
      src: "/images/blog/2.jpeg",
      description:
        "The web slide - Loredolor sit amet consectetur adipisicing elit. Quaerat at expedita ea recusandae reprehenderit hic accusantium, facere cumque maiores rem tempore voluptates accusamus !",
    },
    ux: {
      label: "The UX slide",
      src: "/images/blog/3.jpeg",
      description:
        "the ux side - facere cumque maiores rem tempore voluptates accusamus atque veritatis, tenetur excepturi corporis sint natus! Lorem ipsum dolor sit amet conat expedita ea recusandae rnatus!",
    },
    system: {
      label: "The System slide",
      src: "/images/work2.jpg",
      description:
        "the system side - hic atque veritatis, tenetur excepturi corporis sint natus! Lorem ipsum dolor sit amet consectetur adipisicing elit. Quaerat at turi sint natus!",
    },
  },
]

export function ArticleSectionSlider() {
  const [selectedSlide, setSelectedSlide] = useState<Slide | null>(
    slides[0].app
  )
  return (
    <div className=" mx-4 lg:mx-auto lg:max-w-4xl  ">
      <div className="flex w-full flex-col bg-white sm:h-96 sm:flex-row  ">
        <div className="flex h-full flex-col  items-center justify-center py-6 sm:w-1/2 sm:py-0">
          <ul className="flex flex-col items-center justify-center gap-4 sm:items-start sm:justify-start">
            {slides.map((slide) => {
              return Object.values(slide).map((slideItem) => {
                return (
                  <LinkComponent
                    key={slideItem.label}
                    label={slideItem.label}
                    setSelectedSlide={setSelectedSlide}
                    slide={slideItem}
                  />
                )
              })
            })}
          </ul>
        </div>
        {/* slide area */}
        <div className=" relative flex h-[20rem] flex-col border-l border-gray-100 sm:h-full sm:w-1/2">
          <AnimatePresence initial={false}>
            {selectedSlide && (
              <motion.div
                className="h-full w-full "
                key={selectedSlide.label}
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                transition={{
                  ease: "linear",
                  duration: 0.3,
                }}
              >
                <Slide selectedSlide={selectedSlide} />
              </motion.div>
            )}
          </AnimatePresence>
        </div>
      </div>
    </div>
  )
}



interface Slide {
  label: string
  src: string
  description: string
}

function Slide({ selectedSlide }: { selectedSlide: Slide }) {
  return (
    <div className="flex h-full flex-col bg-white   ">
      <div className="absolute top-0 flex h-full  flex-col ">
        <div className="relative flex h-1/2">
          <h2 className="flex self-end p-4 text-left text-xl font-bold ">
            {selectedSlide.label}
          </h2>
          <Image
            fill
            alt={selectedSlide.label}
            className=" absolute left-0 top-0 z-0 h-full w-full object-cover object-left-top opacity-30 "
            src={selectedSlide.src}
          />
        </div>
        <p className=" h-20 border-t border-gray-100 p-4 ">
          {selectedSlide.description}
        </p>
      </div>
    </div>
  )
}

function LinkComponent({
  label,
  setSelectedSlide,
  slide,
}: {
  label: string
  setSelectedSlide: any
  slide: Slide | null
}) {
  const [isHovered, setIsHovered] = useState(false)
  function handleSelectSlide(slide: Slide | null) {
    setSelectedSlide(slide)
  }

  return (
    <li
      className="font bold relative w-fit  cursor-pointer transition-transform duration-300 ease-in-out sm:hover:translate-x-5"
      onClick={() => {
        handleSelectSlide(slide)
        setIsHovered(false)
      }}
      onMouseEnter={() => {
        handleSelectSlide(slide)
        setIsHovered(true)
      }}
      onMouseLeave={() => {
        setIsHovered(false)
      }}
    >
      <div>
        <p className="text-xl">{label}</p>

        <AnimatedUnderline
          label={label}
          slide={slide}
          isHovered={isHovered}
          handleSelectSlide={handleSelectSlide}
        ></AnimatedUnderline>
      </div>
    </li>
  )
}

function AnimatedUnderline(props: {
  label: string
  slide: Slide | null
  isHovered: boolean
  handleSelectSlide: any
}) {
  return (
    <AnimatePresence>
      {props.isHovered && (
        <motion.div
          key={props.label}
          className="absolute h-0.5 cursor-pointer bg-black"
          initial={{
            width: "0%",
          }}
          animate={{
            width: "100%",
            transition: {
              duration: 0.7,
            },
          }}
          exit={{
            width: "0%",
            transition: {
              duration: 0.5,
            },
          }}
          onMouseEnter={() => props.handleSelectSlide(props.slide)}
          onMouseLeave={() => props.handleSelectSlide(props.slide)}
          onClick={() => props.handleSelectSlide(props.slide)}
        ></motion.div>
      )}
    </AnimatePresence>
  )
}
  • The App slide

  • The Web slide

  • The UX slide

  • The System slide

The App slide

The App slide

The App Slide - eprehenderit hic accusantium, facere cumque maiores rem tempore voluptates accusamus atque veritatis, tenetur excepturi corporis sint natus!