Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
54 changes: 46 additions & 8 deletions example/src/App.jsx
Original file line number Diff line number Diff line change
@@ -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' && <FixedSizeList scrollElementRef={currentScrollRef} />}
{selectedList === 'Variable List' && <VariableSizeList scrollElementRef={currentScrollRef} />}
{selectedList === 'Fixed Grid' && <FixedSizeGrid scrollElementRef={currentScrollRef} />}
{selectedList === 'Variable Grid' && <VariableSizeGrid scrollElementRef={currentScrollRef} />}
</>
), [selectedList, currentScrollRef]);

return (
<>
<nav>
{types.map((demoType) => (
<button key={demoType} onClick={() => setSelectedType(demoType)}>
{demoType}
</button>
))}
</nav>
<nav>
{lists.map((list) => (
<button key={list} onClick={() => setSelected(list)}>
<button key={list} onClick={() => setSelectedList(list)}>
{list}
</button>
))}
</nav>

{selected === 'Fixed List' && <FixedSizeList />}
{selected === 'Variable List' && <VariableSizeList />}
{selected === 'Fixed Grid' && <FixedSizeGrid />}
{selected === 'Variable Grid' && <VariableSizeGrid />}
{selectedType === 'element' && (
<div className="wrapper">
<div className="top-bar">Top Bar</div>
<div className="main-body">
<div className="main-body__left_bar">Left Bar</div>
<div className="main-body__content" ref={setScrollElementRef}>
<div className="main-body__content-tabs">Content Tabs</div>
<div className="main-body__content-list">{getListSelected()}</div>
</div>
</div>
</div>
)}
{selectedType === 'window' && getListSelected()}
</>
)
}
Expand Down
4 changes: 2 additions & 2 deletions example/src/FixedSizeGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { FixedSizeGrid as Grid } from 'react-window'
import { ReactWindowScroller } from 'react-window-scroller'
import { Cell } from './Cell'

const FixedSizeGrid = () => (
<ReactWindowScroller isGrid>
const FixedSizeGrid = ({ scrollElementRef }) => (
<ReactWindowScroller scrollElementRef={scrollElementRef} isGrid>
{({ ref, outerRef, style, onScroll }) => (
<Grid
ref={ref}
Expand Down
4 changes: 2 additions & 2 deletions example/src/FixedSizeList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { FixedSizeList as List } from 'react-window'
import { ReactWindowScroller } from 'react-window-scroller'
import { Row } from './Row'

const FixedSizeList = () => (
<ReactWindowScroller>
const FixedSizeList = ({ scrollElementRef }) => (
<ReactWindowScroller scrollElementRef={scrollElementRef}>
{({ ref, outerRef, style, onScroll }) => (
<List
ref={ref}
Expand Down
4 changes: 2 additions & 2 deletions example/src/VariableSizeGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const rowHeights = [...new Array(1000)].map(
() => 25 + Math.round(Math.random() * 50)
)

const VariableSizeGrid = () => (
<ReactWindowScroller isGrid>
const VariableSizeGrid = ({ scrollElementRef }) => (
<ReactWindowScroller scrollElementRef={scrollElementRef} isGrid>
{({ ref, outerRef, style, onScroll }) => (
<Grid
ref={ref}
Expand Down
4 changes: 2 additions & 2 deletions example/src/VariableSizeList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ const rowSizes = [...new Array(1000)].map(
)
const getItemSize = (index) => rowSizes[index]

const VariableSizeList = () => (
<ReactWindowScroller>
const VariableSizeList = ({ scrollElementRef }) => (
<ReactWindowScroller scrollElementRef={scrollElementRef}>
{({ ref, outerRef, style, onScroll }) => (
<List
ref={ref}
Expand Down
38 changes: 38 additions & 0 deletions example/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,41 @@ nav {
nav button {
margin: 0 5px;
}

.wrapper {
display: flex;
flex-direction: column;
}

.top-bar {
width: 100%;
height: 60px;
background-color: blue;
}

.main-body {
display: flex;
flex-direction: row;
}

.main-body__left_bar {
width: 300px;
background-color: aqua;
}

.main-body__content {
flex: 1 0;
height: calc(100vh - 182px);
width: 100vw;
overflow: auto;
}

.main-body__content-tabs {
position: sticky;
top: 0;
text-align: center;
background-color: greenyellow;
height: 40px;
line-height: 40px;
z-index: 10;
}
10 changes: 5 additions & 5 deletions example/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6439,11 +6439,6 @@ lodash.templatesettings@^4.0.0:
dependencies:
lodash._reinterpolate "^3.0.0"

lodash.throttle@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=

lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
Expand All @@ -6454,6 +6449,11 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==

lodash@^4.17.20:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==

loglevel@^1.6.6:
version "1.6.8"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171"
Expand Down
46 changes: 33 additions & 13 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,73 @@ const documentScrollPositionKey = {
x: 'scrollLeft'
}

const getScrollPosition = (axis) =>
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()

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
Copy link
Author

@jrosspaperless jrosspaperless Sep 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react-window passes scrollDirection: ScrollDirection not scrollLeft and scrollTop. Without setting 0 as default, this function does not do anything as the numbers end up as NaN. From what I can tell, defaulting to 0 resolves that issue and results in this executing properly. Not sure why they are part of the method signature, though.

https://github.com/bvaughn/react-window/blob/master/src/createListComponent.js#L393-L409

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({
Expand Down