mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-18 03:23:00 -05:00
7c97f8ab6e
* Use new JSX runtime and update Babel Node target * Update .eslintrc * Remove React imports GitOrigin-RevId: 559de0267f8f2934c56a860ea8701bb522aa861a
88 lines
2.2 KiB
JavaScript
88 lines
2.2 KiB
JavaScript
import { useRef, useEffect, useLayoutEffect } from 'react'
|
|
import PropTypes from 'prop-types'
|
|
import _ from 'lodash'
|
|
|
|
const SCROLL_END_OFFSET = 30
|
|
|
|
function InfiniteScroll({
|
|
atEnd,
|
|
children,
|
|
className = '',
|
|
fetchData,
|
|
itemCount,
|
|
isLoading,
|
|
}) {
|
|
const root = useRef(null)
|
|
|
|
// we keep the value in a Ref instead of state so it can be safely used in effects
|
|
const scrollBottomRef = useRef(0)
|
|
function setScrollBottom(value) {
|
|
scrollBottomRef.current = value
|
|
}
|
|
|
|
function updateScrollPosition() {
|
|
root.current.scrollTop =
|
|
root.current.scrollHeight -
|
|
root.current.clientHeight -
|
|
scrollBottomRef.current
|
|
}
|
|
|
|
// Repositions the scroll after new items are loaded
|
|
useLayoutEffect(updateScrollPosition, [itemCount])
|
|
|
|
// Repositions the scroll after a window resize
|
|
useEffect(() => {
|
|
const handleResize = _.debounce(updateScrollPosition, 400)
|
|
window.addEventListener('resize', handleResize)
|
|
return () => {
|
|
window.removeEventListener('resize', handleResize)
|
|
}
|
|
}, [])
|
|
|
|
function onScrollHandler(event) {
|
|
setScrollBottom(
|
|
root.current.scrollHeight -
|
|
root.current.scrollTop -
|
|
root.current.clientHeight
|
|
)
|
|
if (event.target !== event.currentTarget) {
|
|
// Ignore scroll events on nested divs
|
|
// (this check won't be necessary in React 17: https://github.com/facebook/react/issues/15723
|
|
return
|
|
}
|
|
if (shouldFetchData()) {
|
|
fetchData()
|
|
}
|
|
}
|
|
|
|
function shouldFetchData() {
|
|
const containerIsLargerThanContent =
|
|
root.current.children[0].clientHeight < root.current.clientHeight
|
|
if (atEnd || isLoading || containerIsLargerThanContent) {
|
|
return false
|
|
} else {
|
|
return root.current.scrollTop < SCROLL_END_OFFSET
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div
|
|
ref={root}
|
|
onScroll={onScrollHandler}
|
|
className={`infinite-scroll ${className}`}
|
|
>
|
|
{children}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
InfiniteScroll.propTypes = {
|
|
atEnd: PropTypes.bool,
|
|
children: PropTypes.element.isRequired,
|
|
className: PropTypes.string,
|
|
fetchData: PropTypes.func.isRequired,
|
|
itemCount: PropTypes.number.isRequired,
|
|
isLoading: PropTypes.bool,
|
|
}
|
|
|
|
export default InfiniteScroll
|