ng-virtual-list 14.0.0 → 14.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +7 -7
  2. package/esm2020/lib/components/ng-virtual-list-item.component.mjs +2 -2
  3. package/esm2020/lib/const/index.mjs +2 -1
  4. package/esm2020/lib/enums/direction.mjs +1 -1
  5. package/esm2020/lib/enums/directions.mjs +2 -2
  6. package/esm2020/lib/models/collection.model.mjs +1 -1
  7. package/esm2020/lib/models/index.mjs +1 -1
  8. package/esm2020/lib/models/item.model.mjs +1 -1
  9. package/esm2020/lib/models/render-collection.model.mjs +1 -1
  10. package/esm2020/lib/models/render-item-config.model.mjs +1 -1
  11. package/esm2020/lib/models/render-item.model.mjs +1 -1
  12. package/esm2020/lib/models/scroll-direction.model.mjs +2 -0
  13. package/esm2020/lib/models/scroll-event.model.mjs +2 -0
  14. package/esm2020/lib/models/sticky-map.model.mjs +1 -1
  15. package/esm2020/lib/ng-virtual-list.component.mjs +43 -46
  16. package/esm2020/lib/types/id.mjs +1 -1
  17. package/esm2020/lib/types/rect.mjs +1 -1
  18. package/esm2020/lib/types/size.mjs +1 -1
  19. package/esm2020/lib/utils/cacheMap.mjs +43 -2
  20. package/esm2020/lib/utils/debounce.mjs +2 -2
  21. package/esm2020/lib/utils/disposableComponent.mjs +2 -2
  22. package/esm2020/lib/utils/eventEmitter.mjs +2 -2
  23. package/esm2020/lib/utils/isDirection.mjs +2 -2
  24. package/esm2020/lib/utils/toggleClassName.mjs +2 -2
  25. package/esm2020/lib/utils/trackBox.mjs +56 -22
  26. package/esm2020/lib/utils/tracker.mjs +2 -2
  27. package/fesm2015/ng-virtual-list.mjs +143 -73
  28. package/fesm2015/ng-virtual-list.mjs.map +1 -1
  29. package/fesm2020/ng-virtual-list.mjs +146 -74
  30. package/fesm2020/ng-virtual-list.mjs.map +1 -1
  31. package/lib/components/ng-virtual-list-item.component.d.ts +1 -1
  32. package/lib/const/index.d.ts +1 -0
  33. package/lib/enums/direction.d.ts +1 -1
  34. package/lib/enums/directions.d.ts +1 -1
  35. package/lib/models/collection.model.d.ts +1 -1
  36. package/lib/models/index.d.ts +3 -1
  37. package/lib/models/item.model.d.ts +1 -1
  38. package/lib/models/render-collection.model.d.ts +1 -1
  39. package/lib/models/render-item-config.model.d.ts +1 -1
  40. package/lib/models/render-item.model.d.ts +1 -1
  41. package/lib/models/scroll-direction.model.d.ts +5 -0
  42. package/lib/models/scroll-event.model.d.ts +18 -0
  43. package/lib/models/sticky-map.model.d.ts +1 -1
  44. package/lib/ng-virtual-list.component.d.ts +7 -9
  45. package/lib/types/id.d.ts +1 -1
  46. package/lib/types/rect.d.ts +1 -1
  47. package/lib/types/size.d.ts +1 -1
  48. package/lib/utils/cacheMap.d.ts +11 -1
  49. package/lib/utils/debounce.d.ts +1 -1
  50. package/lib/utils/disposableComponent.d.ts +1 -1
  51. package/lib/utils/eventEmitter.d.ts +1 -1
  52. package/lib/utils/isDirection.d.ts +1 -1
  53. package/lib/utils/toggleClassName.d.ts +1 -1
  54. package/lib/utils/trackBox.d.ts +4 -4
  55. package/lib/utils/tracker.d.ts +1 -1
  56. package/package.json +1 -1
@@ -6,7 +6,7 @@ import { Subject, BehaviorSubject, filter, map, takeUntil, tap, combineLatest, d
6
6
 
7
7
  /**
8
8
  * Axis of the arrangement of virtual list elements.
9
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/enums/directions.ts
9
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/enums/directions.ts
10
10
  * @author Evgenii Grebennikov
11
11
  * @email djonnyx@gmail.com
12
12
  */
@@ -31,6 +31,7 @@ const DEFAULT_DYNAMIC_SIZE = false;
31
31
  const TRACK_BY_PROPERTY_NAME = 'id';
32
32
  const DEFAULT_DIRECTION = Directions.VERTICAL;
33
33
  const DISPLAY_OBJECTS_LENGTH_MESUREMENT_ERROR = 1;
34
+ const MAX_SCROLL_TO_ITERATIONS = 5;
34
35
  // presets
35
36
  const BEHAVIOR_AUTO = 'auto';
36
37
  const BEHAVIOR_INSTANT = 'instant';
@@ -57,7 +58,7 @@ const CLASS_LIST_HORIZONTAL = 'horizontal';
57
58
 
58
59
  /**
59
60
  * Virtual list item component
60
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/components/ng-virtual-list-item.component.ts
61
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/components/ng-virtual-list-item.component.ts
61
62
  * @author Evgenii Grebennikov
62
63
  * @email djonnyx@gmail.com
63
64
  */
@@ -135,7 +136,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
135
136
  const HORIZONTAL_ALIASES = [Directions.HORIZONTAL, 'horizontal'], VERTICAL_ALIASES = [Directions.VERTICAL, 'vertical'];
136
137
  /**
137
138
  * Determines the axis membership of a virtual list
138
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/isDirection.ts
139
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/isDirection.ts
139
140
  * @author Evgenii Grebennikov
140
141
  * @email djonnyx@gmail.com
141
142
  */
@@ -148,7 +149,7 @@ const isDirection = (src, expected) => {
148
149
 
149
150
  /**
150
151
  * Simple debounce function.
151
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/debounce.ts
152
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/debounce.ts
152
153
  * @author Evgenii Grebennikov
153
154
  * @email djonnyx@gmail.com
154
155
  */
@@ -179,7 +180,7 @@ const debounce = (cb, debounceTime = 0) => {
179
180
 
180
181
  /**
181
182
  * Switch css classes
182
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/toggleClassName.ts
183
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/toggleClassName.ts
183
184
  * @author Evgenii Grebennikov
184
185
  * @email djonnyx@gmail.com
185
186
  */
@@ -194,7 +195,7 @@ const toggleClassName = (el, className, remove = false) => {
194
195
 
195
196
  /**
196
197
  * Tracks display items by property
197
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/tracker.ts
198
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/tracker.ts
198
199
  * @author Evgenii Grebennikov
199
200
  * @email djonnyx@gmail.com
200
201
  */
@@ -303,7 +304,7 @@ class Tracker {
303
304
 
304
305
  /**
305
306
  * Simple event emitter
306
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/eventEmitter.ts
307
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/eventEmitter.ts
307
308
  * @author Evgenii Grebennikov
308
309
  * @email djonnyx@gmail.com
309
310
  */
@@ -407,10 +408,11 @@ class EventEmitter {
407
408
  }
408
409
  }
409
410
 
411
+ const MAX_SCROLL_DIRECTION_POOL = 10, CLEAR_SCROLL_DIRECTION_TO = 0;
410
412
  /**
411
413
  * Cache map.
412
414
  * Emits a change event on each mutation.
413
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/cacheMap.ts
415
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/cacheMap.ts
414
416
  * @author Evgenii Grebennikov
415
417
  * @email djonnyx@gmail.com
416
418
  */
@@ -421,13 +423,52 @@ class CacheMap extends EventEmitter {
421
423
  this._version = 0;
422
424
  this._previouseFullHeigh = 0;
423
425
  this._delta = 0;
426
+ this._deltaDirection = 1;
427
+ this._scrollDirectionCache = [];
428
+ this._scrollDirection = 1;
429
+ this._clearScrollDirectionDebounce = debounce(() => {
430
+ while (this._scrollDirectionCache.length > CLEAR_SCROLL_DIRECTION_TO) {
431
+ this._scrollDirectionCache.shift();
432
+ }
433
+ }, 10);
424
434
  }
425
435
  get delta() {
426
436
  return this._delta;
427
437
  }
438
+ set deltaDirection(v) {
439
+ this._deltaDirection = v;
440
+ this._scrollDirection = this.calcScrollDirection(v);
441
+ }
442
+ get deltaDirection() {
443
+ return this._deltaDirection;
444
+ }
445
+ get scrollDirection() {
446
+ return this._scrollDirection;
447
+ }
428
448
  get version() {
429
449
  return this._version;
430
450
  }
451
+ clearScrollDirectionCache() {
452
+ this._clearScrollDirectionDebounce.execute();
453
+ }
454
+ calcScrollDirection(v) {
455
+ while (this._scrollDirectionCache.length >= MAX_SCROLL_DIRECTION_POOL) {
456
+ this._scrollDirectionCache.shift();
457
+ }
458
+ this._scrollDirectionCache.push(v);
459
+ const dict = { [-1]: 0, [0]: 0, [1]: 0 };
460
+ for (let i = 0, l = this._scrollDirectionCache.length; i < l; i++) {
461
+ const dir = this._scrollDirectionCache[i];
462
+ dict[dir] += 1;
463
+ }
464
+ if (dict[-1] > dict[1]) {
465
+ return -1;
466
+ }
467
+ else if (dict[1] > dict[-1]) {
468
+ return 1;
469
+ }
470
+ return -1;
471
+ }
431
472
  bumpVersion() {
432
473
  this._version = this._version === Number.MAX_SAFE_INTEGER ? 0 : this._version + 1;
433
474
  }
@@ -461,7 +502,7 @@ class CacheMap extends EventEmitter {
461
502
  const TRACK_BOX_CHANGE_EVENT_NAME = 'change';
462
503
  /**
463
504
  * An object that performs tracking, calculations and caching.
464
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/trackBox.ts
505
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/trackBox.ts
465
506
  * @author Evgenii Grebennikov
466
507
  * @email djonnyx@gmail.com
467
508
  */
@@ -499,25 +540,46 @@ class TrackBox extends CacheMap {
499
540
  this._debounceChanges.execute(this._version);
500
541
  }
501
542
  getItemPosition(id, stickyMap, options) {
502
- const opt = Object.assign({ fromItemId: id, stickyMap, scrollDirection: undefined }, options);
543
+ const opt = Object.assign({ fromItemId: id, stickyMap }, options);
503
544
  const { scrollSize } = this.recalculateMetrics(opt);
504
545
  return scrollSize;
505
546
  }
506
547
  updateCollection(items, stickyMap, options) {
507
- const opt = Object.assign({ stickyMap, scrollDirection: undefined }, options);
548
+ const opt = Object.assign({ stickyMap }, options);
508
549
  this.cacheElements();
509
550
  const metrics = this.recalculateMetrics(Object.assign(Object.assign({}, opt), { collection: items }));
510
551
  const displayItems = this.generateDisplayCollection(items, stickyMap, metrics);
511
552
  return { displayItems, totalSize: metrics.totalSize, delta: metrics.delta };
512
553
  }
554
+ getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical) {
555
+ const sizeProperty = isVertical ? HEIGHT_PROP_NAME : WIDTH_PROP_NAME;
556
+ let offset = 0, num = 0;
557
+ for (let j = collection.length - 1; j >= i; j--) {
558
+ const item = collection[j];
559
+ let itemSize = 0;
560
+ if (map.has(item.id)) {
561
+ const bounds = map.get(item.id);
562
+ itemSize = bounds ? bounds[sizeProperty] : typicalItemSize;
563
+ }
564
+ else {
565
+ itemSize = typicalItemSize;
566
+ }
567
+ offset += itemSize;
568
+ num++;
569
+ if (offset > size) {
570
+ return { num: 0, offset };
571
+ }
572
+ }
573
+ return { num, offset };
574
+ }
513
575
  /**
514
576
  * Calculates list metrics
515
577
  */
516
578
  recalculateMetrics(options) {
517
- const { scrollDirection = 0, fromItemId, bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, stickyMap } = options;
518
- const { width, height } = bounds, sizeProperty = isVertical ? HEIGHT_PROP_NAME : WIDTH_PROP_NAME, size = isVertical ? height : width, totalLength = collection.length, typicalItemSize = itemSize, w = isVertical ? width : typicalItemSize, h = isVertical ? typicalItemSize : height, totalSize = dynamicSize ? this.getBoundsFromCache(collection, typicalItemSize, isVertical) : totalLength * typicalItemSize, snippedPos = Math.floor(scrollSize), leftItemsWeights = [], isFromId = fromItemId !== undefined && (typeof fromItemId === 'number' && fromItemId > -1)
579
+ const { fromItemId, bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, stickyMap } = options;
580
+ const { width, height } = bounds, sizeProperty = isVertical ? HEIGHT_PROP_NAME : WIDTH_PROP_NAME, size = isVertical ? height : width, totalLength = collection.length, typicalItemSize = itemSize, w = isVertical ? width : typicalItemSize, h = isVertical ? typicalItemSize : height, snippedPos = Math.floor(scrollSize), leftItemsWeights = [], isFromId = fromItemId !== undefined && (typeof fromItemId === 'number' && fromItemId > -1)
519
581
  || (typeof fromItemId === 'string' && fromItemId > '-1');
520
- let itemsFromStartToScrollEnd = -1, itemsFromDisplayEndToOffsetEnd = 0, itemsFromStartToDisplayEnd = -1, leftItemLength = 0, rightItemLength = 0, leftItemsWeight = 0, rightItemsWeight = 0, leftHiddenItemsWeight = 0, totalItemsToDisplayEndWeight = 0, itemById = undefined, itemByIdPos = 0, lastDisplayItemId = undefined, actualScrollSize = itemByIdPos, fullHeight = 0, startIndex;
582
+ let itemsFromStartToScrollEnd = -1, itemsFromDisplayEndToOffsetEnd = 0, itemsFromStartToDisplayEnd = -1, leftItemLength = 0, rightItemLength = 0, leftItemsWeight = 0, rightItemsWeight = 0, leftHiddenItemsWeight = 0, totalItemsToDisplayEndWeight = 0, itemById = undefined, itemByIdPos = 0, lastDisplayItemId = undefined, actualScrollSize = itemByIdPos, totalSize = 0, startIndex;
521
583
  if (dynamicSize) {
522
584
  let y = 0, stickyCollectionItem = undefined, stickyComponentSize = 0;
523
585
  for (let i = 0, l = collection.length; i < l; i++) {
@@ -530,21 +592,31 @@ class TrackBox extends CacheMap {
530
592
  else {
531
593
  componentSize = typicalItemSize;
532
594
  }
595
+ totalSize += componentSize;
533
596
  if (isFromId) {
534
597
  if (itemById === undefined) {
535
- leftItemsWeights.push(componentSize);
536
- leftHiddenItemsWeight += componentSize;
537
- itemsFromStartToScrollEnd = ii;
538
598
  if (stickyMap && stickyMap[collectionItem.id] > 0) {
539
599
  stickyComponentSize = componentSize;
540
600
  stickyCollectionItem = collectionItem;
541
601
  }
542
602
  if (collectionItem.id === fromItemId) {
543
- itemById = collectionItem;
544
- itemByIdPos = y;
545
603
  if (stickyCollectionItem && stickyMap && stickyMap[stickyCollectionItem.id] > 0) {
546
- itemByIdPos = itemByIdPos - stickyComponentSize;
604
+ const { num } = this.getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical);
605
+ if (num > 0) {
606
+ y -= size - componentSize;
607
+ }
608
+ else {
609
+ y -= stickyComponentSize;
610
+ leftHiddenItemsWeight -= stickyComponentSize;
611
+ }
547
612
  }
613
+ itemById = collectionItem;
614
+ itemByIdPos = y;
615
+ }
616
+ else {
617
+ leftItemsWeights.push(componentSize);
618
+ leftHiddenItemsWeight += componentSize;
619
+ itemsFromStartToScrollEnd = ii;
548
620
  }
549
621
  }
550
622
  }
@@ -572,7 +644,6 @@ class TrackBox extends CacheMap {
572
644
  }
573
645
  y += componentSize;
574
646
  }
575
- fullHeight = y;
576
647
  if (itemsFromStartToScrollEnd === -1) {
577
648
  itemsFromStartToScrollEnd = 0;
578
649
  }
@@ -598,12 +669,12 @@ class TrackBox extends CacheMap {
598
669
  rightItemsWeight = rightItemLength * typicalItemSize,
599
670
  leftHiddenItemsWeight = itemsFromStartToScrollEnd * typicalItemSize,
600
671
  totalItemsToDisplayEndWeight = itemsFromStartToDisplayEnd * typicalItemSize;
601
- actualScrollSize = scrollSize,
602
- fullHeight = totalLength * typicalItemSize;
672
+ actualScrollSize = scrollSize;
673
+ totalSize = totalLength * typicalItemSize;
603
674
  }
604
675
  startIndex = Math.min(itemsFromStartToScrollEnd - leftItemLength, totalLength > 0 ? totalLength - 1 : 0);
605
- const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta = fullHeight - this._previouseFullHeigh;
606
- if (scrollDirection === -1) {
676
+ const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta = totalSize - this._previouseFullHeigh;
677
+ if (this.scrollDirection === -1) {
607
678
  this._delta += delta;
608
679
  }
609
680
  const metrics = {
@@ -636,13 +707,17 @@ class TrackBox extends CacheMap {
636
707
  totalSize,
637
708
  typicalItemSize,
638
709
  };
639
- if (scrollDirection !== 0) {
640
- this._previouseFullHeigh = fullHeight;
641
- }
710
+ this._previouseFullHeigh = totalSize;
642
711
  return metrics;
643
712
  }
644
- clearDelta() {
713
+ clearDeltaDirection() {
714
+ this.clearScrollDirectionCache();
715
+ }
716
+ clearDelta(clearDirectionDetector = false) {
645
717
  this._delta = 0;
718
+ if (clearDirectionDetector) {
719
+ this.clearScrollDirectionCache();
720
+ }
646
721
  }
647
722
  generateDisplayCollection(items, stickyMap, metrics) {
648
723
  var _a, _b;
@@ -806,7 +881,7 @@ class TrackBox extends CacheMap {
806
881
 
807
882
  /**
808
883
  * Base disposable component
809
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/disposableComponent.ts
884
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/disposableComponent.ts
810
885
  * @author Evgenii Grebennikov
811
886
  * @email djonnyx@gmail.com
812
887
  */
@@ -834,7 +909,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
834
909
  * Virtual list component.
835
910
  * Maximum performance for extremely large lists.
836
911
  * It is based on algorithms for virtualization of screen objects.
837
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/ng-virtual-list.component.ts
912
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/ng-virtual-list.component.ts
838
913
  * @author Evgenii Grebennikov
839
914
  * @email djonnyx@gmail.com
840
915
  */
@@ -882,23 +957,17 @@ class NgVirtualListComponent extends DisposableComponent {
882
957
  this._$bounds = new BehaviorSubject(null);
883
958
  this._$scrollSize = new BehaviorSubject(0);
884
959
  this._resizeObserver = null;
885
- /**
886
- * only dynamic
887
- */
888
- this._$scrolledItemId = new BehaviorSubject(undefined);
889
960
  this._onResizeHandler = () => {
890
961
  var _a, _b, _c;
891
962
  this._$bounds.next((_c = (_b = (_a = this._container) === null || _a === void 0 ? void 0 : _a.nativeElement) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect()) !== null && _c !== void 0 ? _c : null);
892
963
  };
893
- this._scrollDirection = 0;
894
964
  this._onScrollHandler = (e) => {
895
965
  var _a;
896
- this._$scrolledItemId.next(undefined);
897
966
  const container = (_a = this._container) === null || _a === void 0 ? void 0 : _a.nativeElement;
898
967
  if (container) {
899
968
  const dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft), previouseScrollSize = this._$scrollSize.getValue();
900
969
  let actualScrollSize = scrollSize;
901
- this._scrollDirection = previouseScrollSize > scrollSize ? -1 : 1;
970
+ this._trackBox.deltaDirection = previouseScrollSize > scrollSize ? -1 : 1;
902
971
  if (dynamicSize && delta !== 0) {
903
972
  actualScrollSize = scrollSize + delta;
904
973
  const params = {
@@ -912,13 +981,15 @@ class NgVirtualListComponent extends DisposableComponent {
912
981
  }
913
982
  }
914
983
  this._$scrollSize.next(actualScrollSize);
915
- this.onScroll.emit(actualScrollSize);
984
+ this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
916
985
  }
917
986
  };
918
987
  this._scrollImmediatelyHandler = undefined;
919
- this._onScrollEndHandler = (e, fireEvent = true) => {
988
+ this._onScrollEndHandler = (e) => {
920
989
  const container = this._container;
921
990
  if (container) {
991
+ this._trackBox.clearDelta();
992
+ this._trackBox.clearDeltaDirection();
922
993
  const itemSize = this.itemSize, snapToItem = this.snapToItem, dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
923
994
  let actualScrollSize = scrollSize;
924
995
  if (dynamicSize && delta !== 0) {
@@ -928,7 +999,6 @@ class NgVirtualListComponent extends DisposableComponent {
928
999
  [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
929
1000
  behavior: BEHAVIOR_INSTANT
930
1001
  };
931
- this._trackBox.clearDelta();
932
1002
  this._$scrollSize.next(actualScrollSize);
933
1003
  container.nativeElement.scroll(params);
934
1004
  return;
@@ -946,9 +1016,7 @@ class NgVirtualListComponent extends DisposableComponent {
946
1016
  }
947
1017
  }
948
1018
  this._$scrollSize.next(actualScrollSize);
949
- if (fireEvent) {
950
- this.onScrollEnd.emit(actualScrollSize);
951
- }
1019
+ this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
952
1020
  }
953
1021
  };
954
1022
  this._$initialized = new BehaviorSubject(false);
@@ -966,7 +1034,7 @@ class NgVirtualListComponent extends DisposableComponent {
966
1034
  this._$initialized = new BehaviorSubject(false);
967
1035
  this.$initialized = this._$initialized.asObservable();
968
1036
  this._trackBox.displayComponents = this._displayComponents;
969
- const $bounds = this._$bounds.asObservable().pipe(filter(b => !!b)), $items = this.$items.pipe(map(i => !i ? [] : i)), $scrollSize = this._$scrollSize.asObservable(), $itemSize = this.$itemSize.pipe(map(v => v <= 0 ? DEFAULT_ITEM_SIZE : v)), $itemsOffset = this.$itemsOffset.pipe(map(v => v < 0 ? DEFAULT_ITEMS_OFFSET : v)), $stickyMap = this.$stickyMap.pipe(map(v => !v ? {} : v)), $snap = this.$snap, $isVertical = this.$direction.pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $dynamicSize = this.$dynamicSize, $cacheVersion = this.$cacheVersion, $scrolledItemId = this._$scrolledItemId.asObservable();
1037
+ const $bounds = this._$bounds.asObservable().pipe(filter(b => !!b)), $items = this.$items.pipe(map(i => !i ? [] : i)), $scrollSize = this._$scrollSize.asObservable(), $itemSize = this.$itemSize.pipe(map(v => v <= 0 ? DEFAULT_ITEM_SIZE : v)), $itemsOffset = this.$itemsOffset.pipe(map(v => v < 0 ? DEFAULT_ITEMS_OFFSET : v)), $stickyMap = this.$stickyMap.pipe(map(v => !v ? {} : v)), $snap = this.$snap, $isVertical = this.$direction.pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $dynamicSize = this.$dynamicSize, $cacheVersion = this.$cacheVersion;
970
1038
  $isVertical.pipe(takeUntil(this._$unsubscribe), tap(v => {
971
1039
  this._isVertical = v;
972
1040
  const el = this._elementRef.nativeElement;
@@ -975,31 +1043,19 @@ class NgVirtualListComponent extends DisposableComponent {
975
1043
  $dynamicSize.pipe(takeUntil(this._$unsubscribe), tap(dynamicSize => {
976
1044
  this.listenCacheChangesIfNeed(dynamicSize);
977
1045
  })).subscribe();
978
- combineLatest([this.$initialized, $scrolledItemId, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
1046
+ combineLatest([this.$initialized, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
979
1047
  $itemsOffset, $snap, $isVertical, $dynamicSize, $cacheVersion,
980
- ]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), debounceTime(0), filter(([initialized]) => !!initialized), switchMap(([, scrolledItemId, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, cacheVersion,]) => {
1048
+ ]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), debounceTime(0), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, cacheVersion,]) => {
981
1049
  const { width, height } = bounds;
982
1050
  let actualScrollSize = scrollSize;
983
1051
  const opts = {
984
1052
  bounds: { width, height }, collection: items, dynamicSize, isVertical, itemSize,
985
- itemsOffset, scrollSize: scrollSize, snap, fromItemId: scrolledItemId,
1053
+ itemsOffset, scrollSize: scrollSize, snap,
986
1054
  };
987
- if (dynamicSize && scrolledItemId !== undefined) {
988
- const scrollSize = this._trackBox.getItemPosition(scrolledItemId, stickyMap, Object.assign(Object.assign({}, opts), { scrollSize: actualScrollSize }));
989
- actualScrollSize = scrollSize;
990
- this._$scrollSize.next(actualScrollSize);
991
- }
992
- const scrollDirection = this._scrollDirection, { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, Object.assign(Object.assign({}, opts), { scrollSize: actualScrollSize, scrollDirection }));
1055
+ const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, Object.assign(Object.assign({}, opts), { scrollSize: actualScrollSize }));
993
1056
  this.resetBoundsSize(isVertical, totalSize);
994
1057
  this.createDisplayComponentsIfNeed(displayItems);
995
1058
  this.tracking();
996
- if (dynamicSize && scrolledItemId !== undefined) {
997
- const container = this._container;
998
- if (container) {
999
- const params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior: BEHAVIOR_AUTO };
1000
- this.scrollImmediately(container, params);
1001
- }
1002
- }
1003
1059
  return of(displayItems);
1004
1060
  })).subscribe();
1005
1061
  combineLatest([this.$initialized, this.$itemRenderer]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), filter(([initialized]) => !!initialized), tap(([, itemRenderer]) => {
@@ -1214,6 +1270,13 @@ class NgVirtualListComponent extends DisposableComponent {
1214
1270
  * Behavior accepts the values ​​"auto", "instant" and "smooth".
1215
1271
  */
1216
1272
  scrollTo(id, behavior = BEHAVIOR_AUTO) {
1273
+ this.scrollToExecutor(id, behavior);
1274
+ }
1275
+ clearScrollToRepeatExecutionTimeout() {
1276
+ clearTimeout(this._scrollToRepeatExecutionTimeout);
1277
+ }
1278
+ scrollToExecutor(id, behavior, iteration = 0) {
1279
+ this.clearScrollToRepeatExecutionTimeout();
1217
1280
  const items = this.items;
1218
1281
  if (!items || !items.length) {
1219
1282
  return;
@@ -1225,24 +1288,33 @@ class NgVirtualListComponent extends DisposableComponent {
1225
1288
  container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
1226
1289
  container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
1227
1290
  }
1228
- const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, opts = {
1291
+ const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, isVertical = this._isVertical, opts = {
1229
1292
  bounds: { width, height }, collection: items, dynamicSize, isVertical: this._isVertical, itemSize,
1230
- itemsOffset: this.itemsOffset, scrollSize: this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
1293
+ itemsOffset: this.itemsOffset, scrollSize: isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
1231
1294
  snap: this.snap, fromItemId: id,
1232
- }, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
1233
- this._$scrolledItemId.next(id);
1295
+ }, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
1234
1296
  this._$scrollSize.next(scrollSize);
1235
1297
  if (container) {
1236
1298
  const handler = () => {
1237
1299
  if (container) {
1238
1300
  container.nativeElement.removeEventListener(SCROLL_END, handler);
1239
- clearTimeout(this._scrollToTimeout);
1240
- this._scrollToTimeout = setTimeout(() => {
1301
+ const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, Object.assign(Object.assign({}, opts), { scrollSize, fromItemId: id }));
1302
+ this.resetBoundsSize(isVertical, totalSize);
1303
+ this.createDisplayComponentsIfNeed(displayItems);
1304
+ this.tracking();
1305
+ const _scrollSize = this._trackBox.getItemPosition(id, stickyMap, Object.assign(Object.assign({}, opts), { scrollSize, fromItemId: id }));
1306
+ if (scrollSize < _scrollSize && iteration < MAX_SCROLL_TO_ITERATIONS) {
1307
+ this.clearScrollToRepeatExecutionTimeout();
1308
+ this._scrollToRepeatExecutionTimeout = setTimeout(() => {
1309
+ this.scrollToExecutor(id, BEHAVIOR_INSTANT, iteration + 1);
1310
+ });
1311
+ }
1312
+ else {
1313
+ this._$scrollSize.next(scrollSize);
1314
+ this.onScroll.emit({ scrollSize, direction: this._trackBox.scrollDirection });
1241
1315
  container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
1242
1316
  container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
1243
- }, 100);
1244
- this.listenCacheChangesIfNeed(dynamicSize);
1245
- this.onScroll.emit(scrollSize);
1317
+ }
1246
1318
  }
1247
1319
  };
1248
1320
  container.nativeElement.addEventListener(SCROLL_END, handler);
@@ -1272,9 +1344,7 @@ class NgVirtualListComponent extends DisposableComponent {
1272
1344
  }
1273
1345
  ngOnDestroy() {
1274
1346
  super.ngOnDestroy();
1275
- if (this._scrollToTimeout) {
1276
- clearTimeout(this._scrollToTimeout);
1277
- }
1347
+ this.clearScrollToRepeatExecutionTimeout();
1278
1348
  if (this._trackBox) {
1279
1349
  this._trackBox.dispose();
1280
1350
  }