import classnames from 'classnames'; import React, { useState, useEffect, useCallback } from 'react'; import IReactComponentProps from '../../../common/structures/IReactComponentProps'; import styles from './ScrollShadow.scss'; import { FunctionGeneric } from '../../../common/structures/Generics'; interface IProps extends IReactComponentProps { // Callback for accessing the scrollable content div ref from a parent - element will be passed on change refCallback?: FunctionGeneric; shadowClassName?: string; /** When this switches from false to true, the scrollable content will be scrolled to the top */ scrollToTopTrigger?: boolean; /** Show a thin bottom border on the scroll container when not scrolling, useful for fixed buttons */ showBottomBorderWhenScrolled?: boolean; } export const ScrollShadow = (props: IProps) => { const { refCallback, shadowClassName, children, className, scrollToTopTrigger, showBottomBorderWhenScrolled, ...otherProps } = props; const [scrollableContent, setScrollableContent] = useState(null); const [showScrollShadow, setShowScrollShadow] = useState(false); const canScroll = (el: Element) => el.scrollHeight > el.clientHeight; const handleScroll = (e: any) => { const hideShadow = !canScroll(e.target) || e.target.scrollHeight - (e.target.scrollTop + 1) <= e.target.clientHeight; setShowScrollShadow(!hideShadow); }; useEffect(() => { if (scrollToTopTrigger && scrollableContent) { scrollableContent.scrollTop = 0; } }, [scrollToTopTrigger]) useEffect(() => { if (!scrollableContent) return; setShowScrollShadow(canScroll(scrollableContent)); scrollableContent.addEventListener('scroll', handleScroll, { passive: true }); const resizeObserver = new ResizeObserver((events) => { for (const event of events) { handleScroll(event); } }); resizeObserver.observe(scrollableContent); return () => { scrollableContent.removeEventListener('scroll', handleScroll); resizeObserver.disconnect(); }; }, [scrollableContent]); const setScrollableContentRef = useCallback( (element: HTMLDivElement | null) => { setScrollableContent(element); if (refCallback) refCallback(element); }, [refCallback] ); return ( <>
{children}
); };