{
  "name": "carousel",
  "type": "registry:ui",
  "dependencies": [
    "@tabler/icons-react"
  ],
  "files": [
    {
      "path": "components/ui/carousel.tsx",
      "content": "\"use client\";\nimport { IconArrowNarrowRight } from \"@tabler/icons-react\";\nimport { useState, useRef, useId, useEffect } from \"react\";\n\ninterface SlideData {\n  title: string;\n  button: string;\n  src: string;\n}\n\ninterface SlideProps {\n  slide: SlideData;\n  index: number;\n  current: number;\n  handleSlideClick: (index: number) => void;\n}\n\nconst Slide = ({ slide, index, current, handleSlideClick }: SlideProps) => {\n  const slideRef = useRef<HTMLLIElement>(null);\n\n  const xRef = useRef(0);\n  const yRef = useRef(0);\n  const frameRef = useRef<number>();\n\n  useEffect(() => {\n    const animate = () => {\n      if (!slideRef.current) return;\n\n      const x = xRef.current;\n      const y = yRef.current;\n\n      slideRef.current.style.setProperty(\"--x\", `${x}px`);\n      slideRef.current.style.setProperty(\"--y\", `${y}px`);\n\n      frameRef.current = requestAnimationFrame(animate);\n    };\n\n    frameRef.current = requestAnimationFrame(animate);\n\n    return () => {\n      if (frameRef.current) {\n        cancelAnimationFrame(frameRef.current);\n      }\n    };\n  }, []);\n\n  const handleMouseMove = (event: React.MouseEvent) => {\n    const el = slideRef.current;\n    if (!el) return;\n\n    const r = el.getBoundingClientRect();\n    xRef.current = event.clientX - (r.left + Math.floor(r.width / 2));\n    yRef.current = event.clientY - (r.top + Math.floor(r.height / 2));\n  };\n\n  const handleMouseLeave = () => {\n    xRef.current = 0;\n    yRef.current = 0;\n  };\n\n  const imageLoaded = (event: React.SyntheticEvent<HTMLImageElement>) => {\n    event.currentTarget.style.opacity = \"1\";\n  };\n\n  const { src, button, title } = slide;\n\n  return (\n    <div className=\"[perspective:1200px] [transform-style:preserve-3d]\">\n      <li\n        ref={slideRef}\n        className=\"flex flex-1 flex-col items-center justify-center relative text-center text-white opacity-100 transition-all duration-300 ease-in-out w-[70vmin] h-[70vmin] mx-[4vmin] z-10 \"\n        onClick={() => handleSlideClick(index)}\n        onMouseMove={handleMouseMove}\n        onMouseLeave={handleMouseLeave}\n        style={{\n          transform:\n            current !== index\n              ? \"scale(0.98) rotateX(8deg)\"\n              : \"scale(1) rotateX(0deg)\",\n          transition: \"transform 0.5s cubic-bezier(0.4, 0, 0.2, 1)\",\n          transformOrigin: \"bottom\",\n        }}\n      >\n        <div\n          className=\"absolute top-0 left-0 w-full h-full bg-[#1D1F2F] rounded-[1%] overflow-hidden transition-all duration-150 ease-out\"\n          style={{\n            transform:\n              current === index\n                ? \"translate3d(calc(var(--x) / 30), calc(var(--y) / 30), 0)\"\n                : \"none\",\n          }}\n        >\n          <img\n            className=\"absolute inset-0 w-[120%] h-[120%] object-cover opacity-100 transition-opacity duration-600 ease-in-out\"\n            style={{\n              opacity: current === index ? 1 : 0.5,\n            }}\n            alt={title}\n            src={src}\n            onLoad={imageLoaded}\n            loading=\"eager\"\n            decoding=\"sync\"\n          />\n          {current === index && (\n            <div className=\"absolute inset-0 bg-black/30 transition-all duration-1000\" />\n          )}\n        </div>\n\n        <article\n          className={`relative p-[4vmin] transition-opacity duration-1000 ease-in-out ${\n            current === index ? \"opacity-100 visible\" : \"opacity-0 invisible\"\n          }`}\n        >\n          <h2 className=\"text-lg md:text-2xl lg:text-4xl font-semibold  relative\">\n            {title}\n          </h2>\n          <div className=\"flex justify-center\">\n            <button className=\"mt-6  px-4 py-2 w-fit mx-auto sm:text-sm text-black bg-white h-12 border border-transparent text-xs flex justify-center items-center rounded-2xl hover:shadow-lg transition duration-200 shadow-[0px_2px_3px_-1px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(25,28,33,0.02),0px_0px_0px_1px_rgba(25,28,33,0.08)]\">\n              {button}\n            </button>\n          </div>\n        </article>\n      </li>\n    </div>\n  );\n};\n\ninterface CarouselControlProps {\n  type: string;\n  title: string;\n  handleClick: () => void;\n}\n\nconst CarouselControl = ({\n  type,\n  title,\n  handleClick,\n}: CarouselControlProps) => {\n  return (\n    <button\n      className={`w-10 h-10 flex items-center mx-2 justify-center bg-neutral-200 dark:bg-neutral-800 border-3 border-transparent rounded-full focus:border-[#6D64F7] focus:outline-none hover:-translate-y-0.5 active:translate-y-0.5 transition duration-200 ${\n        type === \"previous\" ? \"rotate-180\" : \"\"\n      }`}\n      title={title}\n      onClick={handleClick}\n    >\n      <IconArrowNarrowRight className=\"text-neutral-600 dark:text-neutral-200\" />\n    </button>\n  );\n};\n\ninterface CarouselProps {\n  slides: SlideData[];\n}\n\nexport default function Carousel({ slides }: CarouselProps) {\n  const [current, setCurrent] = useState(0);\n\n  const handlePreviousClick = () => {\n    const previous = current - 1;\n    setCurrent(previous < 0 ? slides.length - 1 : previous);\n  };\n\n  const handleNextClick = () => {\n    const next = current + 1;\n    setCurrent(next === slides.length ? 0 : next);\n  };\n\n  const handleSlideClick = (index: number) => {\n    if (current !== index) {\n      setCurrent(index);\n    }\n  };\n\n  const id = useId();\n\n  return (\n    <div\n      className=\"relative w-[70vmin] h-[70vmin] mx-auto\"\n      aria-labelledby={`carousel-heading-${id}`}\n    >\n      <ul\n        className=\"absolute flex mx-[-4vmin] transition-transform duration-1000 ease-in-out\"\n        style={{\n          transform: `translateX(-${current * (100 / slides.length)}%)`,\n        }}\n      >\n        {slides.map((slide, index) => (\n          <Slide\n            key={index}\n            slide={slide}\n            index={index}\n            current={current}\n            handleSlideClick={handleSlideClick}\n          />\n        ))}\n      </ul>\n\n      <div className=\"absolute flex justify-center w-full top-[calc(100%+1rem)]\">\n        <CarouselControl\n          type=\"previous\"\n          title=\"Go to previous slide\"\n          handleClick={handlePreviousClick}\n        />\n\n        <CarouselControl\n          type=\"next\"\n          title=\"Go to next slide\"\n          handleClick={handleNextClick}\n        />\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/ui/carousel.tsx"
    }
  ],
  "author": "Manu Arora <hi@manuarora.in>",
  "title": "Carousel"
}