{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "scroll-area",
  "title": "Scroll Area",
  "author": "AlgorizeTech",
  "description": "Ask the user to pick one of 3 scroll area variants: https://avaraui.com/view/scroll-area. This item holds 3 of them as looks of one component (default, minimal, glass). Skip the question if the user already named one. A scroll container with styled, optionally auto-hiding scrollbars in either axis. Use it to keep scrollbar appearance consistent across browsers.",
  "dependencies": [],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/custom/blocks/ScrollAreaBlock.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport React, { forwardRef } from \"react\";\nimport { getThemeStyles } from \"./scroll-area-theme\";\n\n// ==========================================\n// VARIANT & COLOR CONFIGURATION\n// ==========================================\nexport type ScrollAreaVariant = \"default\" | \"minimal\" | \"glass\";\nexport type ScrollAreaColor =\n  | \"indigo\"\n  | \"rose\"\n  | \"emerald\"\n  | \"zinc\"\n  | (string & {});\n\n// ==========================================\n// SCROLL AREA COMPONENT\n// ==========================================\nexport interface ScrollAreaBlockProps\n  extends React.HTMLAttributes<HTMLDivElement> {\n  color?: ScrollAreaColor;\n  variant?: ScrollAreaVariant;\n  orientation?: \"vertical\" | \"horizontal\" | \"both\";\n  hideTrack?: boolean;\n  autoHide?: boolean;\n  /**\n   * Horizontal areas only: a mouse wheel moves the content sideways, so\n   * scrolling up/down pans left/right. The wheel is handed back to the page\n   * once the area reaches either end. Default true.\n   */\n  wheelToHorizontal?: boolean;\n}\n\nexport const ScrollAreaBlock = forwardRef<HTMLDivElement, ScrollAreaBlockProps>(\n  (\n    {\n      children,\n      color = \"primary\",\n      variant = \"default\",\n      orientation = \"vertical\",\n      hideTrack = false,\n      autoHide = false,\n      wheelToHorizontal = true,\n      className,\n      onScroll,\n      ...props\n    },\n    ref,\n  ) => {\n    const { style } = getThemeStyles(color as string);\n    const [isScrolling, setIsScrolling] = React.useState(false);\n    const scrollTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n    const nodeRef = React.useRef<HTMLDivElement | null>(null);\n\n    const assignRef = (node: HTMLDivElement | null) => {\n      nodeRef.current = node;\n      if (typeof ref === \"function\") ref(node);\n      else if (ref) ref.current = node;\n    };\n\n    // Wheel → sideways pan. Registered natively with passive:false because\n    // React's onWheel is passive and could not preventDefault the page scroll.\n    React.useEffect(() => {\n      const node = nodeRef.current;\n      if (!node || orientation !== \"horizontal\" || !wheelToHorizontal) return;\n\n      const onWheel = (e: WheelEvent) => {\n        // A trackpad swipe or shift+wheel already scrolls sideways natively.\n        if (e.shiftKey || Math.abs(e.deltaX) >= Math.abs(e.deltaY)) return;\n        // deltaMode 1 = lines (Firefox); ~16px per line reads like Chrome.\n        const delta = e.deltaMode === 1 ? e.deltaY * 16 : e.deltaY;\n        const max = node.scrollWidth - node.clientWidth;\n        if (max <= 0) return;\n        const atStart = node.scrollLeft <= 0 && delta < 0;\n        const atEnd = node.scrollLeft >= max - 1 && delta > 0;\n        // At an edge, leave the wheel to the page so the user is never trapped.\n        if (atStart || atEnd) return;\n        e.preventDefault();\n        node.scrollLeft += delta;\n      };\n\n      node.addEventListener(\"wheel\", onWheel, { passive: false });\n      return () => node.removeEventListener(\"wheel\", onWheel);\n    }, [orientation, wheelToHorizontal]);\n\n    const instanceId = React.useId().replace(/:/g, \"\");\n    const uniqueClass = `custom-scroll-${instanceId}`;\n\n    const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {\n      if (autoHide) {\n        setIsScrolling(true);\n        if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);\n        scrollTimeoutRef.current = setTimeout(() => {\n          setIsScrolling(false);\n        }, 1000);\n      }\n      onScroll?.(e);\n    };\n\n    React.useEffect(() => {\n      return () => {\n        if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);\n      };\n    }, []);\n\n    const variantStyles = {\n      default: {\n        width: \"6px\",\n        height: \"6px\",\n        radius: \"9999px\",\n      },\n      minimal: {\n        width: \"4px\",\n        height: \"4px\",\n        radius: \"9999px\",\n      },\n      glass: {\n        width: \"7px\",\n        height: \"7px\",\n        radius: \"9999px\",\n      },\n    };\n\n    const v = variantStyles[variant] || variantStyles.default;\n\n    const overflowClass = {\n      vertical: \"overflow-y-auto overflow-x-hidden\",\n      horizontal: \"overflow-x-auto overflow-y-hidden\",\n      both: \"overflow-auto\",\n    }[orientation];\n\n    return (\n      <div\n        ref={assignRef}\n        onScroll={handleScroll}\n        className={cn(\n          \"relative\",\n          overflowClass,\n          uniqueClass,\n          autoHide && \"auto-hide-scroll\",\n          isScrolling && \"is-scrolling\",\n          className,\n        )}\n        style={style}\n        {...props}\n      >\n        <style>{`\n          /* Webkit Browsers (Chrome, Safari, Edge) */\n          .${uniqueClass}::-webkit-scrollbar {\n            width: ${v.width} !important;\n            height: ${v.height} !important;\n            display: block !important;\n          }\n\n          /* Light Mode Track & Thumb */\n          .${uniqueClass}::-webkit-scrollbar-track {\n            background-color: ${hideTrack ? \"transparent\" : variant === \"glass\" ? \"rgba(0, 0, 0, 0.06)\" : \"var(--scroll-track, rgba(0, 0, 0, 0.05))\"} !important;\n            border-radius: ${v.radius};\n            display: block !important;\n            border: none !important;\n            box-shadow: none !important;\n          }\n          .${uniqueClass}::-webkit-scrollbar-thumb {\n            background-color: ${variant === \"glass\" ? \"rgba(0, 0, 0, 0.35)\" : \"var(--scroll-thumb, rgba(0, 0, 0, 0.22))\"} !important;\n            border-radius: ${v.radius};\n            min-height: 24px !important;\n            min-width: 24px !important;\n            display: block !important;\n          }\n          .${uniqueClass}::-webkit-scrollbar-thumb:hover {\n            background-color: ${variant === \"glass\" ? \"rgba(0, 0, 0, 0.55)\" : \"var(--scroll-thumb-hover, rgba(0, 0, 0, 0.4))\"} !important;\n          }\n\n          /* Dark Mode Track & Thumb */\n          .dark .${uniqueClass}::-webkit-scrollbar-track,\n          html.dark .${uniqueClass}::-webkit-scrollbar-track,\n          [data-theme=\"dark\"] .${uniqueClass}::-webkit-scrollbar-track {\n            background-color: ${hideTrack ? \"transparent\" : variant === \"glass\" ? \"rgba(255, 255, 255, 0.12)\" : \"var(--scroll-track, rgba(255, 255, 255, 0.08))\"} !important;\n          }\n          .dark .${uniqueClass}::-webkit-scrollbar-thumb,\n          html.dark .${uniqueClass}::-webkit-scrollbar-thumb,\n          [data-theme=\"dark\"] .${uniqueClass}::-webkit-scrollbar-thumb {\n            background-color: ${variant === \"glass\" ? \"rgba(255, 255, 255, 0.45)\" : \"var(--scroll-thumb, rgba(255, 255, 255, 0.28))\"} !important;\n          }\n          .dark .${uniqueClass}::-webkit-scrollbar-thumb:hover,\n          html.dark .${uniqueClass}::-webkit-scrollbar-thumb:hover,\n          [data-theme=\"dark\"] .${uniqueClass}::-webkit-scrollbar-thumb:hover {\n            background-color: ${variant === \"glass\" ? \"rgba(255, 255, 255, 0.7)\" : \"var(--scroll-thumb-hover, rgba(255, 255, 255, 0.5))\"} !important;\n          }\n\n          /* Firefox standard */\n          .${uniqueClass} {\n            scrollbar-width: thin !important;\n            scrollbar-color: ${variant === \"glass\" ? \"rgba(0, 0, 0, 0.35)\" : \"var(--scroll-thumb, rgba(0, 0, 0, 0.22))\"} ${hideTrack ? \"transparent\" : \"var(--scroll-track, rgba(0, 0, 0, 0.05))\"} !important;\n          }\n          .dark .${uniqueClass},\n          html.dark .${uniqueClass},\n          [data-theme=\"dark\"] .${uniqueClass} {\n            scrollbar-color: ${variant === \"glass\" ? \"rgba(255, 255, 255, 0.45)\" : \"var(--scroll-thumb, rgba(255, 255, 255, 0.28))\"} ${hideTrack ? \"transparent\" : \"var(--scroll-track, rgba(255, 255, 255, 0.08))\"} !important;\n          }\n\n          /* Auto-Hide Logic: Hide when idle, show on hover/focus/scroll */\n          .${uniqueClass}.auto-hide-scroll:not(:hover):not(:focus-within):not(.is-scrolling)::-webkit-scrollbar-thumb {\n            background-color: transparent !important;\n          }\n          .${uniqueClass}.auto-hide-scroll:not(:hover):not(:focus-within):not(.is-scrolling)::-webkit-scrollbar-track {\n            background-color: transparent !important;\n          }\n          .${uniqueClass}.auto-hide-scroll:not(:hover):not(:focus-within):not(.is-scrolling) {\n            scrollbar-color: transparent transparent !important;\n          }\n        `}</style>\n        {children}\n      </div>\n    );\n  },\n);\nScrollAreaBlock.displayName = \"ScrollAreaBlock\";\n",
      "type": "registry:component",
      "target": "components/custom/blocks/ScrollAreaBlock.tsx"
    },
    {
      "path": "src/components/custom/blocks/scroll-area-theme.ts",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nconst colorThemes: Record<string, Record<string, string>> = {\n  primary: {\n    thumb: \"rgba(161, 161, 170, 0.4)\",\n    thumbHover: \"rgba(113, 113, 122, 0.7)\",\n    track: \"rgba(228, 228, 231, 0.25)\",\n  },\n  indigo: {\n    thumb: \"#4f46e5\",\n    thumbHover: \"#4338ca\",\n    track: \"rgba(79, 70, 229, 0.18)\",\n  },\n  rose: {\n    thumb: \"#e11d48\",\n    thumbHover: \"#be123c\",\n    track: \"rgba(225, 29, 72, 0.18)\",\n  },\n  emerald: {\n    thumb: \"#10b981\",\n    thumbHover: \"#059669\",\n    track: \"rgba(16, 185, 129, 0.18)\",\n  },\n  zinc: {\n    thumb: \"#71717a\",\n    thumbHover: \"#52525b\",\n    track: \"rgba(113, 113, 122, 0.2)\",\n  },\n};\n\nconst hexToRgb = (hex: string) => {\n  let cleanHex = hex.replace(/^#/, \"\");\n  if (cleanHex.length === 3)\n    cleanHex = cleanHex\n      .split(\"\")\n      .map((x) => x + x)\n      .join(\"\");\n  const result = /^([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(cleanHex);\n  return result\n    ? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}`\n    : \"99, 102, 241\";\n};\n\nexport const getThemeStyles = (color: string) => {\n  if (!color || color === \"primary\" || color === \"default\") {\n    return { style: {} };\n  }\n  if (color && color.startsWith(\"#\")) {\n    const rgb = hexToRgb(color);\n    return {\n      style: {\n        \"--scroll-thumb\": color,\n        \"--scroll-thumb-hover\": `rgba(${rgb}, 0.8)`,\n        \"--scroll-track\": `rgba(${rgb}, 0.1)`,\n      } as React.CSSProperties,\n    };\n  }\n  const theme = colorThemes[color];\n  if (!theme) return { style: {} };\n  return {\n    style: {\n      \"--scroll-thumb\": theme.thumb,\n      \"--scroll-thumb-hover\": theme.thumbHover,\n      \"--scroll-track\": theme.track,\n    } as React.CSSProperties,\n  };\n};\n",
      "type": "registry:component",
      "target": "components/custom/blocks/scroll-area-theme.ts"
    }
  ],
  "docs": "Named exports: ScrollAreaBlock, getThemeStyles (from scroll-area-theme.ts, which installs alongside). `orientation` is vertical | horizontal | both; `autoHide` fades the bar when idle and `hideTrack` drops the track entirely. Needs a bounded height to scroll — give it an explicit height or a flex parent.",
  "categories": [
    "layout",
    "scroll"
  ],
  "type": "registry:component"
}
