aboutsummaryrefslogtreecommitdiffstats
path: root/sdc-workflow-designer-ui/src/main/frontend/src/shared/scroll/InfiniteScroll.js
diff options
context:
space:
mode:
Diffstat (limited to 'sdc-workflow-designer-ui/src/main/frontend/src/shared/scroll/InfiniteScroll.js')
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/shared/scroll/InfiniteScroll.js161
1 files changed, 161 insertions, 0 deletions
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/shared/scroll/InfiniteScroll.js b/sdc-workflow-designer-ui/src/main/frontend/src/shared/scroll/InfiniteScroll.js
new file mode 100644
index 00000000..04d00120
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/shared/scroll/InfiniteScroll.js
@@ -0,0 +1,161 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import React from 'react';
+import PropTypes from 'prop-types';
+
+class InfiniteScroll extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ initialLoad: false
+ };
+ }
+ componentDidMount() {
+ this.scrollEl = this.getScrollElement();
+ this.attachScrollListener();
+ this.setState({ initialLoad: true });
+ }
+
+ componentDidUpdate() {
+ this.attachScrollListener();
+ }
+
+ componentWillUnmount() {
+ this.detachScrollListener();
+ }
+
+ getParentElement(el) {
+ return el.parentNode;
+ }
+
+ getScrollElement() {
+ if (this.props.useWindow) {
+ return window;
+ }
+
+ return this.getParentElement(this.scrollComponent);
+ }
+
+ detachScrollListener() {
+ this.scrollEl.removeEventListener(
+ 'scroll',
+ this.scrollListener,
+ this.props.useCapture
+ );
+ window.removeEventListener(
+ 'resize',
+ this.scrollListener,
+ this.props.useCapture
+ );
+ }
+
+ attachScrollListener() {
+ if (!this.props.hasMore || !this.scrollEl) {
+ return;
+ }
+
+ const options = {
+ capture: this.props.useCapture,
+ passive: true
+ };
+
+ this.scrollEl.addEventListener('scroll', this.scrollListener, options);
+ window.addEventListener('resize', this.scrollListener, options);
+
+ this.scrollListener();
+ }
+
+ scrollListener = () => {
+ const el = this.scrollComponent;
+ const scrollEl = window;
+ const parentNode = this.getParentElement(el);
+
+ let offset;
+ if (this.props.useWindow) {
+ const doc =
+ document.documentElement ||
+ document.body.parentNode ||
+ document.body;
+ const scrollTop =
+ scrollEl.pageYOffset !== undefined
+ ? scrollEl.pageYOffset
+ : doc.scrollTop;
+
+ offset =
+ this.calculateTopPosition(el) +
+ (el.offsetHeight - scrollTop - window.innerHeight);
+ } else {
+ offset =
+ el.scrollHeight -
+ parentNode.scrollTop -
+ parentNode.clientHeight;
+ }
+
+ // Here we make sure the element is visible as well as checking the offset
+ if (offset < Number(this.props.threshold) && el.offsetParent !== null) {
+ this.detachScrollListener();
+ // Call loadMore after detachScrollListener to allow for non-async loadMore functions
+ if (
+ typeof this.props.loadMore === 'function' &&
+ this.state.initialLoad
+ ) {
+ this.props.loadMore();
+ }
+ }
+ };
+
+ calculateTopPosition(el) {
+ if (!el) {
+ return 0;
+ }
+ return el.offsetTop + this.calculateTopPosition(el.offsetParent);
+ }
+
+ render() {
+ const { children, element } = this.props;
+
+ const props = {
+ ref: node => {
+ this.scrollComponent = node;
+ }
+ };
+
+ const childrenArray = [children];
+
+ return React.createElement(element, props, childrenArray);
+ }
+}
+
+InfiniteScroll.propTypes = {
+ children: PropTypes.node.isRequired,
+ element: PropTypes.node,
+ hasMore: PropTypes.bool,
+ loadMore: PropTypes.func.isRequired,
+ threshold: PropTypes.number,
+ useCapture: PropTypes.bool,
+ useWindow: PropTypes.bool
+};
+
+InfiniteScroll.defaultProps = {
+ element: 'div',
+ hasMore: false,
+ threshold: 250,
+ useWindow: true,
+ useCapture: false
+};
+
+export default InfiniteScroll;