Skip to content

Commit

Permalink
fix: scroll offset in window scroll (#227)
Browse files Browse the repository at this point in the history
  • Loading branch information
piecyk authored Feb 8, 2022
1 parent bcbd99e commit 20eb79c
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 19 deletions.
32 changes: 16 additions & 16 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React from 'react'
import useRect from './useRect'
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect'

export { useRect }

const defaultEstimateSize = () => 50

const defaultKeyExtractor = index => index
Expand Down Expand Up @@ -84,7 +86,10 @@ export const useElementScroll = ({
}
}

const useWindowRect = (windowRef, initialRect = { width: 0, height: 0 }) => {
export const useWindowRect = (
windowRef,
initialRect = { width: 0, height: 0 }
) => {
const [rect, setRect] = React.useState(initialRect)
const [element, setElement] = React.useState(windowRef.current)

Expand Down Expand Up @@ -129,9 +134,8 @@ export const useWindowScroll = ({
const [scrollOffset, setScrollOffset] = React.useState(0)
const [element, setElement] = React.useState(windowRef.current)

const parentOffsetRef = React.useRef(0)

const rectKey = horizontal ? 'left' : 'top'

const scrollKey = horizontal ? 'scrollX' : 'scrollY'

useIsomorphicLayoutEffect(() => {
Expand All @@ -140,20 +144,15 @@ export const useWindowScroll = ({

useIsomorphicLayoutEffect(() => {
if (!element) {
parentOffsetRef.current = 0
setScrollOffset(0)

return
}

if (parentRef.current) {
parentOffsetRef.current =
element[scrollKey] + parentRef.current.getBoundingClientRect()[rectKey]
}

const onScroll = () => {
const offset = element[scrollKey] - parentOffsetRef.current
setScrollOffset(offset)
setScrollOffset(
Math.max(0, parentRef.current.getBoundingClientRect()[rectKey] * -1)
)
}

onScroll()
Expand All @@ -166,19 +165,20 @@ export const useWindowScroll = ({
return () => {
element.removeEventListener('scroll', onScroll)
}
}, [element, scrollKey, rectKey, parentRef])
}, [element, rectKey, parentRef])

const scrollToFn = React.useCallback(
(offset, reason) => {
if (windowRef.current) {
const delta = ['ToIndex', 'SizeChanged'].includes(reason)
? parentOffsetRef.current
? windowRef.current[scrollKey] +
parentRef.current.getBoundingClientRect()[rectKey]
: 0

windowRef.current.scrollTo({ [rectKey]: offset + delta })
}
},
[windowRef, rectKey]
[windowRef, parentRef, scrollKey, rectKey]
)

const useMeasureParent = useWindowObserver || useWindowRect
Expand Down Expand Up @@ -304,9 +304,9 @@ export function useVirtual({
start,
end,
overscan,
size: measurements.length,
size,
}),
[start, end, overscan, measurements.length, rangeExtractor]
[start, end, overscan, size, rangeExtractor]
)

const measureSizeRef = React.useRef(measureSize)
Expand Down
10 changes: 8 additions & 2 deletions src/tests/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,15 @@ describe('useVirtual list', () => {
expect(result.current.virtualItems.length).toBe(4)
})
it('should handle scroll in window mode', () => {
let offset = 0
const el = document.createElement('div')
el.getBoundingClientRect = jest.fn(() => {
return { top: offset * -1 }
})
const { result } = renderHook(() =>
useVirtualImpl({
size: 100,
parentRef: React.useRef(document.createElement('div')),
parentRef: React.useRef(el),
windowRef: React.useRef(window),
useWindowObserver: () => ({ height: 200, width: '100%' }),
overscan: 0,
Expand All @@ -184,7 +189,8 @@ describe('useVirtual list', () => {
)
expect(result.current.virtualItems[0].index).toBe(0)
act(() => {
fireEvent.scroll(window, { target: { scrollY: 100 } })
offset = 100
fireEvent.scroll(window, { target: { scrollY: offset } })
})
expect(result.current.virtualItems[0].index).toBe(2)
})
Expand Down
18 changes: 17 additions & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ interface Rect {
height: number
}

declare function useRect<T extends HTMLElement>(
ref: React.RefObject<T>,
initialRect?: Rect
): Rect

declare function useWindowRect(
ref: React.RefObject<Window>,
initialRect?: Rect
): Rect

interface ScrollOptions<T> {
parentRef: React.RefObject<T>
windowRef?: React.RefObject<Window>
Expand Down Expand Up @@ -76,4 +86,10 @@ declare function useVirtual<T>(
measure: () => void
}

export { defaultRangeExtractor, useVirtual, useDefaultScroll }
export {
defaultRangeExtractor,
useVirtual,
useDefaultScroll,
useRect,
useWindowRect,
}

0 comments on commit 20eb79c

Please sign in to comment.