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' ;
@@ -49,17 +51,18 @@ export function Scrollable({
4951} : ScrollableProps ) {
5052 const [ topShadow , setTopShadow ] = useState ( false ) ;
5153 const [ bottomShadow , setBottomShadow ] = useState ( false ) ;
52- const stickyManager = useRef ( new StickyManager ( ) ) ;
54+ const stickyManager = useLazyRef ( ( ) => new StickyManager ( ) ) ;
5355 const scrollArea = useRef < HTMLDivElement > ( null ) ;
5456 const scrollTo = useCallback ( ( scrollY : number ) => {
55- scrollArea . current ?. scrollTo ( { top : scrollY , behavior : 'smooth' } ) ;
57+ const behavior = prefersReducedMotion ( ) ? 'auto' : 'smooth' ;
58+ scrollArea . current ?. scrollTo ( { top : scrollY , behavior} ) ;
5659 } , [ ] ) ;
5760
58- useEffect ( ( ) => {
61+ useComponentDidMount ( ( ) => {
5962 if ( hint ) {
6063 performScrollHint ( scrollArea . current ) ;
6164 }
62- } , [ hint ] ) ;
65+ } ) ;
6366
6467 useEffect ( ( ) => {
6568 const currentScrollArea = scrollArea . current ;
@@ -70,11 +73,17 @@ export function Scrollable({
7073
7174 const handleScroll = ( ) => {
7275 const { scrollTop, clientHeight, scrollHeight} = currentScrollArea ;
73-
74- setBottomShadow (
75- Boolean ( shadow && ! ( scrollTop + clientHeight >= scrollHeight ) ) ,
76+ const isBelowTopOfScroll = Boolean ( scrollTop > 0 ) ;
77+ const isAtBottomOfScroll = Boolean (
78+ scrollTop + clientHeight >= scrollHeight - LOW_RES_BUFFER ,
7679 ) ;
77- setTopShadow ( Boolean ( shadow && scrollTop > 0 ) ) ;
80+
81+ setTopShadow ( isBelowTopOfScroll ) ;
82+ setBottomShadow ( ! isAtBottomOfScroll ) ;
83+
84+ if ( isAtBottomOfScroll && onScrolledToBottom ) {
85+ onScrolledToBottom ( ) ;
86+ }
7887 } ;
7988
8089 const handleResize = debounce ( handleScroll , 50 , { trailing : true } ) ;
@@ -89,15 +98,15 @@ export function Scrollable({
8998 currentScrollArea . removeEventListener ( 'scroll' , handleScroll ) ;
9099 globalThis . removeEventListener ( 'resize' , handleResize ) ;
91100 } ;
92- } , [ shadow ] ) ;
101+ } , [ stickyManager , onScrolledToBottom ] ) ;
93102
94103 const finalClassName = classNames (
95104 className ,
96105 styles . Scrollable ,
97106 vertical && styles . vertical ,
98107 horizontal && styles . horizontal ,
99- topShadow && styles . hasTopShadow ,
100- bottomShadow && styles . hasBottomShadow ,
108+ shadow && topShadow && styles . hasTopShadow ,
109+ shadow && bottomShadow && styles . hasBottomShadow ,
101110 ) ;
102111
103112 return (
0 commit comments