{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "scroll-menu",
  "type": "registry:component",
  "title": "Scroll Menu",
  "description": "Horizontal scrolling row with edge-aware arrows, mouse drag-to-scroll, native touch scrolling and a hidden scrollbar. Built on react-horizontal-scrolling-menu.",
  "author": "asmyshlyaev177 <https://asmyshlyaev177.dev>",
  "dependencies": [
    "react-horizontal-scrolling-menu",
    "lucide-react"
  ],
  "registryDependencies": [
    "button"
  ],
  "files": [
    {
      "path": "components/ui/scroll-menu.tsx",
      "target": "components/ui/scroll-menu.tsx",
      "type": "registry:component",
      "content": "'use client';\n\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport * as React from 'react';\nimport {\n  type publicApiType,\n  ScrollMenu as ScrollMenuPrimitive,\n  VisibilityContext,\n} from 'react-horizontal-scrolling-menu';\n\nimport { Button } from '@/components/ui/button';\nimport { cn } from '@/lib/utils';\n\n/** Imperative menu API; only valid inside ScrollMenu children and arrows. */\nfunction useScrollMenu(): publicApiType {\n  return React.useContext(VisibilityContext);\n}\n\ntype PrimitiveProps = React.ComponentProps<typeof ScrollMenuPrimitive>;\n\ninterface ScrollMenuProps extends Omit<\n  PrimitiveProps,\n  'LeftArrow' | 'RightArrow' | 'onMouseDown' | 'onMouseUp' | 'onMouseMove'\n> {\n  className?: string;\n  hideArrows?: boolean;\n}\n\n/**\n * Horizontal scrolling row: edge-aware arrows, drag-to-scroll, hidden\n * scrollbar. Every direct child needs a unique `itemId` prop.\n * Docs: https://react-horizontal-scrolling-menu.dev\n */\nfunction ScrollMenu({\n  className,\n  wrapperClassName,\n  scrollContainerClassName,\n  itemClassName,\n  hideArrows = false,\n  children,\n  ...props\n}: ScrollMenuProps) {\n  const drag = React.useRef({ position: 0, clicked: false, dragging: false });\n\n  const dragStop = () =>\n    window.requestAnimationFrame(() => {\n      drag.current.clicked = false;\n      drag.current.dragging = false;\n    });\n\n  const dragMove =\n    ({ scrollContainer }: publicApiType) =>\n    (ev: React.MouseEvent) => {\n      if (!drag.current.clicked || !scrollContainer.current) return;\n      const diff = drag.current.position - ev.clientX;\n      drag.current.dragging = drag.current.dragging || Math.abs(diff) > 5;\n      if (drag.current.dragging) {\n        scrollContainer.current.scrollLeft += diff;\n        drag.current.position = ev.clientX;\n      }\n    };\n\n  return (\n    <div\n      // The library's 6-rule stylesheet as utilities — a CSS side-effect\n      // import is a TS error in fresh TypeScript 6 projects.\n      className={cn(\n        'relative',\n        '[&_.react-horizontal-scrolling-menu--inner-wrapper]:flex',\n        '[&_.react-horizontal-scrolling-menu--inner-wrapper]:items-center',\n        '[&_.react-horizontal-scrolling-menu--inner-wrapper]:gap-2',\n        '[&_.react-horizontal-scrolling-menu--inner-wrapper]:overflow-y-hidden',\n        '[&_.react-horizontal-scrolling-menu--scroll-container.rtl]:[direction:rtl]',\n        className,\n      )}\n      onMouseLeave={dragStop}\n      // Swallow the click that ends a drag.\n      onClickCapture={(ev) => {\n        if (drag.current.dragging) {\n          ev.preventDefault();\n          ev.stopPropagation();\n        }\n      }}\n    >\n      <ScrollMenuPrimitive\n        LeftArrow={hideArrows ? undefined : ScrollMenuLeftArrow}\n        RightArrow={hideArrows ? undefined : ScrollMenuRightArrow}\n        onMouseDown={() => (ev) => {\n          drag.current.position = ev.clientX;\n          drag.current.clicked = true;\n        }}\n        onMouseUp={() => dragStop}\n        onMouseMove={dragMove}\n        wrapperClassName={cn('flex flex-col', wrapperClassName)}\n        scrollContainerClassName={cn(\n          'relative flex w-full overflow-y-hidden',\n          'gap-2 cursor-grab active:cursor-grabbing',\n          '[scrollbar-width:none] [&::-webkit-scrollbar]:hidden',\n          scrollContainerClassName,\n        )}\n        itemClassName={itemClassName}\n        {...props}\n      >\n        {children}\n      </ScrollMenuPrimitive>\n    </div>\n  );\n}\n\nfunction ScrollMenuLeftArrow() {\n  const api = useScrollMenu();\n  // useLeftArrowVisible() is true when the first item is visible = arrow disabled.\n  const atStart = api.useLeftArrowVisible();\n\n  return (\n    <ScrollMenuArrowButton\n      label=\"Scroll left\"\n      disabled={atStart}\n      onClick={() => api.scrollPrev()}\n    >\n      <ChevronLeft />\n    </ScrollMenuArrowButton>\n  );\n}\n\nfunction ScrollMenuRightArrow() {\n  const api = useScrollMenu();\n  const atEnd = api.useRightArrowVisible();\n\n  return (\n    <ScrollMenuArrowButton\n      label=\"Scroll right\"\n      disabled={atEnd}\n      onClick={() => api.scrollNext()}\n    >\n      <ChevronRight />\n    </ScrollMenuArrowButton>\n  );\n}\n\nfunction ScrollMenuArrowButton({\n  label,\n  disabled,\n  onClick,\n  children,\n}: {\n  label: string;\n  disabled: boolean;\n  onClick: () => void;\n  children: React.ReactNode;\n}) {\n  return (\n    <Button\n      type=\"button\"\n      variant=\"outline\"\n      size=\"icon\"\n      className=\"rounded-full disabled:opacity-30\"\n      aria-label={label}\n      disabled={disabled}\n      onClick={onClick}\n    >\n      {children}\n    </Button>\n  );\n}\n\nexport {\n  type publicApiType,\n  ScrollMenu,\n  ScrollMenuLeftArrow,\n  ScrollMenuRightArrow,\n  useScrollMenu,\n  VisibilityContext,\n};\n"
    }
  ],
  "docs": "Every direct child of <ScrollMenu> needs a unique itemId prop. Examples and full API: https://react-horizontal-scrolling-menu.dev"
}
