77 StickyManagerContext ,
88} from '../../utilities/sticky-manager' ;
99import { scrollable } from '../shared' ;
10+ import { useLazyRef } from '../../utilities/use-lazy-ref' ;
11+ import { useComponentDidMount } from '../../utilities/use-component-did-mount' ;
1012
1113import { ScrollTo } from './components' ;
1214import { ScrollableContext } from './context' ;
@@ -45,17 +47,18 @@ export function Scrollable({
4547} : ScrollableProps ) {
4648 const [ topShadow , setTopShadow ] = useState ( false ) ;
4749 const [ bottomShadow , setBottomShadow ] = useState ( false ) ;
48- const stickyManager = useRef ( new StickyManager ( ) ) ;
50+ const stickyManager = useLazyRef ( ( ) => new StickyManager ( ) ) ;
4951 const scrollArea = useRef < HTMLDivElement > ( null ) ;
5052 const scrollTo = useCallback ( ( scrollY : number ) => {
51- scrollArea . current ?. scrollTo ( { top : scrollY , behavior : 'smooth' } ) ;
53+ const behavior = prefersReducedMotion ( ) ? 'auto' : 'smooth' ;
54+ scrollArea . current ?. scrollTo ( { top : scrollY , behavior} ) ;
5255 } , [ ] ) ;
5356
54- useEffect ( ( ) => {
57+ useComponentDidMount ( ( ) => {
5558 if ( hint ) {
5659 performScrollHint ( scrollArea . current ) ;
5760 }
58- } , [ hint ] ) ;
61+ } ) ;
5962
6063 useEffect ( ( ) => {
6164 const currentScrollArea = scrollArea . current ;
@@ -66,11 +69,17 @@ export function Scrollable({
6669
6770 const handleScroll = ( ) => {
6871 const { scrollTop, clientHeight, scrollHeight} = currentScrollArea ;
69-
70- setBottomShadow (
71- Boolean ( shadow && ! ( scrollTop + clientHeight >= scrollHeight ) ) ,
72+ const isBelowTopOfScroll = Boolean ( scrollTop > 0 ) ;
73+ const isAtBottomOfScroll = Boolean (
74+ scrollTop + clientHeight >= scrollHeight - LOW_RES_BUFFER ,
7275 ) ;
73- setTopShadow ( Boolean ( shadow && scrollTop > 0 ) ) ;
76+
77+ setTopShadow ( isBelowTopOfScroll ) ;
78+ setBottomShadow ( ! isAtBottomOfScroll ) ;
79+
80+ if ( isAtBottomOfScroll && onScrolledToBottom ) {
81+ onScrolledToBottom ( ) ;
82+ }
7483 } ;
7584
7685 const handleResize = debounce ( handleScroll , 50 , { trailing : true } ) ;
@@ -85,15 +94,15 @@ export function Scrollable({
8594 currentScrollArea . removeEventListener ( 'scroll' , handleScroll ) ;
8695 globalThis . removeEventListener ( 'resize' , handleResize ) ;
8796 } ;
88- } , [ shadow ] ) ;
97+ } , [ stickyManager , onScrolledToBottom ] ) ;
8998
9099 const finalClassName = classNames (
91100 className ,
92101 styles . Scrollable ,
93102 vertical && styles . vertical ,
94103 horizontal && styles . horizontal ,
95- topShadow && styles . hasTopShadow ,
96- bottomShadow && styles . hasBottomShadow ,
104+ shadow && topShadow && styles . hasTopShadow ,
105+ shadow && bottomShadow && styles . hasBottomShadow ,
97106 ) ;
98107
99108 return (
0 commit comments