56 lines
1.5 KiB
TypeScript
56 lines
1.5 KiB
TypeScript
// https://usehooks-ts.com/react-hook/use-locked-body
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import { useIsomorphicLayoutEffect } from 'usehooks-ts'
|
|
|
|
type UseLockedBodyOutput = [boolean, (locked: boolean) => void]
|
|
|
|
function useLockedBody(
|
|
initialLocked = false,
|
|
rootId = '___gatsby' // Default to `___gatsby` to not introduce breaking change
|
|
): UseLockedBodyOutput {
|
|
const [locked, setLocked] = useState(initialLocked)
|
|
|
|
// Do the side effect before render
|
|
useIsomorphicLayoutEffect(() => {
|
|
if (!locked) {
|
|
return
|
|
}
|
|
|
|
// Save initial body style
|
|
const originalOverflow = document.body.style.overflow
|
|
const originalPaddingRight = document.body.style.paddingRight
|
|
|
|
// Lock body scroll
|
|
document.body.style.overflow = 'hidden'
|
|
|
|
// Get the scrollBar width
|
|
const root = document.getElementById(rootId) // or root
|
|
const scrollBarWidth = root ? root.offsetWidth - root.scrollWidth : 0
|
|
|
|
// Avoid width reflow
|
|
if (scrollBarWidth) {
|
|
document.body.style.paddingRight = `${scrollBarWidth}px`
|
|
}
|
|
|
|
return () => {
|
|
document.body.style.overflow = originalOverflow
|
|
|
|
if (scrollBarWidth) {
|
|
document.body.style.paddingRight = originalPaddingRight
|
|
}
|
|
}
|
|
}, [locked])
|
|
|
|
// Update state if initialValue changes
|
|
useEffect(() => {
|
|
if (locked !== initialLocked) {
|
|
setLocked(initialLocked)
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [initialLocked])
|
|
|
|
return [locked, setLocked]
|
|
}
|
|
|
|
export default useLockedBody
|