{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "horizontal-scroll",
  "title": "Horizontal Scroll",
  "description": "Pins a row of images and scrolls it sideways with the page, then releases to normal scroll.",
  "dependencies": [
    "gsap",
    "@gsap/react"
  ],
  "registryDependencies": [
    "https://trytweenly.vercel.app/r/use-reduced-motion.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/new-york/horizontal-scroll/horizontal-scroll.tsx",
      "content": "\"use client\"\n\nimport { useRef } from \"react\"\nimport gsap from \"gsap\"\nimport { ScrollTrigger } from \"gsap/ScrollTrigger\"\nimport { useGSAP } from \"@gsap/react\"\nimport { useReducedMotion } from \"@/registry/new-york/hooks/use-reduced-motion\"\nimport { cn } from \"@/lib/utils\"\n\ngsap.registerPlugin(useGSAP, ScrollTrigger)\n\nexport interface HorizontalScrollItem {\n  src: string\n  alt: string\n  caption?: string\n}\n\nexport interface HorizontalScrollProps {\n  items: HorizontalScrollItem[]\n  /** Width of each image (any CSS length). Default: \"min(70cqw, 520px)\" */\n  itemWidth?: string\n  /** Image aspect ratio. Default: \"4 / 5\" */\n  aspectRatio?: string\n  /** Gap between images in px. Default: 24 */\n  gap?: number\n  /** Scroll distance multiplier. Higher = slower horizontal travel. Default: 1 */\n  speed?: number\n  /** Smoothing applied to the scroll link, in seconds. Default: 1 */\n  scrub?: number\n  /** Subtle zoom on each image as it crosses the viewport. Default: true */\n  parallax?: boolean\n  /** Scroll container to track instead of the window (selector or element). */\n  scroller?: string | HTMLElement\n  /** Height of the pinned section (any CSS length). Default: \"100vh\" */\n  height?: string\n  className?: string\n  itemClassName?: string\n}\n\nexport function HorizontalScroll({\n  items,\n  itemWidth = \"min(70cqw, 520px)\",\n  aspectRatio = \"4 / 5\",\n  gap = 24,\n  speed = 1,\n  scrub = 1,\n  parallax = true,\n  scroller,\n  height = \"100vh\",\n  className,\n  itemClassName,\n}: HorizontalScrollProps) {\n  const sectionRef = useRef<HTMLElement>(null)\n  const trackRef = useRef<HTMLDivElement>(null)\n  const reduced = useReducedMotion()\n\n  useGSAP(\n    () => {\n      const section = sectionRef.current\n      const track = trackRef.current\n      if (!section || !track || reduced) return\n\n      const distance = () => Math.max(track.scrollWidth - section.clientWidth, 0)\n\n      const tween = gsap.to(track, {\n        x: () => -distance(),\n        ease: \"none\",\n        scrollTrigger: {\n          trigger: section,\n          scroller,\n          start: \"top top\",\n          end: () => `+=${distance() * speed}`,\n          pin: true,\n          scrub: scrub || true,\n          invalidateOnRefresh: true,\n        },\n      })\n\n      if (parallax) {\n        gsap.utils.toArray<HTMLElement>(\"[data-media]\", track).forEach((img) => {\n          gsap.fromTo(\n            img,\n            { scale: 1.25 },\n            {\n              scale: 1,\n              ease: \"none\",\n              scrollTrigger: {\n                trigger: img.parentElement,\n                scroller,\n                containerAnimation: tween,\n                start: \"left right\",\n                end: \"center center\",\n                scrub: true,\n              },\n            }\n          )\n        })\n      }\n    },\n    { scope: sectionRef, dependencies: [items, gap, speed, scrub, parallax, scroller, reduced], revertOnUpdate: true }\n  )\n\n  return (\n    <section\n      ref={sectionRef}\n      className={cn(\"relative flex w-full items-center overflow-hidden [container-type:inline-size]\", reduced && \"overflow-x-auto\", className)}\n      style={{ height }}\n    >\n      <div ref={trackRef} className=\"flex w-max px-[8cqw] will-change-transform\" style={{ gap }}>\n        {items.map((item, i) => (\n          <figure key={i} className=\"shrink-0\" style={{ width: itemWidth }}>\n            <div className={cn(\"overflow-hidden rounded-2xl bg-muted\", itemClassName)} style={{ aspectRatio }}>\n              <img data-media src={item.src} alt={item.alt} className=\"size-full object-cover\" draggable={false} />\n            </div>\n            {item.caption && (\n              <figcaption className=\"mt-3 flex items-baseline gap-3 text-sm text-muted-foreground\">\n                <span className=\"font-mono text-xs\">{String(i + 1).padStart(2, \"0\")}</span>\n                {item.caption}\n              </figcaption>\n            )}\n          </figure>\n        ))}\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "Docs, live playground and every prop: https://trytweenly.vercel.app/docs/components/horizontal-scroll",
  "type": "registry:component"
}