refactor(LOY-495): create useSidePeekScrollToTop hook * refactor(LOY-495): create useSidePeekScrollToTop hook * fix(LOY-495): Read ref fresh each time Approved-by: Emma Zettervall
61 lines
1.8 KiB
TypeScript
61 lines
1.8 KiB
TypeScript
import { type RefObject, useEffect, useState } from "react"
|
|
|
|
interface UseScrollToTopProps {
|
|
threshold: number
|
|
/**
|
|
* Direct element reference. Use this when the element is conditionally
|
|
* rendered or when you need to reference a parent element via state.
|
|
* Takes precedence over elementRef if both are provided.
|
|
*/
|
|
element?: HTMLElement | null
|
|
/**
|
|
* Ref to the element. Use this when you have a direct ref attachment
|
|
* to the scrollable element.
|
|
*/
|
|
elementRef?: RefObject<HTMLElement | null>
|
|
refScrollable?: boolean
|
|
}
|
|
|
|
export function useScrollToTop({
|
|
threshold,
|
|
element,
|
|
elementRef,
|
|
refScrollable,
|
|
}: UseScrollToTopProps) {
|
|
const [showBackToTop, setShowBackToTop] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const targetElement = element ?? elementRef?.current
|
|
const scrollTarget = refScrollable && targetElement ? targetElement : window
|
|
|
|
function handleScroll() {
|
|
const currentElement = element ?? elementRef?.current
|
|
let position = window.scrollY
|
|
if (currentElement) {
|
|
position = refScrollable
|
|
? currentElement.scrollTop
|
|
: currentElement.getBoundingClientRect().top * -1
|
|
}
|
|
setShowBackToTop(position > threshold)
|
|
}
|
|
|
|
scrollTarget.addEventListener("scroll", handleScroll, { passive: true })
|
|
return () => scrollTarget.removeEventListener("scroll", handleScroll)
|
|
}, [threshold, element, elementRef, refScrollable])
|
|
|
|
function scrollToTop() {
|
|
const targetElement = element ?? elementRef?.current
|
|
if (targetElement) {
|
|
if (refScrollable) {
|
|
targetElement.scrollTo({ top: 0, behavior: "smooth" })
|
|
} else {
|
|
window.scrollTo({ top: targetElement.offsetTop, behavior: "smooth" })
|
|
}
|
|
} else {
|
|
window.scrollTo({ top: 0, behavior: "smooth" })
|
|
}
|
|
}
|
|
|
|
return { showBackToTop, scrollToTop }
|
|
}
|