{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "carousel-free-scroll",
  "title": "Carousel Free Scroll",
  "description": "A drag-and-fling row of cards that glides with momentum and stops anywhere, with edge fades, scroll buttons and a progress bar.",
  "dependencies": [
    "embla-carousel-react@^8.6.0",
    "embla-carousel@^8.6.0",
    "lucide-react"
  ],
  "registryDependencies": [
    "@tealmac/carousel-kit"
  ],
  "files": [
    {
      "path": "registry/tealmac/carousel-free-scroll.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport {\n  Carousel,\n  CarouselNext,\n  CarouselPrevious,\n  CarouselSlide,\n  CarouselTrack,\n  CarouselViewport,\n  useCarousel,\n  type CarouselApi,\n  type CarouselItem,\n} from \"@/registry/tealmac/carousel-kit\"\n\nfunction ScrollProgress() {\n  const { api } = useCarousel()\n  const bar = React.useRef<HTMLSpanElement>(null)\n  React.useEffect(() => {\n    if (!api) return\n    const update = () => {\n      const progress = Math.max(0, Math.min(1, api.scrollProgress()))\n      bar.current?.style.setProperty(\"--progress\", String(progress))\n    }\n    update()\n    api.on(\"scroll\", update).on(\"reInit\", update)\n    return () => {\n      api.off(\"scroll\", update).off(\"reInit\", update)\n    }\n  }, [api])\n  return (\n    <span\n      aria-hidden\n      className=\"bg-muted block h-1 w-full overflow-hidden rounded-full\"\n    >\n      <span\n        ref={bar}\n        className=\"bg-primary block h-full w-full origin-[0%_50%] [transform:scaleX(calc(0.12+var(--progress,0)*0.88))] rounded-full rtl:origin-[100%_50%]\"\n      />\n    </span>\n  )\n}\n\nexport interface CarouselFreeScrollProps {\n  items: CarouselItem[]\n  label: string\n  /** Fade the edges where more content is hidden. @default true */\n  edgeFade?: boolean\n  /** Class for each slide: sets its width, e.g. \"basis-40\". */\n  slideClassName?: string\n  className?: string\n  /** Called with the snap index whenever the selection changes. */\n  onIndexChange?: (index: number) => void\n  /** Receive the Embla API, for controls placed outside the carousel. */\n  setApi?: (api: CarouselApi) => void\n}\n\n/** Drag or fling a row of cards of any width: it glides with momentum and stops anywhere, with a progress bar. For chips, cards and media shelves. */\nexport function CarouselFreeScroll({\n  items,\n  label,\n  edgeFade = true,\n  slideClassName = \"basis-44\",\n  className,\n  onIndexChange,\n  setApi,\n}: CarouselFreeScrollProps) {\n  return (\n    <Carousel\n      label={label}\n      onIndexChange={onIndexChange}\n      setApi={setApi}\n      opts={{ dragFree: true, align: \"start\", containScroll: \"trimSnaps\" }}\n      className={className}\n    >\n      <CarouselViewport\n        className={cn(\n          edgeFade &&\n            \"[mask-image:linear-gradient(to_right,transparent,#000_24px,#000_calc(100%-24px),transparent)] motion-reduce:[mask-image:none]\"\n        )}\n      >\n        <CarouselTrack className=\"gap-3 px-4\">\n          {items.map((item) => (\n            <CarouselSlide\n              key={item.id}\n              className={cn(\"grow-0\", slideClassName)}\n            >\n              {item.content}\n            </CarouselSlide>\n          ))}\n        </CarouselTrack>\n      </CarouselViewport>\n      <div className=\"mt-3 flex items-center gap-3\">\n        <CarouselPrevious aria-label=\"Scroll back\" />\n        <ScrollProgress />\n        <CarouselNext aria-label=\"Scroll forward\" />\n      </div>\n    </Carousel>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}