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
  */
@@ -134,7 +135,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
134
135
  const HORIZONTAL_ALIASES = [Directions.HORIZONTAL, 'horizontal'], VERTICAL_ALIASES = [Directions.VERTICAL, 'vertical'];
135
136
  /**
136
137
  * Determines the axis membership of a virtual list
137
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/isDirection.ts
138
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/isDirection.ts
138
139
  * @author Evgenii Grebennikov
139
140
  * @email djonnyx@gmail.com
140
141
  */
@@ -147,7 +148,7 @@ const isDirection = (src, expected) => {
147
148
 
148
149
  /**
149
150
  * Simple debounce function.
150
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/debounce.ts
151
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/debounce.ts
151
152
  * @author Evgenii Grebennikov
152
153
  * @email djonnyx@gmail.com
153
154
  */
@@ -178,7 +179,7 @@ const debounce = (cb, debounceTime = 0) => {
178
179
 
179
180
  /**
180
181
  * Switch css classes
181
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/toggleClassName.ts
182
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/toggleClassName.ts
182
183
  * @author Evgenii Grebennikov
183
184
  * @email djonnyx@gmail.com
184
185
  */
@@ -193,7 +194,7 @@ const toggleClassName = (el, className, remove = false) => {
193
194
 
194
195
  /**
195
196
  * Tracks display items by property
196
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/tracker.ts
197
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/tracker.ts
197
198
  * @author Evgenii Grebennikov
198
199
  * @email djonnyx@gmail.com
199
200
  */
@@ -301,7 +302,7 @@ class Tracker {
301
302
 
302
303
  /**
303
304
  * Simple event emitter
304
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/eventEmitter.ts
305
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/eventEmitter.ts
305
306
  * @author Evgenii Grebennikov
306
307
  * @email djonnyx@gmail.com
307
308
  */
@@ -405,10 +406,11 @@ class EventEmitter {
405
406
  }
406
407
  }
407
408
 
409
+ const MAX_SCROLL_DIRECTION_POOL = 10, CLEAR_SCROLL_DIRECTION_TO = 0;
408
410
  /**
409
411
  * Cache map.
410
412
  * Emits a change event on each mutation.
411
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/cacheMap.ts
413
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/cacheMap.ts
412
414
  * @author Evgenii Grebennikov
413
415
  * @email djonnyx@gmail.com
414
416
  */
@@ -419,13 +421,52 @@ class CacheMap extends EventEmitter {
419
421
  this._version = 0;
420
422
  this._previouseFullHeigh = 0;
421
423
  this._delta = 0;
424
+ this._deltaDirection = 1;
425
+ this._scrollDirectionCache = [];
426
+ this._scrollDirection = 1;
427
+ this._clearScrollDirectionDebounce = debounce(() => {
428
+ while (this._scrollDirectionCache.length > CLEAR_SCROLL_DIRECTION_TO) {
429
+ this._scrollDirectionCache.shift();
430
+ }
431
+ }, 10);
422
432
  }
423
433
  get delta() {
424
434
  return this._delta;
425
435
  }
436
+ set deltaDirection(v) {
437
+ this._deltaDirection = v;
438
+ this._scrollDirection = this.calcScrollDirection(v);
439
+ }
440
+ get deltaDirection() {
441
+ return this._deltaDirection;
442
+ }
443
+ get scrollDirection() {
444
+ return this._scrollDirection;
445
+ }
426
446
  get version() {
427
447
  return this._version;
428
448
  }
449
+ clearScrollDirectionCache() {
450
+ this._clearScrollDirectionDebounce.execute();
451
+ }
452
+ calcScrollDirection(v) {
453
+ while (this._scrollDirectionCache.length >= MAX_SCROLL_DIRECTION_POOL) {
454
+ this._scrollDirectionCache.shift();
455
+ }
456
+ this._scrollDirectionCache.push(v);
457
+ const dict = { [-1]: 0, [0]: 0, [1]: 0 };
458
+ for (let i = 0, l = this._scrollDirectionCache.length; i < l; i++) {
459
+ const dir = this._scrollDirectionCache[i];
460
+ dict[dir] += 1;
461
+ }
462
+ if (dict[-1] > dict[1]) {
463
+ return -1;
464
+ }
465
+ else if (dict[1] > dict[-1]) {
466
+ return 1;
467
+ }
468
+ return -1;
469
+ }
429
470
  bumpVersion() {
430
471
  this._version = this._version === Number.MAX_SAFE_INTEGER ? 0 : this._version + 1;
431
472
  }
@@ -459,7 +500,7 @@ class CacheMap extends EventEmitter {
459
500
  const TRACK_BOX_CHANGE_EVENT_NAME = 'change';
460
501
  /**
461
502
  * An object that performs tracking, calculations and caching.
462
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/trackBox.ts
503
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/trackBox.ts
463
504
  * @author Evgenii Grebennikov
464
505
  * @email djonnyx@gmail.com
465
506
  */
@@ -497,12 +538,12 @@ class TrackBox extends CacheMap {
497
538
  this._debounceChanges.execute(this._version);
498
539
  }
499
540
  getItemPosition(id, stickyMap, options) {
500
- const opt = { fromItemId: id, stickyMap, scrollDirection: undefined, ...options };
541
+ const opt = { fromItemId: id, stickyMap, ...options };
501
542
  const { scrollSize } = this.recalculateMetrics(opt);
502
543
  return scrollSize;
503
544
  }
504
545
  updateCollection(items, stickyMap, options) {
505
- const opt = { stickyMap, scrollDirection: undefined, ...options };
546
+ const opt = { stickyMap, ...options };
506
547
  this.cacheElements();
507
548
  const metrics = this.recalculateMetrics({
508
549
  ...opt,
@@ -511,14 +552,35 @@ class TrackBox extends CacheMap {
511
552
  const displayItems = this.generateDisplayCollection(items, stickyMap, metrics);
512
553
  return { displayItems, totalSize: metrics.totalSize, delta: metrics.delta };
513
554
  }
555
+ getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical) {
556
+ const sizeProperty = isVertical ? HEIGHT_PROP_NAME : WIDTH_PROP_NAME;
557
+ let offset = 0, num = 0;
558
+ for (let j = collection.length - 1; j >= i; j--) {
559
+ const item = collection[j];
560
+ let itemSize = 0;
561
+ if (map.has(item.id)) {
562
+ const bounds = map.get(item.id);
563
+ itemSize = bounds ? bounds[sizeProperty] : typicalItemSize;
564
+ }
565
+ else {
566
+ itemSize = typicalItemSize;
567
+ }
568
+ offset += itemSize;
569
+ num++;
570
+ if (offset > size) {
571
+ return { num: 0, offset };
572
+ }
573
+ }
574
+ return { num, offset };
575
+ }
514
576
  /**
515
577
  * Calculates list metrics
516
578
  */
517
579
  recalculateMetrics(options) {
518
- const { scrollDirection = 0, fromItemId, bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, stickyMap } = options;
519
- 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)
580
+ const { fromItemId, bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, stickyMap } = options;
581
+ 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)
520
582
  || (typeof fromItemId === 'string' && fromItemId > '-1');
521
- 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;
583
+ 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;
522
584
  if (dynamicSize) {
523
585
  let y = 0, stickyCollectionItem = undefined, stickyComponentSize = 0;
524
586
  for (let i = 0, l = collection.length; i < l; i++) {
@@ -531,21 +593,31 @@ class TrackBox extends CacheMap {
531
593
  else {
532
594
  componentSize = typicalItemSize;
533
595
  }
596
+ totalSize += componentSize;
534
597
  if (isFromId) {
535
598
  if (itemById === undefined) {
536
- leftItemsWeights.push(componentSize);
537
- leftHiddenItemsWeight += componentSize;
538
- itemsFromStartToScrollEnd = ii;
539
599
  if (stickyMap && stickyMap[collectionItem.id] > 0) {
540
600
  stickyComponentSize = componentSize;
541
601
  stickyCollectionItem = collectionItem;
542
602
  }
543
603
  if (collectionItem.id === fromItemId) {
544
- itemById = collectionItem;
545
- itemByIdPos = y;
546
604
  if (stickyCollectionItem && stickyMap && stickyMap[stickyCollectionItem.id] > 0) {
547
- itemByIdPos = itemByIdPos - stickyComponentSize;
605
+ const { num } = this.getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical);
606
+ if (num > 0) {
607
+ y -= size - componentSize;
608
+ }
609
+ else {
610
+ y -= stickyComponentSize;
611
+ leftHiddenItemsWeight -= stickyComponentSize;
612
+ }
548
613
  }
614
+ itemById = collectionItem;
615
+ itemByIdPos = y;
616
+ }
617
+ else {
618
+ leftItemsWeights.push(componentSize);
619
+ leftHiddenItemsWeight += componentSize;
620
+ itemsFromStartToScrollEnd = ii;
549
621
  }
550
622
  }
551
623
  }
@@ -573,7 +645,6 @@ class TrackBox extends CacheMap {
573
645
  }
574
646
  y += componentSize;
575
647
  }
576
- fullHeight = y;
577
648
  if (itemsFromStartToScrollEnd === -1) {
578
649
  itemsFromStartToScrollEnd = 0;
579
650
  }
@@ -599,12 +670,12 @@ class TrackBox extends CacheMap {
599
670
  rightItemsWeight = rightItemLength * typicalItemSize,
600
671
  leftHiddenItemsWeight = itemsFromStartToScrollEnd * typicalItemSize,
601
672
  totalItemsToDisplayEndWeight = itemsFromStartToDisplayEnd * typicalItemSize;
602
- actualScrollSize = scrollSize,
603
- fullHeight = totalLength * typicalItemSize;
673
+ actualScrollSize = scrollSize;
674
+ totalSize = totalLength * typicalItemSize;
604
675
  }
605
676
  startIndex = Math.min(itemsFromStartToScrollEnd - leftItemLength, totalLength > 0 ? totalLength - 1 : 0);
606
- const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta = fullHeight - this._previouseFullHeigh;
607
- if (scrollDirection === -1) {
677
+ const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta = totalSize - this._previouseFullHeigh;
678
+ if (this.scrollDirection === -1) {
608
679
  this._delta += delta;
609
680
  }
610
681
  const metrics = {
@@ -637,13 +708,17 @@ class TrackBox extends CacheMap {
637
708
  totalSize,
638
709
  typicalItemSize,
639
710
  };
640
- if (scrollDirection !== 0) {
641
- this._previouseFullHeigh = fullHeight;
642
- }
711
+ this._previouseFullHeigh = totalSize;
643
712
  return metrics;
644
713
  }
645
- clearDelta() {
714
+ clearDeltaDirection() {
715
+ this.clearScrollDirectionCache();
716
+ }
717
+ clearDelta(clearDirectionDetector = false) {
646
718
  this._delta = 0;
719
+ if (clearDirectionDetector) {
720
+ this.clearScrollDirectionCache();
721
+ }
647
722
  }
648
723
  generateDisplayCollection(items, stickyMap, metrics) {
649
724
  const {
@@ -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,21 +957,15 @@ 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
  this._$bounds.next(this._container?.nativeElement?.getBoundingClientRect() ?? null);
891
962
  };
892
- this._scrollDirection = 0;
893
963
  this._onScrollHandler = (e) => {
894
- this._$scrolledItemId.next(undefined);
895
964
  const container = this._container?.nativeElement;
896
965
  if (container) {
897
966
  const dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft), previouseScrollSize = this._$scrollSize.getValue();
898
967
  let actualScrollSize = scrollSize;
899
- this._scrollDirection = previouseScrollSize > scrollSize ? -1 : 1;
968
+ this._trackBox.deltaDirection = previouseScrollSize > scrollSize ? -1 : 1;
900
969
  if (dynamicSize && delta !== 0) {
901
970
  actualScrollSize = scrollSize + delta;
902
971
  const params = {
@@ -910,13 +979,15 @@ class NgVirtualListComponent extends DisposableComponent {
910
979
  }
911
980
  }
912
981
  this._$scrollSize.next(actualScrollSize);
913
- this.onScroll.emit(actualScrollSize);
982
+ this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
914
983
  }
915
984
  };
916
985
  this._scrollImmediatelyHandler = undefined;
917
- this._onScrollEndHandler = (e, fireEvent = true) => {
986
+ this._onScrollEndHandler = (e) => {
918
987
  const container = this._container;
919
988
  if (container) {
989
+ this._trackBox.clearDelta();
990
+ this._trackBox.clearDeltaDirection();
920
991
  const itemSize = this.itemSize, snapToItem = this.snapToItem, dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
921
992
  let actualScrollSize = scrollSize;
922
993
  if (dynamicSize && delta !== 0) {
@@ -926,7 +997,6 @@ class NgVirtualListComponent extends DisposableComponent {
926
997
  [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
927
998
  behavior: BEHAVIOR_INSTANT
928
999
  };
929
- this._trackBox.clearDelta();
930
1000
  this._$scrollSize.next(actualScrollSize);
931
1001
  container.nativeElement.scroll(params);
932
1002
  return;
@@ -944,9 +1014,7 @@ class NgVirtualListComponent extends DisposableComponent {
944
1014
  }
945
1015
  }
946
1016
  this._$scrollSize.next(actualScrollSize);
947
- if (fireEvent) {
948
- this.onScrollEnd.emit(actualScrollSize);
949
- }
1017
+ this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
950
1018
  }
951
1019
  };
952
1020
  this._$initialized = new BehaviorSubject(false);
@@ -964,7 +1032,7 @@ class NgVirtualListComponent extends DisposableComponent {
964
1032
  this._$initialized = new BehaviorSubject(false);
965
1033
  this.$initialized = this._$initialized.asObservable();
966
1034
  this._trackBox.displayComponents = this._displayComponents;
967
- 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();
1035
+ 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;
968
1036
  $isVertical.pipe(takeUntil(this._$unsubscribe), tap(v => {
969
1037
  this._isVertical = v;
970
1038
  const el = this._elementRef.nativeElement;
@@ -973,33 +1041,21 @@ class NgVirtualListComponent extends DisposableComponent {
973
1041
  $dynamicSize.pipe(takeUntil(this._$unsubscribe), tap(dynamicSize => {
974
1042
  this.listenCacheChangesIfNeed(dynamicSize);
975
1043
  })).subscribe();
976
- combineLatest([this.$initialized, $scrolledItemId, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
1044
+ combineLatest([this.$initialized, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
977
1045
  $itemsOffset, $snap, $isVertical, $dynamicSize, $cacheVersion,
978
- ]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), debounceTime(0), filter(([initialized]) => !!initialized), switchMap(([, scrolledItemId, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, cacheVersion,]) => {
1046
+ ]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), debounceTime(0), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, cacheVersion,]) => {
979
1047
  const { width, height } = bounds;
980
1048
  let actualScrollSize = scrollSize;
981
1049
  const opts = {
982
1050
  bounds: { width, height }, collection: items, dynamicSize, isVertical, itemSize,
983
- itemsOffset, scrollSize: scrollSize, snap, fromItemId: scrolledItemId,
1051
+ itemsOffset, scrollSize: scrollSize, snap,
984
1052
  };
985
- if (dynamicSize && scrolledItemId !== undefined) {
986
- const scrollSize = this._trackBox.getItemPosition(scrolledItemId, stickyMap, { ...opts, scrollSize: actualScrollSize });
987
- actualScrollSize = scrollSize;
988
- this._$scrollSize.next(actualScrollSize);
989
- }
990
- const scrollDirection = this._scrollDirection, { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
991
- ...opts, scrollSize: actualScrollSize, scrollDirection
1053
+ const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
1054
+ ...opts, scrollSize: actualScrollSize,
992
1055
  });
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]) => {
@@ -1213,6 +1269,13 @@ class NgVirtualListComponent extends DisposableComponent {
1213
1269
  * Behavior accepts the values ​​"auto", "instant" and "smooth".
1214
1270
  */
1215
1271
  scrollTo(id, behavior = BEHAVIOR_AUTO) {
1272
+ this.scrollToExecutor(id, behavior);
1273
+ }
1274
+ clearScrollToRepeatExecutionTimeout() {
1275
+ clearTimeout(this._scrollToRepeatExecutionTimeout);
1276
+ }
1277
+ scrollToExecutor(id, behavior, iteration = 0) {
1278
+ this.clearScrollToRepeatExecutionTimeout();
1216
1279
  const items = this.items;
1217
1280
  if (!items || !items.length) {
1218
1281
  return;
@@ -1224,24 +1287,35 @@ class NgVirtualListComponent extends DisposableComponent {
1224
1287
  container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
1225
1288
  container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
1226
1289
  }
1227
- const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, opts = {
1290
+ const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, isVertical = this._isVertical, opts = {
1228
1291
  bounds: { width, height }, collection: items, dynamicSize, isVertical: this._isVertical, itemSize,
1229
- itemsOffset: this.itemsOffset, scrollSize: this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
1292
+ itemsOffset: this.itemsOffset, scrollSize: isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
1230
1293
  snap: this.snap, fromItemId: id,
1231
- }, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
1232
- this._$scrolledItemId.next(id);
1294
+ }, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
1233
1295
  this._$scrollSize.next(scrollSize);
1234
1296
  if (container) {
1235
1297
  const handler = () => {
1236
1298
  if (container) {
1237
1299
  container.nativeElement.removeEventListener(SCROLL_END, handler);
1238
- clearTimeout(this._scrollToTimeout);
1239
- this._scrollToTimeout = setTimeout(() => {
1300
+ const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
1301
+ ...opts, scrollSize, fromItemId: id,
1302
+ });
1303
+ this.resetBoundsSize(isVertical, totalSize);
1304
+ this.createDisplayComponentsIfNeed(displayItems);
1305
+ this.tracking();
1306
+ const _scrollSize = this._trackBox.getItemPosition(id, stickyMap, { ...opts, scrollSize, fromItemId: id });
1307
+ if (scrollSize < _scrollSize && iteration < MAX_SCROLL_TO_ITERATIONS) {
1308
+ this.clearScrollToRepeatExecutionTimeout();
1309
+ this._scrollToRepeatExecutionTimeout = setTimeout(() => {
1310
+ this.scrollToExecutor(id, BEHAVIOR_INSTANT, iteration + 1);
1311
+ });
1312
+ }
1313
+ else {
1314
+ this._$scrollSize.next(scrollSize);
1315
+ this.onScroll.emit({ scrollSize, direction: this._trackBox.scrollDirection });
1240
1316
  container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
1241
1317
  container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
1242
- }, 100);
1243
- this.listenCacheChangesIfNeed(dynamicSize);
1244
- this.onScroll.emit(scrollSize);
1318
+ }
1245
1319
  }
1246
1320
  };
1247
1321
  container.nativeElement.addEventListener(SCROLL_END, handler);
@@ -1271,9 +1345,7 @@ class NgVirtualListComponent extends DisposableComponent {
1271
1345
  }
1272
1346
  ngOnDestroy() {
1273
1347
  super.ngOnDestroy();
1274
- if (this._scrollToTimeout) {
1275
- clearTimeout(this._scrollToTimeout);
1276
- }
1348
+ this.clearScrollToRepeatExecutionTimeout();
1277
1349
  if (this._trackBox) {
1278
1350
  this._trackBox.dispose();
1279
1351
  }