{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "carousel",
  "title": "Carousel",
  "description": "Scrollable content carousel with navigation controls.",
  "dependencies": [
    "@vllnt/ui@^0.3.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/carousel/carousel.tsx",
      "content": "\"use client\";\n\nimport {\n  createContext,\n  use,\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\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 = use(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  const onSelectReference = useRef(onSelect);\n\n  useEffect(() => {\n    onSelectReference.current = onSelect;\n  }, [onSelect]);\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    const notifySelection = (selectedApi: CarouselApi) => {\n      onSelectReference.current(selectedApi);\n    };\n\n    api.on(\"reInit\", notifySelection);\n    api.on(\"select\", notifySelection);\n\n    const rafId = requestAnimationFrame(() => {\n      notifySelection(api);\n    });\n\n    return () => {\n      api?.off(\"select\", notifySelection);\n      api?.off(\"reInit\", notifySelection);\n      cancelAnimationFrame(rafId);\n    };\n  }, [api]);\n\n  return {\n    api,\n    canScrollNext,\n    canScrollPrevious,\n    carouselRef,\n    handleKeyDown,\n    scrollNext,\n    scrollPrevious,\n  };\n}\n\nconst Carousel = ({\n  children,\n  className,\n  opts,\n  orientation = \"horizontal\",\n  plugins,\n  ref,\n  setApi,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement> &\n  CarouselProps & { ref?: React.Ref<HTMLDivElement> }) => {\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};\nCarousel.displayName = \"Carousel\";\n\nconst CarouselContent = ({\n  className,\n  ref,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement> & {\n  ref?: React.Ref<HTMLDivElement>;\n}) => {\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 = ({\n  className,\n  ref,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement> & {\n  ref?: React.Ref<HTMLDivElement>;\n}) => {\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 = ({\n  className,\n  ref,\n  size = \"icon\",\n  variant = \"outline\",\n  ...props\n}: React.ComponentProps<typeof Button> & {\n  ref?: React.Ref<HTMLButtonElement>;\n}) => {\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 = ({\n  className,\n  ref,\n  size = \"icon\",\n  variant = \"outline\",\n  ...props\n}: React.ComponentProps<typeof Button> & {\n  ref?: React.Ref<HTMLButtonElement>;\n}) => {\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"
    }
  ],
  "type": "registry:component",
  "version": "0.3.0",
  "stability": "stable"
}
