{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "components-community-motion-carousel",
  "title": "Motion Carousel",
  "description": "A carousel built on top of Embla Carousel with smooth Motion-powered animations. Each slide scales dynamically based on its active state, and the pagination uses animated pill-style dot buttons for an interactive, fluid experience.",
  "dependencies": [
    "motion",
    "lucide-react",
    "embla-carousel",
    "embla-carousel-react"
  ],
  "registryDependencies": [
    "@animate-ui/components-buttons-button"
  ],
  "files": [
    {
      "path": "registry/components/community/motion-carousel/index.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { motion, type Transition } from 'motion/react';\nimport { EmblaOptionsType, EmblaCarouselType } from 'embla-carousel';\nimport useEmblaCarousel from 'embla-carousel-react';\nimport { Button } from '@/components/animate-ui/components/buttons/button';\nimport { ChevronRight, ChevronLeft } from 'lucide-react';\n\ntype PropType = {\n  slides: number[];\n  options?: EmblaOptionsType;\n};\n\ntype EmblaControls = {\n  selectedIndex: number;\n  scrollSnaps: number[];\n  prevDisabled: boolean;\n  nextDisabled: boolean;\n  onDotClick: (index: number) => void;\n  onPrev: () => void;\n  onNext: () => void;\n};\n\ntype DotButtonProps = {\n  selected?: boolean;\n  label: string;\n  onClick: () => void;\n};\n\nconst transition: Transition = {\n  type: 'spring',\n  stiffness: 240,\n  damping: 24,\n  mass: 1,\n};\n\nconst useEmblaControls = (\n  emblaApi: EmblaCarouselType | undefined,\n): EmblaControls => {\n  const [selectedIndex, setSelectedIndex] = React.useState(0);\n  const [scrollSnaps, setScrollSnaps] = React.useState<number[]>([]);\n  const [prevDisabled, setPrevDisabled] = React.useState(true);\n  const [nextDisabled, setNextDisabled] = React.useState(true);\n\n  const onDotClick = React.useCallback(\n    (index: number) => emblaApi?.scrollTo(index),\n    [emblaApi],\n  );\n\n  const onPrev = React.useCallback(() => emblaApi?.scrollPrev(), [emblaApi]);\n  const onNext = React.useCallback(() => emblaApi?.scrollNext(), [emblaApi]);\n\n  const updateSelectionState = (api: EmblaCarouselType) => {\n    setSelectedIndex(api.selectedScrollSnap());\n    setPrevDisabled(!api.canScrollPrev());\n    setNextDisabled(!api.canScrollNext());\n  };\n\n  const onInit = React.useCallback((api: EmblaCarouselType) => {\n    setScrollSnaps(api.scrollSnapList());\n    updateSelectionState(api);\n  }, []);\n\n  const onSelect = React.useCallback((api: EmblaCarouselType) => {\n    updateSelectionState(api);\n  }, []);\n\n  React.useEffect(() => {\n    if (!emblaApi) return;\n\n    onInit(emblaApi);\n    emblaApi.on('reInit', onInit).on('select', onSelect);\n\n    return () => {\n      emblaApi.off('reInit', onInit).off('select', onSelect);\n    };\n  }, [emblaApi, onInit, onSelect]);\n\n  return {\n    selectedIndex,\n    scrollSnaps,\n    prevDisabled,\n    nextDisabled,\n    onDotClick,\n    onPrev,\n    onNext,\n  };\n};\n\nfunction MotionCarousel(props: PropType) {\n  const { slides, options } = props;\n  const [emblaRef, emblaApi] = useEmblaCarousel(options);\n  const {\n    selectedIndex,\n    scrollSnaps,\n    prevDisabled,\n    nextDisabled,\n    onDotClick,\n    onPrev,\n    onNext,\n  } = useEmblaControls(emblaApi);\n\n  return (\n    <div className=\"w-full space-y-4 [--slide-height:9rem] sm:[--slide-height:13rem] md:[--slide-height:18rem] [--slide-spacing:1.5rem] [--slide-size:55%]\">\n      <div className=\"overflow-hidden\" ref={emblaRef}>\n        <div className=\"flex touch-pan-y touch-pinch-zoom\">\n          {slides.map((index) => {\n            const isActive = index === selectedIndex;\n\n            return (\n              <motion.div\n                key={index}\n                className=\"h-[var(--slide-height)] mr-[var(--slide-spacing)] basis-[var(--slide-size)] flex-none flex min-w-0\"\n              >\n                <motion.div\n                  className=\"size-full flex items-center justify-center text-3xl md:text-5xl font-semibold select-none border-4 rounded-xl\"\n                  initial={false}\n                  animate={{\n                    scale: isActive ? 1 : 0.9,\n                  }}\n                  transition={transition}\n                >\n                  {index + 1}\n                </motion.div>\n              </motion.div>\n            );\n          })}\n        </div>\n      </div>\n\n      <div className=\"flex justify-between\">\n        <Button size=\"icon\" onClick={onPrev} disabled={prevDisabled}>\n          <ChevronLeft className=\"size-5\" />\n        </Button>\n\n        <div className=\"flex flex-wrap justify-end items-center gap-2\">\n          {scrollSnaps.map((_, index) => (\n            <DotButton\n              key={index}\n              label={`Slide ${index + 1}`}\n              selected={index === selectedIndex}\n              onClick={() => onDotClick(index)}\n            />\n          ))}\n        </div>\n\n        <Button size=\"icon\" onClick={onNext} disabled={nextDisabled}>\n          <ChevronRight className=\"size-5\" />\n        </Button>\n      </div>\n    </div>\n  );\n}\n\nfunction DotButton({ selected = false, label, onClick }: DotButtonProps) {\n  return (\n    <motion.button\n      type=\"button\"\n      onClick={onClick}\n      layout\n      initial={false}\n      className=\"flex cursor-pointer select-none items-center justify-center rounded-full border-none bg-primary text-primary-foreground text-sm\"\n      animate={{\n        width: selected ? 68 : 12,\n        height: selected ? 28 : 12,\n      }}\n      transition={transition}\n    >\n      <motion.span\n        layout\n        initial={false}\n        className=\"block whitespace-nowrap px-3 py-1\"\n        animate={{\n          opacity: selected ? 1 : 0,\n          scale: selected ? 1 : 0,\n          filter: selected ? 'blur(0)' : 'blur(4px)',\n        }}\n        transition={transition}\n      >\n        {label}\n      </motion.span>\n    </motion.button>\n  );\n}\n\nexport { MotionCarousel };\n",
      "type": "registry:ui",
      "target": "components/animate-ui/components/community/motion-carousel.tsx"
    }
  ],
  "type": "registry:ui"
}