{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "carousel",
  "type": "registry:component",
  "title": "Carousel",
  "description": "Scrollable content carousel with navigation controls.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/carousel/carousel.tsx",
      "content": "\"use client\";\n\nimport {\n  createContext,\n  forwardRef,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useState,\n} from \"react\";\n\nimport useEmblaCarousel, {\n  type UseEmblaCarouselType,\n} from \"embla-carousel-react\";\nimport { ArrowLeft, ArrowRight } from \"lucide-react\";\n\nimport { cn } from \"@vllnt/ui\";\nimport { Button } from \"@vllnt/ui\";\n\ntype CarouselApi = UseEmblaCarouselType[1];\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>;\ntype CarouselOptions = UseCarouselParameters[0];\ntype CarouselPlugin = UseCarouselParameters[1];\n\ntype CarouselProps = {\n  opts?: CarouselOptions;\n  orientation?: \"horizontal\" | \"vertical\";\n  plugins?: CarouselPlugin;\n  setApi?: (api: CarouselApi) => void;\n};\n\ntype CarouselContextProps = {\n  api: ReturnType<typeof useEmblaCarousel>[1];\n  canScrollNext: boolean;\n  canScrollPrev: boolean;\n  carouselRef: ReturnType<typeof useEmblaCarousel>[0];\n  scrollNext: () => void;\n  scrollPrev: () => void;\n} & CarouselProps;\n\nconst CarouselContext = createContext<CarouselContextProps | null>(null);\n\nfunction useCarousel() {\n  const context = useContext(CarouselContext);\n\n  if (!context) {\n    throw new Error(\"useCarousel must be used within a <Carousel />\");\n  }\n\n  return context;\n}\n\ntype UseCarouselLogicOptions = {\n  options: CarouselOptions;\n  orientation: \"horizontal\" | \"vertical\";\n  plugins: CarouselPlugin;\n  setApi?: (api: CarouselApi) => void;\n};\n\nfunction useCarouselLogic({\n  options,\n  orientation,\n  plugins,\n  setApi,\n}: UseCarouselLogicOptions) {\n  const [carouselRef, api] = useEmblaCarousel(\n    {\n      ...options,\n      axis: orientation === \"horizontal\" ? \"x\" : \"y\",\n    },\n    plugins,\n  );\n  const [canScrollPrevious, setCanScrollPrevious] = useState(false);\n  const [canScrollNext, setCanScrollNext] = useState(false);\n\n  const onSelect = useCallback((api: CarouselApi) => {\n    if (!api) {\n      return;\n    }\n\n    setCanScrollPrevious(api.canScrollPrev());\n    setCanScrollNext(api.canScrollNext());\n  }, []);\n\n  const scrollPrevious = useCallback(() => {\n    api?.scrollPrev();\n  }, [api]);\n\n  const scrollNext = useCallback(() => {\n    api?.scrollNext();\n  }, [api]);\n\n  const handleKeyDown = useCallback(\n    (event: React.KeyboardEvent<HTMLDivElement>) => {\n      if (event.key === \"ArrowLeft\") {\n        event.preventDefault();\n        scrollPrevious();\n      } else if (event.key === \"ArrowRight\") {\n        event.preventDefault();\n        scrollNext();\n      }\n    },\n    [scrollPrevious, scrollNext],\n  );\n\n  useEffect(() => {\n    if (!api || !setApi) {\n      return;\n    }\n\n    setApi(api);\n  }, [api, setApi]);\n\n  useEffect(() => {\n    if (!api) {\n      return;\n    }\n\n    api.on(\"reInit\", onSelect);\n    api.on(\"select\", onSelect);\n\n    const rafId = requestAnimationFrame(() => {\n      onSelect(api);\n    });\n\n    return () => {\n      api?.off(\"select\", onSelect);\n      api?.off(\"reInit\", onSelect);\n      cancelAnimationFrame(rafId);\n    };\n  }, [api, onSelect]);\n\n  return {\n    api,\n    canScrollNext,\n    canScrollPrevious,\n    carouselRef,\n    handleKeyDown,\n    scrollNext,\n    scrollPrevious,\n  };\n}\n\nconst Carousel = forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & CarouselProps\n>(\n  (\n    {\n      children,\n      className,\n      opts,\n      orientation = \"horizontal\",\n      plugins,\n      setApi,\n      ...props\n    },\n    ref,\n  ) => {\n    const {\n      api,\n      canScrollNext,\n      canScrollPrevious,\n      carouselRef,\n      handleKeyDown,\n      scrollNext,\n      scrollPrevious,\n    } = useCarouselLogic({ options: opts, orientation, plugins, setApi });\n\n    const contextValue = useMemo(\n      () => ({\n        api,\n        canScrollNext,\n        canScrollPrev: canScrollPrevious,\n        carouselRef,\n        opts,\n        orientation:\n          orientation || (opts?.axis === \"y\" ? \"vertical\" : \"horizontal\"),\n        scrollNext,\n        scrollPrev: scrollPrevious,\n      }),\n      [\n        api,\n        canScrollNext,\n        canScrollPrevious,\n        carouselRef,\n        opts,\n        orientation,\n        scrollNext,\n        scrollPrevious,\n      ],\n    );\n\n    return (\n      <CarouselContext.Provider value={contextValue}>\n        <div\n          aria-roledescription=\"carousel\"\n          className={cn(\"relative\", className)}\n          onKeyDownCapture={handleKeyDown}\n          ref={ref}\n          role=\"region\"\n          {...props}\n        >\n          {children}\n        </div>\n      </CarouselContext.Provider>\n    );\n  },\n);\nCarousel.displayName = \"Carousel\";\n\nconst CarouselContent = forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n  const { carouselRef, orientation } = useCarousel();\n\n  return (\n    <div className=\"overflow-hidden\" ref={carouselRef}>\n      <div\n        className={cn(\n          \"flex\",\n          orientation === \"horizontal\" ? \"-ml-4\" : \"-mt-4 flex-col\",\n          className,\n        )}\n        ref={ref}\n        {...props}\n      />\n    </div>\n  );\n});\nCarouselContent.displayName = \"CarouselContent\";\n\nconst CarouselItem = forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n  const { orientation } = useCarousel();\n\n  return (\n    <div\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      ref={ref}\n      role=\"group\"\n      {...props}\n    />\n  );\n});\nCarouselItem.displayName = \"CarouselItem\";\n\nconst CarouselPrevious = forwardRef<\n  HTMLButtonElement,\n  React.ComponentProps<typeof Button>\n>(({ className, size = \"icon\", variant = \"outline\", ...props }, ref) => {\n  const { canScrollPrev, orientation, scrollPrev } = useCarousel();\n\n  return (\n    <Button\n      className={cn(\n        \"absolute size-8 rounded-full\",\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      ref={ref}\n      size={size}\n      variant={variant}\n      {...props}\n    >\n      <ArrowLeft className=\"size-4\" />\n      <span className=\"sr-only\">Previous slide</span>\n    </Button>\n  );\n});\nCarouselPrevious.displayName = \"CarouselPrevious\";\n\nconst CarouselNext = forwardRef<\n  HTMLButtonElement,\n  React.ComponentProps<typeof Button>\n>(({ className, size = \"icon\", variant = \"outline\", ...props }, ref) => {\n  const { canScrollNext, orientation, scrollNext } = useCarousel();\n\n  return (\n    <Button\n      className={cn(\n        \"absolute size-8 rounded-full\",\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      ref={ref}\n      size={size}\n      variant={variant}\n      {...props}\n    >\n      <ArrowRight className=\"size-4\" />\n      <span className=\"sr-only\">Next slide</span>\n    </Button>\n  );\n});\nCarouselNext.displayName = \"CarouselNext\";\n\nexport {\n  Carousel,\n  type CarouselApi,\n  CarouselContent,\n  CarouselItem,\n  CarouselNext,\n  CarouselPrevious,\n};\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
