MCPcopy
hub / github.com/primer/react / useStickyPaneHeight

Function useStickyPaneHeight

packages/react/src/PageLayout/useStickyPaneHeight.ts:11–104  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

9 * fits into the viewport even when the header or footer are visible.
10 */
11export function useStickyPaneHeight() {
12 const rootRef = React.useRef<HTMLDivElement>(null)
13
14 // Default the height to the viewport height
15 const [height, setHeight] = React.useState(dvh(100))
16 const [offsetHeader, setOffsetHeader] = React.useState<number | string>(0)
17
18 // Create intersection observers to track the top and bottom of the content region
19 const [contentTopRef, contentTopInView, contentTopEntry] = useInView()
20 const [contentBottomRef, contentBottomInView] = useInView()
21
22 // Calculate the height of the sticky pane based on the position of the
23 // top and bottom of the content region
24 const calculateHeight = React.useCallback(() => {
25 // Uncomment to debug
26 // console.log('Recalculating pane height...')
27
28 let calculatedHeight = ''
29
30 const scrollContainer = getScrollContainer(rootRef.current)
31
32 const topRect = contentTopEntry?.target.getBoundingClientRect()
33
34 // Custom sticky header's height with units
35 const offsetHeaderWithUnits = typeof offsetHeader === 'number' ? `${offsetHeader}px` : offsetHeader
36
37 if (scrollContainer) {
38 const scrollRect = scrollContainer.getBoundingClientRect()
39 const topOffset = topRect ? Math.max(topRect.top - scrollRect.top, 0) : 0
40 calculatedHeight = `calc(${scrollRect.height}px - (max(${topOffset}px, ${offsetHeaderWithUnits})))`
41 } else {
42 const topOffset = topRect ? Math.max(topRect.top, 0) : 0
43 calculatedHeight = `calc(${dvh(100)} - (max(${topOffset}px, ${offsetHeaderWithUnits})))`
44 }
45
46 setHeight(calculatedHeight)
47 }, [contentTopEntry, offsetHeader])
48
49 // We only want to add scroll and resize listeners if the pane is sticky.
50 // Since hooks can't be called conditionally, we need to use state to track
51 // if the pane is sticky.
52 const [isEnabled, setIsEnabled] = React.useState(false)
53
54 useLayoutEffect(() => {
55 const scrollContainer = getScrollContainer(rootRef.current)
56
57 if (isEnabled && (contentTopInView || contentBottomInView)) {
58 calculateHeight()
59
60 // Start listeners if the top or the bottom edge of the content region is visible
61
62 if (scrollContainer) {
63 // eslint-disable-next-line github/prefer-observers
64 scrollContainer.addEventListener('scroll', calculateHeight)
65 } else {
66 // eslint-disable-next-line github/prefer-observers
67 window.addEventListener('scroll', calculateHeight)
68 }

Callers 1

RootFunction · 0.90

Calls 2

getScrollContainerFunction · 0.90
dvhFunction · 0.85

Tested by

no test coverage detected