Files
omnivore/packages/web/lib/hooks/useScrollWatcher.tsx
Jackson Harper eb8cb3854c Do all scroll watching on the main window
This moves all scrolling from child divs to the main window. This
improves our keyboard handling, as focus will be given to the
body element, not the child div when navigating by keyboard
commands, so arrow keys and space bar will work after navigating
to the reader with the keyboard commands.
2022-06-17 15:59:51 -07:00

47 lines
1.2 KiB
TypeScript

import { useRef, useEffect, useState, useCallback } from 'react'
type ScrollOffset = {
x: number
y: number
}
export type ScrollOffsetChangeset = {
current: ScrollOffset
previous: ScrollOffset
}
type Effect = (offset: ScrollOffsetChangeset) => void
export function useScrollWatcher(
effect: Effect,
delay: number
): void {
const throttleTimeout = useRef<NodeJS.Timeout | undefined>(undefined)
const [currentOffset, setCurrentOffset] = useState<ScrollOffset>({
x: 0,
y: 0,
})
useEffect(() => {
const callback = () => {
const newOffset = {
x: window.document.documentElement.scrollLeft ?? window?.scrollX ?? 0,
y: window.document.documentElement.scrollTop ?? window?.scrollY ?? 0,
}
effect({ current: newOffset, previous: currentOffset })
setCurrentOffset(newOffset)
throttleTimeout.current = undefined
}
const handleScroll = () => {
if (typeof throttleTimeout.current === 'undefined') {
throttleTimeout.current = setTimeout(callback, delay)
}
}
window.addEventListener('scroll', handleScroll)
return () =>
window.removeEventListener('scroll', handleScroll)
}, [currentOffset, delay, effect])
}