Scroll Area
optics/scroll-areaFreeComponent
Augments native scroll functionality for custom, cross-browser styling.
See it running
Open the demo on optics
optics.agusmayol.com.ar/components/scroll-areaInstall it
npx shadcn@latest add https://optics.agusmayol.com.ar/r/scroll-area.jsonSource
1"use client";23import * as React from "react";45import { cn } from "@/registry/optics/lib/utils";67const ScrollArea = React.forwardRef(8 (9 {10 className = "",11 children = null,12 scrollHideDelay = 0,13 viewportClassName = "",14 maskClassName = "",15 maskHeight = 30,16 maskColor = undefined,17 ...props18 },19 ref,20 ) => {21 const [showMask, setShowMask] = React.useState({22 top: false,23 bottom: false,24 left: false,25 right: false,26 });27 const viewportRef = React.useRef(null);2829 const checkScrollability = React.useCallback(() => {30 const element = viewportRef.current;31 if (!element) return;3233 const {34 scrollTop,35 scrollLeft,36 scrollWidth,37 clientWidth,38 scrollHeight,39 clientHeight,40 } = element;41 setShowMask((prev) => ({42 ...prev,43 top: scrollTop > 0,44 bottom: scrollTop + clientHeight < scrollHeight - 1,45 left: scrollLeft > 0,46 right: scrollLeft + clientWidth < scrollWidth - 1,47 }));48 }, []);4950 React.useEffect(() => {51 if (typeof window === "undefined") return;5253 const element = viewportRef.current;54 if (!element) return;5556 const controller = new AbortController();57 const { signal } = controller;5859 const resizeObserver = new ResizeObserver(checkScrollability);60 resizeObserver.observe(element);6162 element.addEventListener("scroll", checkScrollability, { signal });63 window.addEventListener("resize", checkScrollability, { signal });6465 checkScrollability();6667 return () => {68 controller.abort();69 resizeObserver.disconnect();70 };71 }, [checkScrollability]);7273 return (74 <div75 ref={ref}76 role="group"77 data-slot="scroll-area"78 aria-roledescription="scroll area"79 className={cn("relative overflow-hidden", className)}80 {...props}81 >82 <div83 ref={viewportRef}84 data-slot="scroll-area-viewport"85 className={cn(86 "size-full overflow-auto rounded-[inherit] scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent",87 viewportClassName,88 )}89 tabIndex={0}90 >91 {children}92 </div>9394 {maskHeight > 0 && (95 <ScrollMask96 showMask={showMask}97 className={maskClassName}98 maskHeight={maskHeight}99 maskColor={maskColor}100 />101 )}102 </div>103 );104 },105);106107ScrollArea.displayName = "ScrollArea";108109const ScrollBar = () => null;110111const ScrollMask = ({112 showMask,113 maskHeight,114 maskColor = undefined,115 className = "",116 ...props117}) => {118// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from optics
All 79 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | optics | Components | Free | 5 deps | |
| Alertalert | optics | Components | Free | 4 deps | |
| Alert Dialogalert-dialog | optics | Components | Free | 4 deps | |
| Aspect Ratioaspect-ratio | optics | Components | Free | 2 deps | |
| Auto Heightauto-height | optics | Components | Free | 3 deps | |
| Avataravatar | optics | Components | Free | 3 deps | |
| Badgebadge | optics | Components | Free | 4 deps | |
| Breadcrumbbreadcrumb | optics | Components | Free | 4 deps |
