ng-virtual-list 17.0.0 → 17.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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -7
  3. package/esm2022/lib/components/ng-virtual-list-item.component.mjs +2 -2
  4. package/esm2022/lib/const/index.mjs +2 -1
  5. package/esm2022/lib/enums/direction.mjs +1 -1
  6. package/esm2022/lib/enums/directions.mjs +2 -2
  7. package/esm2022/lib/models/collection.model.mjs +1 -1
  8. package/esm2022/lib/models/index.mjs +1 -1
  9. package/esm2022/lib/models/item.model.mjs +1 -1
  10. package/esm2022/lib/models/render-collection.model.mjs +1 -1
  11. package/esm2022/lib/models/render-item-config.model.mjs +1 -1
  12. package/esm2022/lib/models/render-item.model.mjs +1 -1
  13. package/esm2022/lib/models/scroll-direction.model.mjs +2 -0
  14. package/esm2022/lib/models/scroll-event.model.mjs +2 -0
  15. package/esm2022/lib/models/sticky-map.model.mjs +1 -1
  16. package/esm2022/lib/ng-virtual-list.component.mjs +44 -47
  17. package/esm2022/lib/types/id.mjs +1 -1
  18. package/esm2022/lib/types/rect.mjs +1 -1
  19. package/esm2022/lib/types/size.mjs +1 -1
  20. package/esm2022/lib/utils/cacheMap.mjs +43 -2
  21. package/esm2022/lib/utils/debounce.mjs +2 -2
  22. package/esm2022/lib/utils/eventEmitter.mjs +2 -2
  23. package/esm2022/lib/utils/isDirection.mjs +2 -2
  24. package/esm2022/lib/utils/toggleClassName.mjs +2 -2
  25. package/esm2022/lib/utils/trackBox.mjs +56 -22
  26. package/esm2022/lib/utils/tracker.mjs +2 -2
  27. package/fesm2022/ng-virtual-list.mjs +146 -74
  28. package/fesm2022/ng-virtual-list.mjs.map +1 -1
  29. package/lib/components/ng-virtual-list-item.component.d.ts +1 -1
  30. package/lib/const/index.d.ts +1 -0
  31. package/lib/enums/direction.d.ts +1 -1
  32. package/lib/enums/directions.d.ts +1 -1
  33. package/lib/models/collection.model.d.ts +1 -1
  34. package/lib/models/index.d.ts +3 -1
  35. package/lib/models/item.model.d.ts +1 -1
  36. package/lib/models/render-collection.model.d.ts +1 -1
  37. package/lib/models/render-item-config.model.d.ts +1 -1
  38. package/lib/models/render-item.model.d.ts +1 -1
  39. package/lib/models/scroll-direction.model.d.ts +5 -0
  40. package/lib/models/scroll-event.model.d.ts +18 -0
  41. package/lib/models/sticky-map.model.d.ts +1 -1
  42. package/lib/ng-virtual-list.component.d.ts +7 -10
  43. package/lib/types/id.d.ts +1 -1
  44. package/lib/types/rect.d.ts +1 -1
  45. package/lib/types/size.d.ts +1 -1
  46. package/lib/utils/cacheMap.d.ts +11 -1
  47. package/lib/utils/debounce.d.ts +1 -1
  48. package/lib/utils/eventEmitter.d.ts +1 -1
  49. package/lib/utils/isDirection.d.ts +1 -1
  50. package/lib/utils/toggleClassName.d.ts +1 -1
  51. package/lib/utils/trackBox.d.ts +4 -4
  52. package/lib/utils/tracker.d.ts +1 -1
  53. package/package.json +1 -1
@@ -7,7 +7,7 @@ import { BehaviorSubject, filter, map, tap, combineLatest, distinctUntilChanged,
7
7
 
8
8
  /**
9
9
  * Axis of the arrangement of virtual list elements.
10
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/enums/directions.ts
10
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/enums/directions.ts
11
11
  * @author Evgenii Grebennikov
12
12
  * @email djonnyx@gmail.com
13
13
  */
@@ -32,6 +32,7 @@ const DEFAULT_DYNAMIC_SIZE = false;
32
32
  const TRACK_BY_PROPERTY_NAME = 'id';
33
33
  const DEFAULT_DIRECTION = Directions.VERTICAL;
34
34
  const DISPLAY_OBJECTS_LENGTH_MESUREMENT_ERROR = 1;
35
+ const MAX_SCROLL_TO_ITERATIONS = 5;
35
36
  // presets
36
37
  const BEHAVIOR_AUTO = 'auto';
37
38
  const BEHAVIOR_INSTANT = 'instant';
@@ -58,7 +59,7 @@ const CLASS_LIST_HORIZONTAL = 'horizontal';
58
59
 
59
60
  /**
60
61
  * Virtual list item component
61
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/components/ng-virtual-list-item.component.ts
62
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/components/ng-virtual-list-item.component.ts
62
63
  * @author Evgenii Grebennikov
63
64
  * @email djonnyx@gmail.com
64
65
  */
@@ -140,7 +141,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
140
141
  const HORIZONTAL_ALIASES = [Directions.HORIZONTAL, 'horizontal'], VERTICAL_ALIASES = [Directions.VERTICAL, 'vertical'];
141
142
  /**
142
143
  * Determines the axis membership of a virtual list
143
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/isDirection.ts
144
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/isDirection.ts
144
145
  * @author Evgenii Grebennikov
145
146
  * @email djonnyx@gmail.com
146
147
  */
@@ -153,7 +154,7 @@ const isDirection = (src, expected) => {
153
154
 
154
155
  /**
155
156
  * Simple debounce function.
156
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/debounce.ts
157
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/debounce.ts
157
158
  * @author Evgenii Grebennikov
158
159
  * @email djonnyx@gmail.com
159
160
  */
@@ -184,7 +185,7 @@ const debounce = (cb, debounceTime = 0) => {
184
185
 
185
186
  /**
186
187
  * Switch css classes
187
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/toggleClassName.ts
188
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/toggleClassName.ts
188
189
  * @author Evgenii Grebennikov
189
190
  * @email djonnyx@gmail.com
190
191
  */
@@ -199,7 +200,7 @@ const toggleClassName = (el, className, remove = false) => {
199
200
 
200
201
  /**
201
202
  * Tracks display items by property
202
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/tracker.ts
203
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/tracker.ts
203
204
  * @author Evgenii Grebennikov
204
205
  * @email djonnyx@gmail.com
205
206
  */
@@ -308,7 +309,7 @@ class Tracker {
308
309
 
309
310
  /**
310
311
  * Simple event emitter
311
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/eventEmitter.ts
312
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/eventEmitter.ts
312
313
  * @author Evgenii Grebennikov
313
314
  * @email djonnyx@gmail.com
314
315
  */
@@ -411,10 +412,11 @@ class EventEmitter {
411
412
  }
412
413
  }
413
414
 
415
+ const MAX_SCROLL_DIRECTION_POOL = 10, CLEAR_SCROLL_DIRECTION_TO = 0;
414
416
  /**
415
417
  * Cache map.
416
418
  * Emits a change event on each mutation.
417
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/cacheMap.ts
419
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/cacheMap.ts
418
420
  * @author Evgenii Grebennikov
419
421
  * @email djonnyx@gmail.com
420
422
  */
@@ -426,12 +428,51 @@ class CacheMap extends EventEmitter {
426
428
  get delta() {
427
429
  return this._delta;
428
430
  }
431
+ _deltaDirection = 1;
432
+ set deltaDirection(v) {
433
+ this._deltaDirection = v;
434
+ this._scrollDirection = this.calcScrollDirection(v);
435
+ }
436
+ get deltaDirection() {
437
+ return this._deltaDirection;
438
+ }
439
+ _scrollDirectionCache = [];
440
+ _scrollDirection = 1;
441
+ get scrollDirection() {
442
+ return this._scrollDirection;
443
+ }
429
444
  get version() {
430
445
  return this._version;
431
446
  }
447
+ _clearScrollDirectionDebounce = debounce(() => {
448
+ while (this._scrollDirectionCache.length > CLEAR_SCROLL_DIRECTION_TO) {
449
+ this._scrollDirectionCache.shift();
450
+ }
451
+ }, 10);
432
452
  constructor() {
433
453
  super();
434
454
  }
455
+ clearScrollDirectionCache() {
456
+ this._clearScrollDirectionDebounce.execute();
457
+ }
458
+ calcScrollDirection(v) {
459
+ while (this._scrollDirectionCache.length >= MAX_SCROLL_DIRECTION_POOL) {
460
+ this._scrollDirectionCache.shift();
461
+ }
462
+ this._scrollDirectionCache.push(v);
463
+ const dict = { [-1]: 0, [0]: 0, [1]: 0 };
464
+ for (let i = 0, l = this._scrollDirectionCache.length; i < l; i++) {
465
+ const dir = this._scrollDirectionCache[i];
466
+ dict[dir] += 1;
467
+ }
468
+ if (dict[-1] > dict[1]) {
469
+ return -1;
470
+ }
471
+ else if (dict[1] > dict[-1]) {
472
+ return 1;
473
+ }
474
+ return -1;
475
+ }
435
476
  bumpVersion() {
436
477
  this._version = this._version === Number.MAX_SAFE_INTEGER ? 0 : this._version + 1;
437
478
  }
@@ -465,7 +506,7 @@ class CacheMap extends EventEmitter {
465
506
  const TRACK_BOX_CHANGE_EVENT_NAME = 'change';
466
507
  /**
467
508
  * An object that performs tracking, calculations and caching.
468
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/utils/trackBox.ts
509
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/trackBox.ts
469
510
  * @author Evgenii Grebennikov
470
511
  * @email djonnyx@gmail.com
471
512
  */
@@ -506,12 +547,12 @@ class TrackBox extends CacheMap {
506
547
  this._debounceChanges.execute(this._version);
507
548
  }
508
549
  getItemPosition(id, stickyMap, options) {
509
- const opt = { fromItemId: id, stickyMap, scrollDirection: undefined, ...options };
550
+ const opt = { fromItemId: id, stickyMap, ...options };
510
551
  const { scrollSize } = this.recalculateMetrics(opt);
511
552
  return scrollSize;
512
553
  }
513
554
  updateCollection(items, stickyMap, options) {
514
- const opt = { stickyMap, scrollDirection: undefined, ...options };
555
+ const opt = { stickyMap, ...options };
515
556
  this.cacheElements();
516
557
  const metrics = this.recalculateMetrics({
517
558
  ...opt,
@@ -520,14 +561,35 @@ class TrackBox extends CacheMap {
520
561
  const displayItems = this.generateDisplayCollection(items, stickyMap, metrics);
521
562
  return { displayItems, totalSize: metrics.totalSize, delta: metrics.delta };
522
563
  }
564
+ getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical) {
565
+ const sizeProperty = isVertical ? HEIGHT_PROP_NAME : WIDTH_PROP_NAME;
566
+ let offset = 0, num = 0;
567
+ for (let j = collection.length - 1; j >= i; j--) {
568
+ const item = collection[j];
569
+ let itemSize = 0;
570
+ if (map.has(item.id)) {
571
+ const bounds = map.get(item.id);
572
+ itemSize = bounds ? bounds[sizeProperty] : typicalItemSize;
573
+ }
574
+ else {
575
+ itemSize = typicalItemSize;
576
+ }
577
+ offset += itemSize;
578
+ num++;
579
+ if (offset > size) {
580
+ return { num: 0, offset };
581
+ }
582
+ }
583
+ return { num, offset };
584
+ }
523
585
  /**
524
586
  * Calculates list metrics
525
587
  */
526
588
  recalculateMetrics(options) {
527
- const { scrollDirection = 0, fromItemId, bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, stickyMap } = options;
528
- 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)
589
+ const { fromItemId, bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, stickyMap } = options;
590
+ 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)
529
591
  || (typeof fromItemId === 'string' && fromItemId > '-1');
530
- 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;
592
+ 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;
531
593
  if (dynamicSize) {
532
594
  let y = 0, stickyCollectionItem = undefined, stickyComponentSize = 0;
533
595
  for (let i = 0, l = collection.length; i < l; i++) {
@@ -540,21 +602,31 @@ class TrackBox extends CacheMap {
540
602
  else {
541
603
  componentSize = typicalItemSize;
542
604
  }
605
+ totalSize += componentSize;
543
606
  if (isFromId) {
544
607
  if (itemById === undefined) {
545
- leftItemsWeights.push(componentSize);
546
- leftHiddenItemsWeight += componentSize;
547
- itemsFromStartToScrollEnd = ii;
548
608
  if (stickyMap && stickyMap[collectionItem.id] > 0) {
549
609
  stickyComponentSize = componentSize;
550
610
  stickyCollectionItem = collectionItem;
551
611
  }
552
612
  if (collectionItem.id === fromItemId) {
553
- itemById = collectionItem;
554
- itemByIdPos = y;
555
613
  if (stickyCollectionItem && stickyMap && stickyMap[stickyCollectionItem.id] > 0) {
556
- itemByIdPos = itemByIdPos - stickyComponentSize;
614
+ const { num } = this.getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical);
615
+ if (num > 0) {
616
+ y -= size - componentSize;
617
+ }
618
+ else {
619
+ y -= stickyComponentSize;
620
+ leftHiddenItemsWeight -= stickyComponentSize;
621
+ }
557
622
  }
623
+ itemById = collectionItem;
624
+ itemByIdPos = y;
625
+ }
626
+ else {
627
+ leftItemsWeights.push(componentSize);
628
+ leftHiddenItemsWeight += componentSize;
629
+ itemsFromStartToScrollEnd = ii;
558
630
  }
559
631
  }
560
632
  }
@@ -582,7 +654,6 @@ class TrackBox extends CacheMap {
582
654
  }
583
655
  y += componentSize;
584
656
  }
585
- fullHeight = y;
586
657
  if (itemsFromStartToScrollEnd === -1) {
587
658
  itemsFromStartToScrollEnd = 0;
588
659
  }
@@ -608,12 +679,12 @@ class TrackBox extends CacheMap {
608
679
  rightItemsWeight = rightItemLength * typicalItemSize,
609
680
  leftHiddenItemsWeight = itemsFromStartToScrollEnd * typicalItemSize,
610
681
  totalItemsToDisplayEndWeight = itemsFromStartToDisplayEnd * typicalItemSize;
611
- actualScrollSize = scrollSize,
612
- fullHeight = totalLength * typicalItemSize;
682
+ actualScrollSize = scrollSize;
683
+ totalSize = totalLength * typicalItemSize;
613
684
  }
614
685
  startIndex = Math.min(itemsFromStartToScrollEnd - leftItemLength, totalLength > 0 ? totalLength - 1 : 0);
615
- const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta = fullHeight - this._previouseFullHeigh;
616
- if (scrollDirection === -1) {
686
+ const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta = totalSize - this._previouseFullHeigh;
687
+ if (this.scrollDirection === -1) {
617
688
  this._delta += delta;
618
689
  }
619
690
  const metrics = {
@@ -646,13 +717,17 @@ class TrackBox extends CacheMap {
646
717
  totalSize,
647
718
  typicalItemSize,
648
719
  };
649
- if (scrollDirection !== 0) {
650
- this._previouseFullHeigh = fullHeight;
651
- }
720
+ this._previouseFullHeigh = totalSize;
652
721
  return metrics;
653
722
  }
654
- clearDelta() {
723
+ clearDeltaDirection() {
724
+ this.clearScrollDirectionCache();
725
+ }
726
+ clearDelta(clearDirectionDetector = false) {
655
727
  this._delta = 0;
728
+ if (clearDirectionDetector) {
729
+ this.clearScrollDirectionCache();
730
+ }
656
731
  }
657
732
  generateDisplayCollection(items, stickyMap, metrics) {
658
733
  const {
@@ -817,7 +892,7 @@ class TrackBox extends CacheMap {
817
892
  * Virtual list component.
818
893
  * Maximum performance for extremely large lists.
819
894
  * It is based on algorithms for virtualization of screen objects.
820
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/main/projects/ng-virtual-list/src/lib/ng-virtual-list.component.ts
895
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/ng-virtual-list.component.ts
821
896
  * @author Evgenii Grebennikov
822
897
  * @email djonnyx@gmail.com
823
898
  */
@@ -887,27 +962,20 @@ class NgVirtualListComponent {
887
962
  * Number of elements outside the scope of visibility. Default value is 2.
888
963
  */
889
964
  itemsOffset = input(DEFAULT_ITEMS_OFFSET);
890
- _scrollToTimeout;
891
965
  _isVertical = this.getIsVertical();
892
966
  _displayComponents = [];
893
967
  _bounds = signal(null);
894
968
  _scrollSize = signal(0);
895
969
  _resizeObserver = null;
896
- /**
897
- * only dynamic
898
- */
899
- _scrolledItemId = signal(undefined);
900
970
  _onResizeHandler = () => {
901
971
  this._bounds.set(this._container()?.nativeElement?.getBoundingClientRect() ?? null);
902
972
  };
903
- _scrollDirection = 0;
904
973
  _onScrollHandler = (e) => {
905
- this._scrolledItemId.set(undefined);
906
974
  const container = this._container()?.nativeElement;
907
975
  if (container) {
908
976
  const dynamicSize = this.dynamicSize(), delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft), previouseScrollSize = this._scrollSize();
909
977
  let actualScrollSize = scrollSize;
910
- this._scrollDirection = previouseScrollSize > scrollSize ? -1 : 1;
978
+ this._trackBox.deltaDirection = previouseScrollSize > scrollSize ? -1 : 1;
911
979
  if (dynamicSize && delta !== 0) {
912
980
  actualScrollSize = scrollSize + delta;
913
981
  const params = {
@@ -921,7 +989,7 @@ class NgVirtualListComponent {
921
989
  }
922
990
  }
923
991
  this._scrollSize.set(actualScrollSize);
924
- this.onScroll.emit(actualScrollSize);
992
+ this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
925
993
  }
926
994
  };
927
995
  scrollImmediately(container, params) {
@@ -948,9 +1016,11 @@ class NgVirtualListComponent {
948
1016
  container.nativeElement.removeEventListener(SCROLL_END, this._scrollImmediatelyHandler);
949
1017
  }
950
1018
  }
951
- _onScrollEndHandler = (e, fireEvent = true) => {
1019
+ _onScrollEndHandler = (e) => {
952
1020
  const container = this._container();
953
1021
  if (container) {
1022
+ this._trackBox.clearDelta();
1023
+ this._trackBox.clearDeltaDirection();
954
1024
  const itemSize = this.itemSize(), snapToItem = this.snapToItem(), dynamicSize = this.dynamicSize(), delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
955
1025
  let actualScrollSize = scrollSize;
956
1026
  if (dynamicSize && delta !== 0) {
@@ -960,7 +1030,6 @@ class NgVirtualListComponent {
960
1030
  [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
961
1031
  behavior: BEHAVIOR_INSTANT
962
1032
  };
963
- this._trackBox.clearDelta();
964
1033
  this._scrollSize.set(actualScrollSize);
965
1034
  container.nativeElement.scroll(params);
966
1035
  return;
@@ -978,9 +1047,7 @@ class NgVirtualListComponent {
978
1047
  }
979
1048
  }
980
1049
  this._scrollSize.set(actualScrollSize);
981
- if (fireEvent) {
982
- this.onScrollEnd.emit(actualScrollSize);
983
- }
1050
+ this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
984
1051
  }
985
1052
  };
986
1053
  _elementRef = inject((ElementRef));
@@ -1002,7 +1069,7 @@ class NgVirtualListComponent {
1002
1069
  this._initialized = signal(false);
1003
1070
  this.$initialized = toObservable(this._initialized);
1004
1071
  this._trackBox.displayComponents = this._displayComponents;
1005
- const $bounds = toObservable(this._bounds).pipe(filter(b => !!b)), $items = toObservable(this.items).pipe(map(i => !i ? [] : i)), $scrollSize = toObservable(this._scrollSize), $itemSize = toObservable(this.itemSize).pipe(map(v => v <= 0 ? DEFAULT_ITEM_SIZE : v)), $itemsOffset = toObservable(this.itemsOffset).pipe(map(v => v < 0 ? DEFAULT_ITEMS_OFFSET : v)), $stickyMap = toObservable(this.stickyMap).pipe(map(v => !v ? {} : v)), $snap = toObservable(this.snap), $isVertical = toObservable(this.direction).pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $dynamicSize = toObservable(this.dynamicSize), $cacheVersion = this.$cacheVersion, $scrolledItemId = toObservable(this._scrolledItemId);
1072
+ const $bounds = toObservable(this._bounds).pipe(filter(b => !!b)), $items = toObservable(this.items).pipe(map(i => !i ? [] : i)), $scrollSize = toObservable(this._scrollSize), $itemSize = toObservable(this.itemSize).pipe(map(v => v <= 0 ? DEFAULT_ITEM_SIZE : v)), $itemsOffset = toObservable(this.itemsOffset).pipe(map(v => v < 0 ? DEFAULT_ITEMS_OFFSET : v)), $stickyMap = toObservable(this.stickyMap).pipe(map(v => !v ? {} : v)), $snap = toObservable(this.snap), $isVertical = toObservable(this.direction).pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $dynamicSize = toObservable(this.dynamicSize), $cacheVersion = this.$cacheVersion;
1006
1073
  $isVertical.pipe(takeUntilDestroyed(), tap(v => {
1007
1074
  this._isVertical = v;
1008
1075
  const el = this._elementRef.nativeElement;
@@ -1011,33 +1078,21 @@ class NgVirtualListComponent {
1011
1078
  $dynamicSize.pipe(takeUntilDestroyed(), tap(dynamicSize => {
1012
1079
  this.listenCacheChangesIfNeed(dynamicSize);
1013
1080
  })).subscribe();
1014
- combineLatest([this.$initialized, $scrolledItemId, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
1081
+ combineLatest([this.$initialized, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
1015
1082
  $itemsOffset, $snap, $isVertical, $dynamicSize, $cacheVersion,
1016
- ]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), switchMap(([, scrolledItemId, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, cacheVersion,]) => {
1083
+ ]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, cacheVersion,]) => {
1017
1084
  const { width, height } = bounds;
1018
1085
  let actualScrollSize = scrollSize;
1019
1086
  const opts = {
1020
1087
  bounds: { width, height }, collection: items, dynamicSize, isVertical, itemSize,
1021
- itemsOffset, scrollSize: scrollSize, snap, fromItemId: scrolledItemId,
1088
+ itemsOffset, scrollSize: scrollSize, snap,
1022
1089
  };
1023
- if (dynamicSize && scrolledItemId !== undefined) {
1024
- const scrollSize = this._trackBox.getItemPosition(scrolledItemId, stickyMap, { ...opts, scrollSize: actualScrollSize });
1025
- actualScrollSize = scrollSize;
1026
- this._scrollSize.set(actualScrollSize);
1027
- }
1028
- const scrollDirection = this._scrollDirection, { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
1029
- ...opts, scrollSize: actualScrollSize, scrollDirection
1090
+ const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
1091
+ ...opts, scrollSize: actualScrollSize,
1030
1092
  });
1031
1093
  this.resetBoundsSize(isVertical, totalSize);
1032
1094
  this.createDisplayComponentsIfNeed(displayItems);
1033
1095
  this.tracking();
1034
- if (dynamicSize && scrolledItemId !== undefined) {
1035
- const container = this._container();
1036
- if (container) {
1037
- const params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior: BEHAVIOR_AUTO };
1038
- this.scrollImmediately(container, params);
1039
- }
1040
- }
1041
1096
  return of(displayItems);
1042
1097
  })).subscribe();
1043
1098
  combineLatest([this.$initialized, toObservable(this.itemRenderer)]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), tap(([, itemRenderer]) => {
@@ -1113,6 +1168,14 @@ class NgVirtualListComponent {
1113
1168
  * Behavior accepts the values ​​"auto", "instant" and "smooth".
1114
1169
  */
1115
1170
  scrollTo(id, behavior = BEHAVIOR_AUTO) {
1171
+ this.scrollToExecutor(id, behavior);
1172
+ }
1173
+ _scrollToRepeatExecutionTimeout;
1174
+ clearScrollToRepeatExecutionTimeout() {
1175
+ clearTimeout(this._scrollToRepeatExecutionTimeout);
1176
+ }
1177
+ scrollToExecutor(id, behavior, iteration = 0) {
1178
+ this.clearScrollToRepeatExecutionTimeout();
1116
1179
  const items = this.items();
1117
1180
  if (!items || !items.length) {
1118
1181
  return;
@@ -1124,24 +1187,35 @@ class NgVirtualListComponent {
1124
1187
  container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
1125
1188
  container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
1126
1189
  }
1127
- const { width, height } = this._bounds() || { width: 0, height: 0 }, stickyMap = this.stickyMap(), items = this.items(), opts = {
1190
+ const { width, height } = this._bounds() || { width: 0, height: 0 }, stickyMap = this.stickyMap(), items = this.items(), isVertical = this._isVertical, opts = {
1128
1191
  bounds: { width, height }, collection: items, dynamicSize, isVertical: this._isVertical, itemSize,
1129
- itemsOffset: this.itemsOffset(), scrollSize: this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
1192
+ itemsOffset: this.itemsOffset(), scrollSize: isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
1130
1193
  snap: this.snap(), fromItemId: id,
1131
- }, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
1132
- this._scrolledItemId.set(id);
1194
+ }, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
1133
1195
  this._scrollSize.set(scrollSize);
1134
1196
  if (container) {
1135
1197
  const handler = () => {
1136
1198
  if (container) {
1137
1199
  container.nativeElement.removeEventListener(SCROLL_END, handler);
1138
- clearTimeout(this._scrollToTimeout);
1139
- this._scrollToTimeout = setTimeout(() => {
1200
+ const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
1201
+ ...opts, scrollSize, fromItemId: id,
1202
+ });
1203
+ this.resetBoundsSize(isVertical, totalSize);
1204
+ this.createDisplayComponentsIfNeed(displayItems);
1205
+ this.tracking();
1206
+ const _scrollSize = this._trackBox.getItemPosition(id, stickyMap, { ...opts, scrollSize, fromItemId: id });
1207
+ if (scrollSize < _scrollSize && iteration < MAX_SCROLL_TO_ITERATIONS) {
1208
+ this.clearScrollToRepeatExecutionTimeout();
1209
+ this._scrollToRepeatExecutionTimeout = setTimeout(() => {
1210
+ this.scrollToExecutor(id, BEHAVIOR_INSTANT, iteration + 1);
1211
+ });
1212
+ }
1213
+ else {
1214
+ this._scrollSize.set(scrollSize);
1215
+ this.onScroll.emit({ scrollSize, direction: this._trackBox.scrollDirection });
1140
1216
  container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
1141
1217
  container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
1142
- }, 100);
1143
- this.listenCacheChangesIfNeed(dynamicSize);
1144
- this.onScroll.emit(scrollSize);
1218
+ }
1145
1219
  }
1146
1220
  };
1147
1221
  container.nativeElement.addEventListener(SCROLL_END, handler);
@@ -1170,9 +1244,7 @@ class NgVirtualListComponent {
1170
1244
  }
1171
1245
  }
1172
1246
  ngOnDestroy() {
1173
- if (this._scrollToTimeout) {
1174
- clearTimeout(this._scrollToTimeout);
1175
- }
1247
+ this.clearScrollToRepeatExecutionTimeout();
1176
1248
  if (this._trackBox) {
1177
1249
  this._trackBox.dispose();
1178
1250
  }