Files
web/packages/common/hooks/useSidePeekScrollToTop.ts
Chuma Mcphoy (We Ahead) 9d8399b7c7 Merged in refactor/LOY-495-hook-for-sidepeek-scrolling (pull request #3297)
refactor(LOY-495): create useSidePeekScrollToTop hook

* refactor(LOY-495): create useSidePeekScrollToTop hook

* fix(LOY-495):  Read ref fresh each time


Approved-by: Emma Zettervall
2025-12-05 13:51:10 +00:00

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 }
}