2 * Copyright © 2018 European Support Limited
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 import React from 'react';
18 import PropTypes from 'prop-types';
20 class InfiniteScroll extends React.Component {
28 this.scrollEl = this.getScrollElement();
29 this.attachScrollListener();
30 this.setState({ initialLoad: true });
33 componentDidUpdate() {
34 this.attachScrollListener();
37 componentWillUnmount() {
38 this.detachScrollListener();
41 getParentElement(el) {
46 if (this.props.useWindow) {
50 return this.getParentElement(this.scrollComponent);
53 detachScrollListener() {
54 this.scrollEl.removeEventListener(
59 window.removeEventListener(
66 attachScrollListener() {
67 if (!this.props.hasMore || !this.scrollEl) {
72 capture: this.props.useCapture,
76 this.scrollEl.addEventListener('scroll', this.scrollListener, options);
77 window.addEventListener('resize', this.scrollListener, options);
79 this.scrollListener();
82 scrollListener = () => {
83 const el = this.scrollComponent;
84 const scrollEl = window;
85 const parentNode = this.getParentElement(el);
88 if (this.props.useWindow) {
90 document.documentElement ||
91 document.body.parentNode ||
94 scrollEl.pageYOffset !== undefined
95 ? scrollEl.pageYOffset
99 this.calculateTopPosition(el) +
100 (el.offsetHeight - scrollTop - window.innerHeight);
104 parentNode.scrollTop -
105 parentNode.clientHeight;
108 // Here we make sure the element is visible as well as checking the offset
109 if (offset < Number(this.props.threshold) && el.offsetParent !== null) {
110 this.detachScrollListener();
111 // Call loadMore after detachScrollListener to allow for non-async loadMore functions
113 typeof this.props.loadMore === 'function' &&
114 this.state.initialLoad
116 this.props.loadMore();
121 calculateTopPosition(el) {
125 return el.offsetTop + this.calculateTopPosition(el.offsetParent);
129 const { children, element } = this.props;
133 this.scrollComponent = node;
137 const childrenArray = [children];
139 return React.createElement(element, props, childrenArray);
143 InfiniteScroll.propTypes = {
144 children: PropTypes.node.isRequired,
145 element: PropTypes.node,
146 hasMore: PropTypes.bool,
147 loadMore: PropTypes.func.isRequired,
148 threshold: PropTypes.number,
149 useCapture: PropTypes.bool,
150 useWindow: PropTypes.bool
153 InfiniteScroll.defaultProps = {
161 export default InfiniteScroll;