{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "carousel",
  "type": "registry:ui",
  "description": "Carousel component with navigation, dots, and touch support built on Embla",
  "dependencies": [
    "embla-carousel-react",
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "button"
  ],
  "files": [
    {
      "path": "registry/default/ui/carousel.tsx",
      "content": "import * as React from 'react'\nimport useEmblaCarousel, {\n  type UseEmblaCarouselType,\n} from 'embla-carousel-react'\nimport { ArrowLeft, ArrowRight } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { Button } from '@/components/ui/button'\n\ntype CarouselApi = UseEmblaCarouselType[1]\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>\ntype CarouselOptions = UseCarouselParameters[0]\ntype CarouselPlugin = UseCarouselParameters[1]\n\ninterface CarouselProps {\n  opts?: CarouselOptions\n  plugins?: CarouselPlugin\n  orientation?: 'horizontal' | 'vertical'\n  setApi?: (api: CarouselApi) => void\n}\n\ntype CarouselContextProps = {\n  carouselRef: ReturnType<typeof useEmblaCarousel>[0]\n  api: ReturnType<typeof useEmblaCarousel>[1]\n  scrollPrev: () => void\n  scrollNext: () => void\n  canScrollPrev: boolean\n  canScrollNext: boolean\n  selectedIndex: number\n  scrollSnaps: number[]\n  scrollTo: (index: number) => void\n} & CarouselProps\n\nconst CarouselContext = React.createContext<CarouselContextProps | null>(null)\n\nfunction useCarousel() {\n  const context = React.useContext(CarouselContext)\n\n  if (!context) {\n    throw new Error('useCarousel must be used within a <Carousel />')\n  }\n\n  return context\n}\n\nconst Carousel = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & CarouselProps\n>(\n  (\n    {\n      orientation = 'horizontal',\n      opts,\n      setApi,\n      plugins,\n      className,\n      children,\n      ...props\n    },\n    ref\n  ) => {\n    const [carouselRef, api] = useEmblaCarousel(\n      {\n        ...opts,\n        axis: orientation === 'horizontal' ? 'x' : 'y',\n      },\n      plugins\n    )\n    const [canScrollPrev, setCanScrollPrev] = React.useState(false)\n    const [canScrollNext, setCanScrollNext] = React.useState(false)\n    const [selectedIndex, setSelectedIndex] = React.useState(0)\n    const [scrollSnaps, setScrollSnaps] = React.useState<number[]>([])\n\n    const onSelect = React.useCallback((api: CarouselApi) => {\n      if (!api) return\n\n      setSelectedIndex(api.selectedScrollSnap())\n      setCanScrollPrev(api.canScrollPrev())\n      setCanScrollNext(api.canScrollNext())\n    }, [])\n\n    const scrollPrev = React.useCallback(() => {\n      api?.scrollPrev()\n    }, [api])\n\n    const scrollNext = React.useCallback(() => {\n      api?.scrollNext()\n    }, [api])\n\n    const scrollTo = React.useCallback(\n      (index: number) => {\n        api?.scrollTo(index)\n      },\n      [api]\n    )\n\n    const handleKeyDown = React.useCallback(\n      (event: React.KeyboardEvent<HTMLDivElement>) => {\n        if (event.key === 'ArrowLeft') {\n          event.preventDefault()\n          scrollPrev()\n        } else if (event.key === 'ArrowRight') {\n          event.preventDefault()\n          scrollNext()\n        }\n      },\n      [scrollPrev, scrollNext]\n    )\n\n    React.useEffect(() => {\n      if (!api || !setApi) return\n      setApi(api)\n    }, [api, setApi])\n\n    React.useEffect(() => {\n      if (!api) return\n\n      setScrollSnaps(api.scrollSnapList())\n      onSelect(api)\n      api.on('reInit', onSelect)\n      api.on('select', onSelect)\n\n      return () => {\n        api?.off('select', onSelect)\n      }\n    }, [api, onSelect])\n\n    return (\n      <CarouselContext.Provider\n        value={{\n          carouselRef,\n          api: api,\n          opts,\n          orientation:\n            orientation || (opts?.axis === 'y' ? 'vertical' : 'horizontal'),\n          scrollPrev,\n          scrollNext,\n          canScrollPrev,\n          canScrollNext,\n          selectedIndex,\n          scrollSnaps,\n          scrollTo,\n        }}\n      >\n        <div\n          ref={ref}\n          onKeyDownCapture={handleKeyDown}\n          className={cn('relative', className)}\n          role=\"region\"\n          aria-roledescription=\"carousel\"\n          {...props}\n        >\n          {children}\n        </div>\n      </CarouselContext.Provider>\n    )\n  }\n)\nCarousel.displayName = 'Carousel'\n\nconst CarouselContent = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n  const { carouselRef, orientation } = useCarousel()\n\n  return (\n    <div ref={carouselRef} className=\"overflow-hidden\">\n      <div\n        ref={ref}\n        className={cn(\n          'flex',\n          orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',\n          className\n        )}\n        {...props}\n      />\n    </div>\n  )\n})\nCarouselContent.displayName = 'CarouselContent'\n\nconst CarouselItem = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n  const { orientation } = useCarousel()\n\n  return (\n    <div\n      ref={ref}\n      role=\"group\"\n      aria-roledescription=\"slide\"\n      className={cn(\n        'min-w-0 shrink-0 grow-0 basis-full',\n        orientation === 'horizontal' ? 'pl-4' : 'pt-4',\n        className\n      )}\n      {...props}\n    />\n  )\n})\nCarouselItem.displayName = 'CarouselItem'\n\nconst CarouselPrevious = React.forwardRef<\n  HTMLButtonElement,\n  React.ComponentProps<typeof Button>\n>(({ className, variant = 'outline', size = 'icon', ...props }, ref) => {\n  const { orientation, scrollPrev, canScrollPrev } = useCarousel()\n\n  return (\n    <Button\n      ref={ref}\n      variant={variant}\n      size={size}\n      className={cn(\n        'absolute h-10 w-10',\n        // Disable translate on hover - use scale instead for carousel buttons\n        'hover:translate-x-0 hover:translate-y-0 hover:scale-105 hover:shadow-[4px_4px_0px_hsl(var(--shadow-color))]',\n        orientation === 'horizontal'\n          ? '-left-12 top-1/2 -translate-y-1/2'\n          : '-top-12 left-1/2 -translate-x-1/2 rotate-90',\n        className\n      )}\n      disabled={!canScrollPrev}\n      onClick={scrollPrev}\n      {...props}\n    >\n      <ArrowLeft className=\"h-5 w-5 stroke-[3]\" />\n      <span className=\"sr-only\">Previous slide</span>\n    </Button>\n  )\n})\nCarouselPrevious.displayName = 'CarouselPrevious'\n\nconst CarouselNext = React.forwardRef<\n  HTMLButtonElement,\n  React.ComponentProps<typeof Button>\n>(({ className, variant = 'outline', size = 'icon', ...props }, ref) => {\n  const { orientation, scrollNext, canScrollNext } = useCarousel()\n\n  return (\n    <Button\n      ref={ref}\n      variant={variant}\n      size={size}\n      className={cn(\n        'absolute h-10 w-10',\n        // Disable translate on hover - use scale instead for carousel buttons\n        'hover:translate-x-0 hover:translate-y-0 hover:scale-105 hover:shadow-[4px_4px_0px_hsl(var(--shadow-color))]',\n        orientation === 'horizontal'\n          ? '-right-12 top-1/2 -translate-y-1/2'\n          : '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',\n        className\n      )}\n      disabled={!canScrollNext}\n      onClick={scrollNext}\n      {...props}\n    >\n      <ArrowRight className=\"h-5 w-5 stroke-[3]\" />\n      <span className=\"sr-only\">Next slide</span>\n    </Button>\n  )\n})\nCarouselNext.displayName = 'CarouselNext'\n\nconst CarouselDots = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n  const { selectedIndex, scrollSnaps, scrollTo } = useCarousel()\n\n  if (scrollSnaps.length <= 1) return null\n\n  return (\n    <div\n      ref={ref}\n      className={cn('flex items-center justify-center gap-2 mt-4', className)}\n      {...props}\n    >\n      {scrollSnaps.map((_, index) => (\n        <button\n          key={index}\n          type=\"button\"\n          onClick={() => scrollTo(index)}\n          className={cn(\n            'h-3 w-3 border-2 border-foreground transition-all duration-200',\n            index === selectedIndex\n              ? 'bg-primary scale-110 shadow-[2px_2px_0px_hsl(var(--shadow-color))]'\n              : 'bg-muted hover:bg-muted/80'\n          )}\n          aria-label={`Go to slide ${index + 1}`}\n        />\n      ))}\n    </div>\n  )\n})\nCarouselDots.displayName = 'CarouselDots'\n\nexport {\n  type CarouselApi,\n  Carousel,\n  CarouselContent,\n  CarouselItem,\n  CarouselPrevious,\n  CarouselNext,\n  CarouselDots,\n  useCarousel,\n}\n",
      "type": "registry:ui",
      "target": "components/ui/carousel.tsx"
    }
  ]
}