import { useEffect , useRef , useState } from 'react' ; import { classnames } from '../../helpers' ; import './Scroll.scss' ; const TOLERANCE = 4 ; interface ScrollProps extends React . ComponentPropsWithoutRef < 'div' > { horizontal ? : boolean ; } const Scroll = ( { children , horizontal , className , ... rest } ) => { const scrollContainerRef = useRef < HTMLDivElement > ( null ) ; const scrollChildRef = useRef < HTMLDivElement > ( null ) ; const [ showStartShadow , setShowStartShadow ] = useState ( false ) ; const [ showEndShadow , setShowEndShadow ] = useState ( false ) ; const setShadows = ( container , child ) => { if ( ! container || ! child ) { return ; } const containerRect = container . getBoundingClientRect ( ) ; const childRect = child . getBoundingClientRect ( ) ; const isOnStartEdge = horizontal ? containerRect . left - childRect . left < TOLERANCE : containerRect . top - childRect . top < TOLERANCE ; const isOnEndEdge = horizontal ? childRect . right - containerRect . right < TOLERANCE : childRect . bottom - containerRect . bottom < TOLERANCE ; setShowStartShadow ( ( previousShowStartShadow ) => isOnStartEdge === previousShowStartShadow ? ! previousShowStartShadow : previousShowStartShadow ) ; setShowEndShadow ( ( previousShowEndShadow ) => isOnEndEdge === previousShowEndShadow ? ! previousShowEndShadow : previousShowEndShadow ) ; } ; useEffect ( ( ) => { const resizeObserver = new ResizeObserver ( ( ) => { setShadows ( scrollContainerRef . current ! , scrollChildRef . current ! ) ; } ) ; resizeObserver . observe ( scrollChildRef . current ! ) ; resizeObserver . observe ( scrollContainerRef . current ! ) ; return ( ) => { resizeObserver . disconnect ( ) ; } ; } , [ ] ) ; const handleScroll = ( { currentTarget : scrollContainer } ) => { const { current : scrollChild } = scrollChildRef ; if ( ! scrollChild ) { return ; } setShadows ( scrollContainer , scrollChild ) ; } ; const outerClassName = classnames ( [ className , horizontal ? 'scroll-outer-horizontal' : 'scroll-outer-vertical' ] ) ; const startShadowClassName = classnames ( [ 'scroll-start-shadow no-pointer-events' , showStartShadow && 'scroll-start-shadow-visible' , ] ) ; const endShadowClassName = classnames ( [ 'scroll-end-shadow no-pointer-events' , showEndShadow && 'scroll-end-shadow-visible' , ] ) ; return ( < div { ... rest } className = { outerClassName } > < div className = { startShadowClassName } aria - hidden = "true" / > < div className = { endShadowClassName } aria - hidden = "true" / > < div className = "scroll-inner" ref = { scrollContainerRef } onScroll = { handleScroll } > < div className = "scroll-child" ref = { scrollChildRef } > { children } < /div> < /div> < /div>); } ; export default Scroll ;