refactor(LOY-495): create useSidePeekScrollToTop hook * refactor(LOY-495): create useSidePeekScrollToTop hook * fix(LOY-495): Read ref fresh each time Approved-by: Emma Zettervall
54 lines
1.6 KiB
TypeScript
54 lines
1.6 KiB
TypeScript
import { useState } from "react"
|
|
|
|
import { useScrollToTop } from "./useScrollToTop"
|
|
|
|
interface UseSidePeekScrollToTopProps {
|
|
threshold?: number
|
|
}
|
|
|
|
/**
|
|
* Hook for managing scroll-to-top functionality within a SidePeekSelfControlled component.
|
|
* Automatically finds the scrollable dialog container from the SidePeek DOM structure.
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const { scrollContainerRef, showBackToTop, scrollToTop } = useSidePeekScrollToTop()
|
|
*
|
|
* return (
|
|
* <SidePeekSelfControlled>
|
|
* <div ref={scrollContainerRef}>
|
|
* {content}
|
|
* {showBackToTop && <BackToTopButton onPress={scrollToTop} />}
|
|
* </div>
|
|
* </SidePeekSelfControlled>
|
|
* )
|
|
* ```
|
|
*/
|
|
export function useSidePeekScrollToTop({
|
|
threshold = 200,
|
|
}: UseSidePeekScrollToTopProps = {}) {
|
|
const [scrollContainer, setScrollContainer] = useState<HTMLElement | null>(
|
|
null
|
|
)
|
|
|
|
const scrollContainerRef = (node: HTMLDivElement | null) => {
|
|
// SidePeekSelfControlled renders children twice: in the modal & in an sr-only SEO wrapper.
|
|
// We filter out the SEO wrapper to get the actual scrollable container.
|
|
// DOM structure: Dialog (scrollable) -> aside -> sidePeekContent -> our content div
|
|
const sidePeekContent = node?.parentElement
|
|
const aside = sidePeekContent?.parentElement
|
|
const dialog = aside?.parentElement
|
|
if (dialog && !sidePeekContent?.classList.contains("sr-only")) {
|
|
setScrollContainer(dialog)
|
|
}
|
|
}
|
|
|
|
const { showBackToTop, scrollToTop } = useScrollToTop({
|
|
threshold,
|
|
element: scrollContainer,
|
|
refScrollable: true,
|
|
})
|
|
|
|
return { scrollContainerRef, showBackToTop, scrollToTop }
|
|
}
|