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.
47 lines
1.2 KiB
TypeScript
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])
|
|
}
|