import * as React from 'react' ; import throttle from 'lodash/throttle' ; import { InfiniteLoaderPropTypes } from './types' ; import noop from '../_utils/noop' ; class InfiniteLoader extends React . PureComponent < InfiniteLoaderPropTypes , any > { static defaultProps = { style : { } , className : 'Yep-infinite-loader' , onScroll : noop , } ; throttledOnScrollListener : any ; _scrollableNode : any ; el : any ; _infScroll : any ; constructor ( props ) { super ( props ) ; this . state = { showLoader : false , lastScrollTop : 0 , actionTriggered : false , } ; this . onScrollListener = this . onScrollListener . bind ( this ) ; this . throttledOnScrollListener = throttle ( this . onScrollListener , 150 ) . bind ( this ) ; this . getScrollableTarget = this . getScrollableTarget . bind ( this ) ; } componentDidMount ( ) { this . _scrollableNode = this . getScrollableTarget ( ) ; this . el = this . props . height ? this . _infScroll : this . props . scrollableTarget ? this . _scrollableNode : window ; this . el . addEventListener ( 'scroll' , this . throttledOnScrollListener ) ; if ( typeof this . props . initialScrollY === 'number' && this . el . scrollHeight > this . props . initialScrollY ) { if ( typeof this . el . scrollTo === 'function' ) { this . el . scrollTo ( 0 , this . props . initialScrollY ) ; } else { this . el . scrollTop = this . props . initialScrollY ; } } } componentWillUnmount ( ) { this . el . removeEventListener ( 'scroll' , this . throttledOnScrollListener ) ; } componentWillReceiveProps ( props ) { if ( this . props . dataLength === props . dataLength ) return ; this . setState ( { showLoader : false , actionTriggered : false , } ) ; } getScrollableTarget ( ) { if ( this . props . scrollableTarget instanceof HTMLElement ) return this . props . scrollableTarget ; if ( typeof this . props . scrollableTarget === 'string' ) { return document . getElementById ( this . props . scrollableTarget ) ; } return this . el && this . el . parentNode ; } isElementAtBottom ( target , scrollThreshold = 0.8 ) { const clientHeight = target . clientHeight ; return target . scrollTop + clientHeight >= scrollThreshold * target . scrollHeight ; } onScrollListener ( event ) { const { onScroll } = this . props ; if ( onScroll && typeof onScroll === 'function' ) { setTimeout ( ( ) => onScroll ( event ) , 0 ) ; } const target = this . props . height || this . _scrollableNode ? event . target : document . documentElement . scrollTop ? document . documentElement : ( document . body as any ) ; if ( this . state . actionTriggered ) return ; const atBottom = this . isElementAtBottom ( target , this . props . scrollThreshold ) ; if ( atBottom && this . props . hasMore ) { this . setState ( { actionTriggered : true , showLoader : true } ) ; this . props . loadMore ( ) ; } this . setState ( { lastScrollTop : target . scrollTop } ) ; } render ( ) { const { className , style , hasChildren , children , height , hasMore , endMessage , loader } = this . props ; const componentStyle = { height : height || 'auto' , overflow : 'auto' , WebkitOverflowScrolling : 'touch' , ... style , } as React . CSSProperties ; const hasChild = hasChildren || ! ! ( children && React . Children . count ( children ) ) ; return ( < div className = { className } ref = { infScroll => ( this . _infScroll = infScroll ) } style = { componentStyle } > { children } { ! this . state . showLoader && ! hasChild && hasMore && loader } { this . state . showLoader && hasMore && loader } { ! hasMore && endMessage } < /div>); } } export default InfiniteLoader ;