11import React , { Component , ReactNode , CSSProperties } from "react" ;
2- import PropTypes from "prop-types" ;
32import throttle from "./utils/throttle" ;
43import { ThresholdUnits , parseThreshold } from "./utils/threshold" ;
4+
55type Fn = ( ) => any ;
66interface Props {
77 next : Fn ;
8- hasMore : ReactNode ;
8+ hasMore : boolean ;
99 children : ReactNode ;
1010 loader : ReactNode ;
1111 scrollThreshold : number | string ;
1212 endMessage : ReactNode ;
1313 style : CSSProperties ;
1414 height : number ;
1515 scrollableTarget : ReactNode ;
16- hasChildren : ReactNode ;
17- pullDownToRefresh : ReactNode ;
16+ hasChildren : boolean ;
17+ pullDownToRefresh : boolean ;
1818 pullDownToRefreshContent : ReactNode ;
1919 releaseToRefreshContent : ReactNode ;
2020 pullDownToRefreshThreshold : number ;
2121 refreshFunction : Fn ;
22- onScroll : Fn ;
22+ onScroll : ( e : MouseEvent ) => any ;
2323 dataLength : number ;
24+ initialScrollY : number ;
25+ key : string ;
26+ className : string ;
2427}
2528
2629interface State {
@@ -29,7 +32,6 @@ interface State {
2932}
3033
3134export default class InfiniteScroll extends Component < Props , State > {
32- private throttledOnScrollListener : ( ) => void ;
3335 constructor ( props : Props ) {
3436 super ( props ) ;
3537
@@ -48,32 +50,43 @@ export default class InfiniteScroll extends Component<Props, State> {
4850 this . getScrollableTarget = this . getScrollableTarget . bind ( this ) ;
4951 }
5052
53+ private throttledOnScrollListener : ( ) => void ;
54+ private _scrollableNode : HTMLElement | undefined | null ;
55+ private el : HTMLElement | undefined | Window & typeof globalThis ;
56+ private _infScroll : HTMLDivElement | undefined ;
5157 private lastScrollTop = 0 ;
5258 private actionTriggered = false ;
59+ private _pullDown : HTMLDivElement | undefined ;
5360
5461 // variables to keep track of pull down behaviour
55- private startY = 0 ;
56- private currentY = 0 ;
57- private dragging = false ;
62+ private startY : number = 0 ;
63+ private currentY : number = 0 ;
64+ private dragging : boolean = false ;
5865
5966 // will be populated in componentDidMount
6067 // based on the height of the pull down element
61- private maxPullDownDistance = 0 ;
68+ private maxPullDownDistance : number = 0 ;
69+
6270 componentDidMount ( ) {
6371 this . _scrollableNode = this . getScrollableTarget ( ) ;
6472 this . el = this . props . height
6573 ? this . _infScroll
6674 : this . _scrollableNode || window ;
67- this . el . addEventListener ( "scroll" , this . throttledOnScrollListener ) ;
75+
76+ if ( this . el ) {
77+ this . el . addEventListener ( "scroll" , this . throttledOnScrollListener ) ;
78+ }
6879
6980 if (
7081 typeof this . props . initialScrollY === "number" &&
82+ this . el &&
83+ this . el instanceof HTMLElement &&
7184 this . el . scrollHeight > this . props . initialScrollY
7285 ) {
7386 this . el . scrollTo ( 0 , this . props . initialScrollY ) ;
7487 }
7588
76- if ( this . props . pullDownToRefresh ) {
89+ if ( this . props . pullDownToRefresh && this . el ) {
7790 this . el . addEventListener ( "touchstart" , this . onStart ) ;
7891 this . el . addEventListener ( "touchmove" , this . onMove ) ;
7992 this . el . addEventListener ( "touchend" , this . onEnd ) ;
@@ -83,7 +96,12 @@ export default class InfiniteScroll extends Component<Props, State> {
8396 this . el . addEventListener ( "mouseup" , this . onEnd ) ;
8497
8598 // get BCR of pullDown element to position it above
86- this . maxPullDownDistance = this . _pullDown . firstChild . getBoundingClientRect ( ) . height ;
99+ this . maxPullDownDistance =
100+ ( this . _pullDown &&
101+ this . _pullDown . firstChild &&
102+ ( this . _pullDown . firstChild as HTMLDivElement ) . getBoundingClientRect ( )
103+ . height ) ||
104+ 0 ;
87105 this . forceUpdate ( ) ;
88106
89107 if ( typeof this . props . refreshFunction !== "function" ) {
@@ -97,20 +115,22 @@ export default class InfiniteScroll extends Component<Props, State> {
97115 }
98116
99117 componentWillUnmount ( ) {
100- this . el . removeEventListener ( "scroll" , this . throttledOnScrollListener ) ;
118+ if ( this . el ) {
119+ this . el . removeEventListener ( "scroll" , this . throttledOnScrollListener ) ;
101120
102- if ( this . props . pullDownToRefresh ) {
103- this . el . removeEventListener ( "touchstart" , this . onStart ) ;
104- this . el . removeEventListener ( "touchmove" , this . onMove ) ;
105- this . el . removeEventListener ( "touchend" , this . onEnd ) ;
121+ if ( this . props . pullDownToRefresh ) {
122+ this . el . removeEventListener ( "touchstart" , this . onStart ) ;
123+ this . el . removeEventListener ( "touchmove" , this . onMove ) ;
124+ this . el . removeEventListener ( "touchend" , this . onEnd ) ;
106125
107- this . el . removeEventListener ( "mousedown" , this . onStart ) ;
108- this . el . removeEventListener ( "mousemove" , this . onMove ) ;
109- this . el . removeEventListener ( "mouseup" , this . onEnd ) ;
126+ this . el . removeEventListener ( "mousedown" , this . onStart ) ;
127+ this . el . removeEventListener ( "mousemove" , this . onMove ) ;
128+ this . el . removeEventListener ( "mouseup" , this . onEnd ) ;
129+ }
110130 }
111131 }
112132
113- componentWillReceiveProps ( props ) {
133+ componentWillReceiveProps ( props : Props ) {
114134 // do nothing when dataLength and key are unchanged
115135 if (
116136 this . props . key === props . key &&
@@ -141,20 +161,32 @@ export default class InfiniteScroll extends Component<Props, State> {
141161 return null ;
142162 }
143163
144- onStart ( evt ) {
164+ onStart : EventListener = ( evt : Event ) => {
145165 if ( this . lastScrollTop ) return ;
146166
147167 this . dragging = true ;
148- this . startY = evt . pageY || evt . touches [ 0 ] . pageY ;
168+
169+ if ( evt instanceof MouseEvent ) {
170+ this . startY = evt . pageY ;
171+ } else if ( evt instanceof TouchEvent ) {
172+ this . startY = evt . touches [ 0 ] . pageY ;
173+ }
149174 this . currentY = this . startY ;
150175
151- this . _infScroll . style . willChange = "transform" ;
152- this . _infScroll . style . transition = `transform 0.2s cubic-bezier(0,0,0.31,1)` ;
153- }
176+ if ( this . _infScroll ) {
177+ this . _infScroll . style . willChange = "transform" ;
178+ this . _infScroll . style . transition = `transform 0.2s cubic-bezier(0,0,0.31,1)` ;
179+ }
180+ } ;
154181
155- onMove ( evt ) {
182+ onMove : EventListener = ( evt : Event ) => {
156183 if ( ! this . dragging ) return ;
157- this . currentY = evt . pageY || evt . touches [ 0 ] . pageY ;
184+
185+ if ( evt instanceof MouseEvent ) {
186+ this . currentY = evt . pageY ;
187+ } else if ( evt instanceof TouchEvent ) {
188+ this . currentY = evt . touches [ 0 ] . pageY ;
189+ }
158190
159191 // user is scrolling down to up
160192 if ( this . currentY < this . startY ) return ;
@@ -168,12 +200,14 @@ export default class InfiniteScroll extends Component<Props, State> {
168200 // so you can drag upto 1.5 times of the maxPullDownDistance
169201 if ( this . currentY - this . startY > this . maxPullDownDistance * 1.5 ) return ;
170202
171- this . _infScroll . style . overflow = "visible" ;
172- this . _infScroll . style . transform = `translate3d(0px, ${ this . currentY -
173- this . startY } px, 0px)`;
174- }
203+ if ( this . _infScroll ) {
204+ this . _infScroll . style . overflow = "visible" ;
205+ this . _infScroll . style . transform = `translate3d(0px, ${ this . currentY -
206+ this . startY } px, 0px)`;
207+ }
208+ } ;
175209
176- onEnd ( evt ) {
210+ onEnd : EventListener = evt => {
177211 this . startY = 0 ;
178212 this . currentY = 0 ;
179213
@@ -191,9 +225,12 @@ export default class InfiniteScroll extends Component<Props, State> {
191225 this . _infScroll . style . willChange = "none" ;
192226 }
193227 } ) ;
194- }
228+ } ;
195229
196- isElementAtBottom ( target , scrollThreshold = 0.8 ) {
230+ isElementAtBottom (
231+ target : HTMLElement ,
232+ scrollThreshold : string | number = 0.8
233+ ) {
197234 const clientHeight =
198235 target === document . body || target === document . documentElement
199236 ? window . screen . availHeight
@@ -213,7 +250,7 @@ export default class InfiniteScroll extends Component<Props, State> {
213250 ) ;
214251 }
215252
216- onScrollListener ( event ) {
253+ onScrollListener ( event : MouseEvent ) {
217254 if ( typeof this . props . onScroll === "function" ) {
218255 // Execute this callback in next tick so that it does not affect the
219256 // functionality of the library.
@@ -222,7 +259,7 @@ export default class InfiniteScroll extends Component<Props, State> {
222259
223260 let target =
224261 this . props . height || this . _scrollableNode
225- ? event . target
262+ ? ( event . target as HTMLElement )
226263 : document . documentElement . scrollTop
227264 ? document . documentElement
228265 : document . body ;
@@ -249,10 +286,14 @@ export default class InfiniteScroll extends Component<Props, State> {
249286 overflow : "auto" ,
250287 WebkitOverflowScrolling : "touch" ,
251288 ...this . props . style
252- } ;
289+ } as CSSProperties ;
253290 const hasChildren =
254291 this . props . hasChildren ||
255- ! ! ( this . props . children && this . props . children . length ) ;
292+ ! ! (
293+ this . props . children &&
294+ this . props . children instanceof Array &&
295+ this . props . children . length
296+ ) ;
256297
257298 // because heighted infiniteScroll visualy breaks
258299 // on drag down as overflow becomes visible
@@ -264,13 +305,13 @@ export default class InfiniteScroll extends Component<Props, State> {
264305 < div style = { outerDivStyle } >
265306 < div
266307 className = { `infinite-scroll-component ${ this . props . className || "" } ` }
267- ref = { infScroll => ( this . _infScroll = infScroll ) }
308+ ref = { ( infScroll : HTMLDivElement ) => ( this . _infScroll = infScroll ) }
268309 style = { style }
269310 >
270311 { this . props . pullDownToRefresh && (
271312 < div
272313 style = { { position : "relative" } }
273- ref = { pullDown => ( this . _pullDown = pullDown ) }
314+ ref = { ( pullDown : HTMLDivElement ) => ( this . _pullDown = pullDown ) }
274315 >
275316 < div
276317 style = { {
@@ -298,31 +339,3 @@ export default class InfiniteScroll extends Component<Props, State> {
298339 ) ;
299340 }
300341}
301-
302- InfiniteScroll . defaultProps = {
303- pullDownToRefreshContent : < h3 > Pull down to refresh</ h3 > ,
304- releaseToRefreshContent : < h3 > Release to refresh</ h3 > ,
305- pullDownToRefreshThreshold : 100 ,
306- disableBrowserPullToRefresh : true
307- } ;
308-
309- InfiniteScroll . propTypes = {
310- next : PropTypes . func ,
311- hasMore : PropTypes . bool ,
312- children : PropTypes . node ,
313- loader : PropTypes . node . isRequired ,
314- scrollThreshold : PropTypes . oneOfType ( [ PropTypes . number , PropTypes . string ] ) ,
315- endMessage : PropTypes . node ,
316- style : PropTypes . object ,
317- height : PropTypes . number ,
318- scrollableTarget : PropTypes . node ,
319- hasChildren : PropTypes . bool ,
320- pullDownToRefresh : PropTypes . bool ,
321- pullDownToRefreshContent : PropTypes . node ,
322- releaseToRefreshContent : PropTypes . node ,
323- pullDownToRefreshThreshold : PropTypes . number ,
324- refreshFunction : PropTypes . func ,
325- onScroll : PropTypes . func ,
326- dataLength : PropTypes . number . isRequired ,
327- key : PropTypes . string
328- } ;
0 commit comments