@@ -7,12 +7,14 @@ import {scrollHandler} from "./event-handlers";
7
7
import Emitter from "tiny-emitter" ;
8
8
import { getDistanceToFold , getRootRect , getScrollPosition } from "./dimensions" ;
9
9
import { nextHandler } from './next-handler' ;
10
+ import { prevHandler } from './prev-handler' ;
10
11
import Pagination from './pagination' ;
11
12
import Spinner from './spinner' ;
12
13
import Logger from './logger' ;
13
14
import Paging from './paging' ;
14
15
import Trigger from './trigger' ;
15
16
import { appendFn } from './append' ;
17
+ import { prependFn } from './prepend' ;
16
18
import * as Events from './events' ;
17
19
import ResizeObserverFactory from './resize-observer' ;
18
20
import Prefill from "./prefill" ;
@@ -36,21 +38,29 @@ export default class InfiniteAjaxScroll {
36
38
}
37
39
38
40
this . nextHandler = nextHandler ;
41
+ this . prevHandler = prevHandler ;
39
42
40
43
if ( this . options . next === false ) {
41
44
this . nextHandler = function ( ) { }
42
45
} else if ( typeof this . options . next === 'function' ) {
43
46
this . nextHandler = this . options . next ;
44
47
}
45
48
49
+ if ( this . options . prev === false ) {
50
+ this . prevHandler = function ( ) { }
51
+ } else if ( typeof this . options . prev === 'function' ) {
52
+ this . prevHandler = this . options . prev ;
53
+ }
54
+
46
55
this . resizeObserver = ResizeObserverFactory ( this , this . scrollContainer ) ;
47
56
this . _scrollListener = throttle ( scrollHandler , 200 ) . bind ( this ) ;
48
57
49
58
this . ready = false ;
50
59
this . bindOnReady = true ;
51
60
this . binded = false ;
52
61
this . paused = false ;
53
- this . pageIndex = this . sentinel ( ) ? 0 : - 1 ;
62
+ this . pageIndexPrev = 0 ;
63
+ this . pageIndex = this . pageIndexNext = this . sentinel ( ) ? 0 : - 1 ;
54
64
55
65
this . on ( Events . HIT , ( ) => {
56
66
if ( ! this . loadOnScroll ) {
@@ -60,6 +70,14 @@ export default class InfiniteAjaxScroll {
60
70
this . next ( ) ;
61
71
} ) ;
62
72
73
+ this . on ( Events . TOP , ( ) => {
74
+ if ( ! this . loadOnScroll ) {
75
+ return ;
76
+ }
77
+
78
+ this . prev ( ) ;
79
+ } ) ;
80
+
63
81
this . on ( Events . SCROLLED , this . measure ) ;
64
82
this . on ( Events . RESIZED , this . measure ) ;
65
83
@@ -74,6 +92,11 @@ export default class InfiniteAjaxScroll {
74
92
// prefill/measure after all plugins are done binding
75
93
this . on ( Events . BINDED , this . prefill . prefill . bind ( this . prefill ) ) ;
76
94
95
+ this . hitFirst = this . hitLast = false ;
96
+
97
+ this . on ( Events . LAST , ( ) => this . hitLast = true ) ;
98
+ this . on ( Events . FIRST , ( ) => this . hitFirst = true ) ;
99
+
77
100
let ready = ( ) => {
78
101
if ( this . ready ) {
79
102
return ;
@@ -132,6 +155,10 @@ export default class InfiniteAjaxScroll {
132
155
}
133
156
134
157
next ( ) {
158
+ if ( this . hitLast ) {
159
+ return ;
160
+ }
161
+
135
162
if ( ! this . binded ) {
136
163
if ( ! this . ready ) {
137
164
return this . once ( Events . BINDED , this . next ) ;
@@ -142,23 +169,46 @@ export default class InfiniteAjaxScroll {
142
169
143
170
this . pause ( ) ;
144
171
145
- const pageIndex = this . pageIndex + 1 ;
172
+ const pageIndex = this . pageIndexNext + 1 ;
146
173
147
- this . emitter . emit ( Events . NEXT , { pageIndex : this . pageIndex + 1 } ) ;
174
+ this . emitter . emit ( Events . NEXT , { pageIndex : this . pageIndexNext + 1 } ) ;
148
175
149
176
return Promise . resolve ( this . nextHandler ( pageIndex ) )
150
177
. then ( ( hasNextUrl ) => {
151
- this . pageIndex = pageIndex ;
178
+ this . pageIndexNext = pageIndex ;
152
179
153
180
if ( ! hasNextUrl ) {
154
181
this . emitter . emit ( Events . LAST ) ;
155
-
156
- return ;
157
182
}
158
183
159
184
this . resume ( ) ;
160
185
} ) . then ( ( ) => {
161
- this . emitter . emit ( Events . NEXTED , { pageIndex : this . pageIndex } ) ;
186
+ this . emitter . emit ( Events . NEXTED , { pageIndex : this . pageIndexNext } ) ;
187
+ } ) ;
188
+ }
189
+
190
+ prev ( ) {
191
+ if ( ! this . binded || this . hitFirst ) {
192
+ return ;
193
+ }
194
+
195
+ this . pause ( ) ;
196
+
197
+ const pageIndex = this . pageIndexPrev - 1 ;
198
+
199
+ this . emitter . emit ( Events . PREV , { pageIndex : this . pageIndexPrev - 1 } ) ;
200
+
201
+ return Promise . resolve ( this . prevHandler ( pageIndex ) )
202
+ . then ( ( hasPrevUrl ) => {
203
+ this . pageIndexPrev = pageIndex ;
204
+
205
+ this . resume ( ) ;
206
+
207
+ if ( ! hasPrevUrl ) {
208
+ this . emitter . emit ( Events . FIRST ) ;
209
+ }
210
+ } ) . then ( ( ) => {
211
+ this . emitter . emit ( Events . PREVED , { pageIndex : this . pageIndexPrev } ) ;
162
212
} ) ;
163
213
}
164
214
@@ -272,6 +322,48 @@ export default class InfiniteAjaxScroll {
272
322
} ) ;
273
323
}
274
324
325
+ /**
326
+ * @param {array<Element> } items
327
+ * @param {Element|null } parent
328
+ */
329
+ prepend ( items , parent ) {
330
+ let ias = this ;
331
+ parent = parent || ias . container ;
332
+
333
+ let event = {
334
+ items,
335
+ parent,
336
+ prependFn
337
+ } ;
338
+
339
+ ias . emitter . emit ( Events . PREPEND , event ) ;
340
+
341
+ let executor = ( resolve ) => {
342
+ window . requestAnimationFrame ( ( ) => {
343
+ const first = ias . first ( ) ;
344
+ const scrollPositionStart = getScrollPosition ( this . scrollContainer ) ;
345
+ const topStart = first . getBoundingClientRect ( ) . top + scrollPositionStart . y ;
346
+
347
+ Promise . resolve ( event . prependFn ( event . items , event . parent , ias . first ( ) ) )
348
+ . then ( ( ) => {
349
+ const scrollPositionEnd = getScrollPosition ( this . scrollContainer ) ;
350
+ const topEnd = first . getBoundingClientRect ( ) . top + scrollPositionEnd . y ;
351
+
352
+ let deltaY = topEnd - topStart ;
353
+
354
+ this . scrollContainer . scrollTo ( scrollPositionEnd . x , deltaY ) ;
355
+ } )
356
+ . then ( ( ) => {
357
+ resolve ( { items, parent} ) ;
358
+ } ) ;
359
+ } ) ;
360
+ } ;
361
+
362
+ return ( new Promise ( executor ) ) . then ( ( event ) => {
363
+ ias . emitter . emit ( Events . PREPENDED , event ) ;
364
+ } ) ;
365
+ }
366
+
275
367
sentinel ( ) {
276
368
const items = $ ( this . options . item , this . container ) ;
277
369
@@ -282,6 +374,16 @@ export default class InfiniteAjaxScroll {
282
374
return items [ items . length - 1 ] ;
283
375
}
284
376
377
+ first ( ) {
378
+ const items = $ ( this . options . item , this . container ) ;
379
+
380
+ if ( ! items . length ) {
381
+ return null ;
382
+ }
383
+
384
+ return items [ 0 ] ;
385
+ }
386
+
285
387
pause ( ) {
286
388
this . paused = true ;
287
389
}
@@ -298,9 +400,15 @@ export default class InfiniteAjaxScroll {
298
400
this . loadOnScroll = false ;
299
401
}
300
402
403
+ /**
404
+ * @deprecated replaced by distanceBottom
405
+ */
301
406
distance ( rootRect , sentinel ) {
302
- const _rootRect = rootRect || getRootRect ( this . scrollContainer ) ;
407
+ return this . distanceBottom ( rootRect , sentinel ) ;
408
+ }
303
409
410
+ distanceBottom ( rootRect , sentinel ) {
411
+ const _rootRect = rootRect || getRootRect ( this . scrollContainer ) ;
304
412
const _sentinel = sentinel || this . sentinel ( ) ;
305
413
306
414
const scrollPosition = getScrollPosition ( this . scrollContainer ) ;
@@ -313,8 +421,14 @@ export default class InfiniteAjaxScroll {
313
421
return distance ;
314
422
}
315
423
424
+ distanceTop ( ) {
425
+ const scrollPosition = getScrollPosition ( this . scrollContainer ) ;
426
+
427
+ return scrollPosition . y - this . negativeMargin ;
428
+ }
429
+
316
430
measure ( ) {
317
- if ( this . paused ) {
431
+ if ( this . paused || ( this . hitFirst && this . hitLast ) ) {
318
432
return ;
319
433
}
320
434
@@ -330,12 +444,20 @@ export default class InfiniteAjaxScroll {
330
444
return ;
331
445
}
332
446
333
- const sentinel = this . sentinel ( ) ;
447
+ if ( ! this . hitFirst ) {
448
+ let distanceTop = this . distanceTop ( ) ;
334
449
335
- let distance = this . distance ( rootRect , sentinel ) ;
450
+ if ( distanceTop <= 0 ) {
451
+ this . emitter . emit ( Events . TOP , { distance : distanceTop } ) ;
452
+ }
453
+ }
336
454
337
- if ( distance <= 0 ) {
338
- this . emitter . emit ( Events . HIT , { distance} ) ;
455
+ if ( ! this . hitLast ) {
456
+ let distanceBottom = this . distanceBottom ( rootRect , this . sentinel ( ) ) ;
457
+
458
+ if ( distanceBottom <= 0 ) {
459
+ this . emitter . emit ( Events . HIT , { distance : distanceBottom } ) ;
460
+ }
339
461
}
340
462
}
341
463
0 commit comments