diff --git a/README.md b/README.md index 249e5dd..bb50b1f 100644 --- a/README.md +++ b/README.md @@ -71,11 +71,12 @@ const App = () => ( ## Props -| Prop name | Type | Description | Default | -| ---------------- | -------- | ------------------------------------------------------------------------------------------ | --------- | -| **children** | function | Render props function called with 4 props: `ref`, `outerRef`, `style` and `onScroll` | undefined | -| **throttleTime** | number | Timing (ms) for the throttle on window scroll event handler | 10 | -| **isGrid** | boolean | Set to true if rendering a react-window Grid component (FixedSizeGrid or VariableSizeGrid) | false | +| Prop name | Type | Description | Default | +| -------------------- | ---------- | ------------------------------------------------------------------------------------------ | --------- | +| **children** | function | Render props function called with 4 props: `ref`, `outerRef`, `style` and `onScroll` | undefined | +| **throttleTime** | number | Timing (ms) for the throttle on window scroll event handler | 10 | +| **isGrid** | boolean | Set to true if rendering a react-window Grid component (FixedSizeGrid or VariableSizeGrid) | false | +| **scrollElementRef** | ElementRef | The element in your project that is scrolling, if not window | window | ## License diff --git a/example/src/App.jsx b/example/src/App.jsx index 3dd1111..b5352a3 100644 --- a/example/src/App.jsx +++ b/example/src/App.jsx @@ -1,27 +1,65 @@ -import React, { useState } from 'react' +import React, { useMemo, useState, useEffect, useCallback } from 'react' import FixedSizeList from './FixedSizeList' import VariableSizeList from './VariableSizeList' import FixedSizeGrid from './FixedSizeGrid' import VariableSizeGrid from './VariableSizeGrid' +const types = ['window', 'element']; const lists = ['Fixed List', 'Variable List', 'Fixed Grid', 'Variable Grid'] const App = () => { - const [selected, setSelected] = useState(lists[0]) + const [selectedType, setSelectedType] = useState(types[0]); + const [selectedList, setSelectedList] = useState(lists[0]); + // Normally we would use `useRef`, but since we are flipping back and forth we need to trigger a render. + const [scrollElementRef, setScrollElementRef] = useState(); + + useEffect(() => { + if (selectedType === 'window') { + document.body.style.overflow = 'inherit'; + } else { + document.body.style.overflow = 'hidden'; + } + }, [selectedType]) + + const currentScrollRef = useMemo(() => selectedType === 'element' ? scrollElementRef || undefined : undefined, [selectedType, scrollElementRef]); + const getListSelected = useCallback(() => ( + <> + {selectedList === 'Fixed List' && } + {selectedList === 'Variable List' && } + {selectedList === 'Fixed Grid' && } + {selectedList === 'Variable Grid' && } + + ), [selectedList, currentScrollRef]); + return ( <> + - - {selected === 'Fixed List' && } - {selected === 'Variable List' && } - {selected === 'Fixed Grid' && } - {selected === 'Variable Grid' && } + {selectedType === 'element' && ( +
+
Top Bar
+
+
Left Bar
+
+
Content Tabs
+
{getListSelected()}
+
+
+
+ )} + {selectedType === 'window' && getListSelected()} ) } diff --git a/example/src/FixedSizeGrid.jsx b/example/src/FixedSizeGrid.jsx index 55d6dc1..a88643a 100644 --- a/example/src/FixedSizeGrid.jsx +++ b/example/src/FixedSizeGrid.jsx @@ -3,8 +3,8 @@ import { FixedSizeGrid as Grid } from 'react-window' import { ReactWindowScroller } from 'react-window-scroller' import { Cell } from './Cell' -const FixedSizeGrid = () => ( - +const FixedSizeGrid = ({ scrollElementRef }) => ( + {({ ref, outerRef, style, onScroll }) => ( ( - +const FixedSizeList = ({ scrollElementRef }) => ( + {({ ref, outerRef, style, onScroll }) => ( 25 + Math.round(Math.random() * 50) ) -const VariableSizeGrid = () => ( - +const VariableSizeGrid = ({ scrollElementRef }) => ( + {({ ref, outerRef, style, onScroll }) => ( rowSizes[index] -const VariableSizeList = () => ( - +const VariableSizeList = ({ scrollElementRef }) => ( + {({ ref, outerRef, style, onScroll }) => ( +const elementScrollPositionKey = documentScrollPositionKey + +const getScrollPositionWindow = (axis) => window[windowScrollPositionKey[axis]] || document.documentElement[documentScrollPositionKey[axis]] || document.body[documentScrollPositionKey[axis]] || 0 +const getScrollPositionElement = (ref, axis) => + ref[elementScrollPositionKey[axis]] + +const getScrollPosition = (ref, axis) => { + if (ref === window) { + return getScrollPositionWindow(axis) + } + return getScrollPositionElement(ref, axis) +} + export const ReactWindowScroller = ({ children, throttleTime = 10, - isGrid = false + isGrid = false, + scrollElementRef = window }) => { const ref = useRef() const outerRef = useRef() @@ -28,36 +41,43 @@ export const ReactWindowScroller = ({ useEffect(() => { const handleWindowScroll = throttle(() => { const { offsetTop = 0, offsetLeft = 0 } = outerRef.current || {} - const scrollTop = getScrollPosition('y') - offsetTop - const scrollLeft = getScrollPosition('x') - offsetLeft + + const scrollTop = getScrollPosition(scrollElementRef, 'y') - offsetTop + const scrollLeft = getScrollPosition(scrollElementRef, 'x') - offsetLeft if (isGrid) ref.current && ref.current.scrollTo({ scrollLeft, scrollTop }) if (!isGrid) ref.current && ref.current.scrollTo(scrollTop) }, throttleTime) - window.addEventListener('scroll', handleWindowScroll) + scrollElementRef.addEventListener('scroll', handleWindowScroll) return () => { handleWindowScroll.cancel() - window.removeEventListener('scroll', handleWindowScroll) + scrollElementRef.removeEventListener('scroll', handleWindowScroll) } - }, [isGrid]) + }, [isGrid, scrollElementRef, throttleTime]) const onScroll = useCallback( - ({ scrollLeft, scrollTop, scrollOffset, scrollUpdateWasRequested }) => { + ({ + scrollLeft = 0, // This is not provided by react-window + scrollTop = 0, // This is not provided by react-window + scrollOffset, + scrollUpdateWasRequested + }) => { if (!scrollUpdateWasRequested) return - const top = getScrollPosition('y') - const left = getScrollPosition('x') + const top = getScrollPosition(scrollElementRef, 'y') + const left = getScrollPosition(scrollElementRef, 'x') const { offsetTop = 0, offsetLeft = 0 } = outerRef.current || {} scrollOffset += Math.min(top, offsetTop) scrollTop += Math.min(top, offsetTop) scrollLeft += Math.min(left, offsetLeft) - if (!isGrid && scrollOffset !== top) window.scrollTo(0, scrollOffset) + if (!isGrid && scrollOffset !== top) + scrollElementRef.scrollTo(0, scrollOffset) if (isGrid && (scrollTop !== top || scrollLeft !== left)) { - window.scrollTo(scrollLeft, scrollTop) + scrollElementRef.scrollTo(scrollLeft, scrollTop) } }, - [isGrid] + [isGrid, scrollElementRef] ) return children({