UNPKG

react-virtualized

Version:

React components for efficiently rendering large, scrollable lists and tabular data

172 lines (140 loc) 5.24 kB
import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of'; import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; import _createClass from 'babel-runtime/helpers/createClass'; import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn'; import _inherits from 'babel-runtime/helpers/inherits'; import React, { Component, PropTypes } from 'react'; import ReactDOM from 'react-dom'; import shallowCompare from 'react-addons-shallow-compare'; import raf from 'raf'; import { registerScrollListener, unregisterScrollListener } from './utils/onScroll'; var WindowScroller = function (_Component) { _inherits(WindowScroller, _Component); function WindowScroller(props) { _classCallCheck(this, WindowScroller); var _this = _possibleConstructorReturn(this, (WindowScroller.__proto__ || _Object$getPrototypeOf(WindowScroller)).call(this, props)); var height = typeof window !== 'undefined' ? window.innerHeight : 0; _this.state = { isScrolling: false, height: height, scrollTop: 0 }; _this._onScrollWindow = _this._onScrollWindow.bind(_this); _this._onResizeWindow = _this._onResizeWindow.bind(_this); _this._enablePointerEventsAfterDelayCallback = _this._enablePointerEventsAfterDelayCallback.bind(_this); return _this; } _createClass(WindowScroller, [{ key: 'componentDidMount', value: function componentDidMount() { var height = this.state.height; // Subtract documentElement top to handle edge-case where a user is navigating back (history) from an already-scrolled bage. // In this case the body's top position will be a negative number and this element's top will be increased (by that amount). this._positionFromTop = ReactDOM.findDOMNode(this).getBoundingClientRect().top - document.documentElement.getBoundingClientRect().top; if (height !== window.innerHeight) { this.setState({ height: window.innerHeight }); } registerScrollListener(this); window.addEventListener('resize', this._onResizeWindow, false); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { unregisterScrollListener(this); window.removeEventListener('resize', this._onResizeWindow, false); } /** * Updates the state during the next animation frame. * Use this method to avoid multiple renders in a small span of time. * This helps performance for bursty events (like onScroll). */ }, { key: '_setNextState', value: function _setNextState(state) { var _this2 = this; if (this._setNextStateAnimationFrameId) { raf.cancel(this._setNextStateAnimationFrameId); } this._setNextStateAnimationFrameId = raf(function () { _this2._setNextStateAnimationFrameId = null; _this2.setState(state); }); } }, { key: 'render', value: function render() { var children = this.props.children; var _state = this.state; var isScrolling = _state.isScrolling; var scrollTop = _state.scrollTop; var height = _state.height; return React.createElement( 'div', null, children({ height: height, isScrolling: isScrolling, scrollTop: scrollTop }) ); } }, { key: 'shouldComponentUpdate', value: function shouldComponentUpdate(nextProps, nextState) { return shallowCompare(this, nextProps, nextState); } }, { key: '_enablePointerEventsAfterDelayCallback', value: function _enablePointerEventsAfterDelayCallback() { this.setState({ isScrolling: false }); } }, { key: '_onResizeWindow', value: function _onResizeWindow(event) { var onResize = this.props.onResize; var height = window.innerHeight || 0; this.setState({ height: height }); onResize({ height: height }); } }, { key: '_onScrollWindow', value: function _onScrollWindow(event) { var onScroll = this.props.onScroll; // In IE10+ scrollY is undefined, so we replace that with the latter var scrollY = 'scrollY' in window ? window.scrollY : document.documentElement.scrollTop; var scrollTop = Math.max(0, scrollY - this._positionFromTop); var state = { isScrolling: true, scrollTop: scrollTop }; if (!this.state.isScrolling) { this.setState(state); } else { this._setNextState(state); } onScroll({ scrollTop: scrollTop }); } }]); return WindowScroller; }(Component); WindowScroller.propTypes = { /** * Function respondible for rendering children. * This function should implement the following signature: * ({ height, scrollTop }) => PropTypes.element */ children: PropTypes.func.isRequired, /** Callback to be invoked on-resize: ({ height }) */ onResize: PropTypes.func.isRequired, /** Callback to be invoked on-scroll: ({ scrollTop }) */ onScroll: PropTypes.func.isRequired }; WindowScroller.defaultProps = { onResize: function onResize() {}, onScroll: function onScroll() {} }; export default WindowScroller;