@@ -6,36 +6,70 @@ export default function BackToTopButton() {
66 const [ isVisible , setIsVisible ] = useState ( false ) ;
77
88 useEffect ( ( ) => {
9+ const getScrollTop = ( ) =>
10+ window . pageYOffset ||
11+ ( document . scrollingElement && document . scrollingElement . scrollTop ) ||
12+ document . documentElement . scrollTop ||
13+ document . body . scrollTop ||
14+ 0 ;
15+
916 const onScroll = ( ) => {
10- setIsVisible ( window . scrollY > 300 ) ;
17+ setIsVisible ( getScrollTop ( ) > 300 ) ;
1118 } ;
19+
20+ // initial check
1221 onScroll ( ) ;
22+
23+ const mainEl = document . querySelector ( "main, [data-scroll-container], .app-scroll" ) ;
24+ const scrollEl = document . scrollingElement || document . documentElement || document . body ;
25+
26+ // attach listeners broadly to catch different scroll container patterns
1327 window . addEventListener ( "scroll" , onScroll , { passive : true } ) ;
14- return ( ) => window . removeEventListener ( "scroll" , onScroll ) ;
28+ try { scrollEl . addEventListener ( "scroll" , onScroll , { passive : true } ) ; } catch { }
29+ if ( mainEl && mainEl !== scrollEl ) mainEl . addEventListener ( "scroll" , onScroll , { passive : true } ) ;
30+
31+ return ( ) => {
32+ window . removeEventListener ( "scroll" , onScroll ) ;
33+ try { scrollEl . removeEventListener ( "scroll" , onScroll ) ; } catch { }
34+ if ( mainEl && mainEl !== scrollEl ) mainEl . removeEventListener ( "scroll" , onScroll ) ;
35+ } ;
1536 } , [ ] ) ;
1637
1738 const handleClick = ( ) => {
18- window . scrollTo ( { top : 0 , behavior : "smooth" } ) ;
39+ const target =
40+ document . scrollingElement ||
41+ document . querySelector ( "main, [data-scroll-container], .app-scroll" ) ||
42+ document . documentElement ||
43+ document . body ;
44+ try {
45+ target . scrollTo ( { top : 0 , behavior : "smooth" } ) ;
46+ } catch {
47+ window . scrollTo ( { top : 0 , behavior : "smooth" } ) ;
48+ }
1949 } ;
2050
21- if ( ! isVisible ) return null ;
22-
2351 return (
2452 < button
25- onClick = { handleClick }
53+ data-testid = "back-to-top"
2654 aria-label = "Back to top"
27- className = "fixed bottom-6 right-6 z-50 rounded-full bg-blue-600 text-white shadow-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 p-3 md:p-4 transition-transform duration-200 hover:scale-105"
55+ title = "Back to top"
56+ onClick = { handleClick }
57+ className = {
58+ "fixed bottom-6 right-6 z-50 rounded-full bg-blue-600 text-white shadow-lg focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 p-3 md:p-4 transition-all duration-200 transform " +
59+ ( isVisible
60+ ? "opacity-100 scale-100 pointer-events-auto translate-y-0"
61+ : "opacity-0 scale-75 pointer-events-none -translate-y-2" )
62+ }
2863 >
2964 < svg
3065 xmlns = "http://www.w3.org/2000/svg"
3166 viewBox = "0 0 24 24"
3267 fill = "currentColor"
3368 className = "h-5 w-5 md:h-6 md:w-6"
69+ aria-hidden = "true"
3470 >
35- < path d = "M12 20c-.414 0-.75-.336-.75-.75V6.56l-3.72 3.72a.75.75 0 1 1-1.06-1.06l5-5a.75.75 0 0 1 1.06 0l5 5a.75.75 0 1 1-1.06 1.06l-3.72-3.72v12.69c0 .414-.336.75-.75.75Z" />
71+ < path d = "M12 20c-.414 0-.75-.336-.75-.75V6.56l-3.72 3.72a.75.75 0 1 1-1.06-1.06l5-5a.75.75 0 0 1 1.06 0l5 5a.75.75 0 1 1-1.06 1.06l-3.72-3.72v12.69c0 .414-.336.75-.75.75Z" />
3672 </ svg >
3773 </ button >
3874 ) ;
39- }
40-
41-
75+ }
0 commit comments