{
  "title": "Scroll Area",
  "description": "Augments native scroll functionality for custom, cross-browser styling with smooth scrolling, tactile spring thumb interactions, and transition physics.",
  "name": "motion-scroll-area",
  "dependencies": [
    "motion",
    "@base-ui/react"
  ],
  "registryDependencies": [],
  "type": "registry:ui",
  "files": [
    {
      "path": "components/paceui/motion/scroll-area.tsx",
      "type": "registry:ui",
      "content": "import * as React from \"react\";\n\nimport { ScrollArea as ScrollAreaPrimitive } from \"@base-ui/react/scroll-area\";\nimport { type Transition, motion } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface ScrollAreaContextValue {\n    transition?: Transition;\n}\n\nconst ScrollAreaContext = React.createContext<ScrollAreaContextValue>({\n    transition: undefined,\n});\n\nexport interface ScrollAreaProps extends ScrollAreaPrimitive.Root.Props {\n    transition?: Transition;\n}\n\nexport interface ScrollAreaViewportProps extends ScrollAreaPrimitive.Viewport.Props {}\n\nexport interface ScrollBarProps extends ScrollAreaPrimitive.Scrollbar.Props {\n    transition?: Transition;\n}\n\nexport interface ScrollAreaThumbProps extends ScrollAreaPrimitive.Thumb.Props {\n    transition?: Transition;\n}\n\nexport interface ScrollAreaCornerProps extends ScrollAreaPrimitive.Corner.Props {}\n\nfunction ScrollArea({ className, children, transition, ...props }: ScrollAreaProps) {\n    const hasViewport = React.Children.toArray(children).some(\n        (child) =>\n            React.isValidElement(child) &&\n            (child.type === ScrollAreaViewport ||\n                (child.props as { \"data-slot\"?: string })?.[\"data-slot\"] === \"scroll-area-viewport\"),\n    );\n\n    const hasScrollbar = React.Children.toArray(children).some(\n        (child) =>\n            React.isValidElement(child) &&\n            (child.type === ScrollBar ||\n                (child.props as { \"data-slot\"?: string })?.[\"data-slot\"] === \"scroll-area-scrollbar\"),\n    );\n\n    return (\n        <ScrollAreaContext.Provider value={{ transition }}>\n            <ScrollAreaPrimitive.Root\n                data-slot=\"scroll-area\"\n                className={cn(\"relative overflow-hidden\", className)}\n                {...props}>\n                {hasViewport ? children : <ScrollAreaViewport>{children}</ScrollAreaViewport>}\n                {!hasScrollbar && <ScrollBar transition={transition} />}\n                <ScrollAreaCorner />\n            </ScrollAreaPrimitive.Root>\n        </ScrollAreaContext.Provider>\n    );\n}\n\nconst ScrollAreaViewport = React.forwardRef<HTMLDivElement, ScrollAreaViewportProps>(function ScrollAreaViewport(\n    { className, children, ...props },\n    forwardedRef,\n) {\n    const internalRef = React.useRef<HTMLDivElement>(null);\n\n    React.useImperativeHandle(forwardedRef, () => internalRef.current as HTMLDivElement);\n\n    React.useEffect(() => {\n        const viewport = internalRef.current;\n        if (!viewport) return;\n\n        let targetY = viewport.scrollTop;\n        let targetX = viewport.scrollLeft;\n        let currentY = viewport.scrollTop;\n        let currentX = viewport.scrollLeft;\n        let rafId: number | null = null;\n        let isRunning = false;\n\n        const lerp = (start: number, end: number, factor: number) => start + (end - start) * factor;\n\n        const tick = () => {\n            const maxY = viewport.scrollHeight - viewport.clientHeight;\n            const maxX = viewport.scrollWidth - viewport.clientWidth;\n\n            targetY = Math.max(0, Math.min(maxY, targetY));\n            targetX = Math.max(0, Math.min(maxX, targetX));\n\n            currentY = lerp(currentY, targetY, 0.09);\n            currentX = lerp(currentX, targetX, 0.09);\n\n            if (Math.abs(targetY - currentY) < 0.5 && Math.abs(targetX - currentX) < 0.5) {\n                viewport.scrollTop = targetY;\n                viewport.scrollLeft = targetX;\n                currentY = targetY;\n                currentX = targetX;\n                isRunning = false;\n                rafId = null;\n                return;\n            }\n\n            viewport.scrollTop = currentY;\n            viewport.scrollLeft = currentX;\n            rafId = requestAnimationFrame(tick);\n        };\n\n        const onWheel = (e: WheelEvent) => {\n            const maxY = viewport.scrollHeight - viewport.clientHeight;\n            const maxX = viewport.scrollWidth - viewport.clientWidth;\n\n            if (maxY <= 0 && maxX <= 0) return;\n\n            if (!isRunning) {\n                currentY = viewport.scrollTop;\n                currentX = viewport.scrollLeft;\n                targetY = viewport.scrollTop;\n                targetX = viewport.scrollLeft;\n            }\n\n            const multiplier =\n                e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? Math.max(viewport.clientHeight, viewport.clientWidth) : 1;\n            const rawDeltaY = e.deltaY * multiplier;\n            const rawDeltaX = e.deltaX * multiplier;\n\n            let deltaY = rawDeltaY;\n            let deltaX = rawDeltaX;\n\n            if (maxX > 0 && maxY <= 0 && rawDeltaX === 0 && rawDeltaY !== 0) {\n                deltaX = rawDeltaY;\n                deltaY = 0;\n            }\n\n            const nextTargetY = Math.max(0, Math.min(maxY, targetY + deltaY));\n            const nextTargetX = Math.max(0, Math.min(maxX, targetX + deltaX));\n\n            if (nextTargetY !== targetY || nextTargetX !== targetX) {\n                e.preventDefault();\n                targetY = nextTargetY;\n                targetX = nextTargetX;\n\n                if (!isRunning) {\n                    isRunning = true;\n                    rafId = requestAnimationFrame(tick);\n                }\n            }\n        };\n\n        const onScroll = () => {\n            if (!isRunning) {\n                currentY = viewport.scrollTop;\n                currentX = viewport.scrollLeft;\n                targetY = viewport.scrollTop;\n                targetX = viewport.scrollLeft;\n            }\n        };\n\n        viewport.addEventListener(\"wheel\", onWheel, { passive: false });\n        viewport.addEventListener(\"scroll\", onScroll, { passive: true });\n\n        return () => {\n            viewport.removeEventListener(\"wheel\", onWheel);\n            viewport.removeEventListener(\"scroll\", onScroll);\n            if (rafId !== null) cancelAnimationFrame(rafId);\n        };\n    }, []);\n\n    return (\n        <ScrollAreaPrimitive.Viewport\n            ref={internalRef}\n            data-slot=\"scroll-area-viewport\"\n            className={cn(\n                \"focus-visible:ring-ring/50 size-full overflow-auto rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1\",\n                className,\n            )}\n            {...props}>\n            {children}\n        </ScrollAreaPrimitive.Viewport>\n    );\n});\n\nfunction ScrollBar({ className, orientation = \"vertical\", transition: explicitTransition, ...props }: ScrollBarProps) {\n    const context = React.useContext(ScrollAreaContext);\n    const transition = explicitTransition ??\n        context.transition ?? {\n            type: \"spring\",\n            stiffness: 400,\n            damping: 28,\n        };\n\n    return (\n        <ScrollAreaPrimitive.Scrollbar\n            data-slot=\"scroll-area-scrollbar\"\n            data-orientation={orientation}\n            orientation={orientation}\n            className={cn(\n                \"flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent\",\n                className,\n            )}\n            {...props}>\n            <ScrollAreaThumb transition={transition} />\n        </ScrollAreaPrimitive.Scrollbar>\n    );\n}\n\nfunction ScrollAreaThumb({ className, transition: explicitTransition, ...props }: ScrollAreaThumbProps) {\n    const context = React.useContext(ScrollAreaContext);\n    const transition = explicitTransition ??\n        context.transition ?? {\n            type: \"spring\",\n            stiffness: 400,\n            damping: 28,\n        };\n\n    return (\n        <ScrollAreaPrimitive.Thumb\n            data-slot=\"scroll-area-thumb\"\n            className={cn(\"bg-border relative flex-1 rounded-full\", className)}\n            render={(thumbProps) => (\n                <motion.div\n                    {...thumbProps}\n                    whileHover={{ scale: 1.15 }}\n                    whileTap={{ scale: 0.95 }}\n                    transition={transition}\n                    className={cn(\"bg-border relative flex-1 rounded-full\", className)}\n                />\n            )}\n            {...props}\n        />\n    );\n}\n\nfunction ScrollAreaCorner({ className, ...props }: ScrollAreaCornerProps) {\n    return <ScrollAreaPrimitive.Corner data-slot=\"scroll-area-corner\" className={cn(className)} {...props} />;\n}\n\nexport { ScrollArea, ScrollBar, ScrollAreaViewport, ScrollAreaThumb, ScrollAreaCorner };\n"
    }
  ],
  "mcp": {
    "description": "Augments native scroll functionality with custom cross-browser scrollbars. Provides smooth scrolling and tactile spring thumb physics.",
    "tags": [
      "component",
      "scroll-area",
      "scroll",
      "overflow",
      "paceui",
      "animation"
    ]
  }
}