ng-virtual-list 16.0.13 → 16.0.15

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.
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { Component, ChangeDetectionStrategy, EventEmitter as EventEmitter$1, ViewContainerRef, ElementRef, ViewEncapsulation, ViewChild, Output, Input, NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
- import { BehaviorSubject, filter, map, tap, combineLatest, distinctUntilChanged, debounceTime, switchMap, of } from 'rxjs';
5
+ import { BehaviorSubject, tap, filter, map, combineLatest, distinctUntilChanged, debounceTime, switchMap, of } from 'rxjs';
6
6
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
7
 
8
8
  /**
@@ -60,7 +60,7 @@ const CLASS_LIST_HORIZONTAL = 'horizontal';
60
60
 
61
61
  /**
62
62
  * Virtual list item component
63
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/16.x/projects/ng-virtual-list/src/lib/components/ng-virtual-list-item.component.ts
63
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/15.x/projects/ng-virtual-list/src/lib/components/ng-virtual-list-item.component.ts
64
64
  * @author Evgenii Grebennikov
65
65
  * @email djonnyx@gmail.com
66
66
  */
@@ -94,6 +94,9 @@ class NgVirtualListItemComponent {
94
94
  }
95
95
  this._cdr.detectChanges();
96
96
  }
97
+ get item() {
98
+ return this.data;
99
+ }
97
100
  get itemId() {
98
101
  return this.data?.id;
99
102
  }
@@ -105,6 +108,9 @@ class NgVirtualListItemComponent {
105
108
  this.itemRenderer = v;
106
109
  this._cdr.markForCheck();
107
110
  }
111
+ get element() {
112
+ return this._elementRef.nativeElement;
113
+ }
108
114
  constructor(_cdr, _elementRef) {
109
115
  this._cdr = _cdr;
110
116
  this._elementRef = _elementRef;
@@ -201,7 +207,7 @@ const toggleClassName = (el, className, remove = false) => {
201
207
 
202
208
  /**
203
209
  * Tracks display items by property
204
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/16.x/projects/ng-virtual-list/src/lib/utils/tracker.ts
210
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/15.x/projects/ng-virtual-list/src/lib/utils/tracker.ts
205
211
  * @author Evgenii Grebennikov
206
212
  * @email djonnyx@gmail.com
207
213
  */
@@ -227,6 +233,9 @@ class Tracker {
227
233
  return this._trackMap;
228
234
  }
229
235
  _trackingPropertyName;
236
+ set trackingPropertyName(v) {
237
+ this._trackingPropertyName = v;
238
+ }
230
239
  constructor(trackingPropertyName) {
231
240
  this._trackingPropertyName = trackingPropertyName;
232
241
  }
@@ -244,17 +253,17 @@ class Tracker {
244
253
  const diId = this._trackMap[itemTrackingProperty];
245
254
  if (this._trackMap.hasOwnProperty(itemTrackingProperty)) {
246
255
  const lastIndex = this._displayObjectIndexMapById[diId], el = components[lastIndex];
247
- this._checkComponentProperty(el?.instance);
248
- const elId = el?.instance?.[itemTrackingProperty];
256
+ const elId = el?.instance?.id;
249
257
  if (el && elId === diId) {
250
258
  const indexByUntrackedItems = untrackedItems.findIndex(v => {
251
- this._checkComponentProperty(v.instance);
252
- return v.instance[itemTrackingProperty] === elId;
259
+ return v.instance.id === elId;
253
260
  });
254
261
  if (indexByUntrackedItems > -1) {
255
- el.instance.item = item;
256
- if (afterComponentSetup !== undefined) {
257
- afterComponentSetup(el.instance, item);
262
+ if (el.instance.item !== item) {
263
+ el.instance.item = item;
264
+ if (afterComponentSetup !== undefined) {
265
+ afterComponentSetup(el.instance, item);
266
+ }
258
267
  }
259
268
  untrackedItems.splice(indexByUntrackedItems, 1);
260
269
  continue;
@@ -266,13 +275,14 @@ class Tracker {
266
275
  if (untrackedItems.length > 0) {
267
276
  const el = untrackedItems.shift(), item = items[i];
268
277
  if (el) {
269
- el.instance.item = item;
270
- if (this._trackMap) {
271
- this._checkComponentProperty(el.instance);
272
- this._trackMap[itemTrackingProperty] = el.instance[itemTrackingProperty];
273
- }
274
- if (afterComponentSetup !== undefined) {
275
- afterComponentSetup(el.instance, item);
278
+ if (el.instance.item !== item) {
279
+ el.instance.item = item;
280
+ if (this._trackMap) {
281
+ this._trackMap[itemTrackingProperty] = el.instance.id;
282
+ }
283
+ if (afterComponentSetup !== undefined) {
284
+ afterComponentSetup(el.instance, item);
285
+ }
276
286
  }
277
287
  }
278
288
  }
@@ -286,23 +296,10 @@ class Tracker {
286
296
  return;
287
297
  }
288
298
  const propertyIdName = this._trackingPropertyName;
289
- this._checkComponentProperty(component);
290
299
  if (this._trackMap && component[propertyIdName] !== undefined) {
291
300
  delete this._trackMap[propertyIdName];
292
301
  }
293
302
  }
294
- _checkComponentProperty(component) {
295
- if (!component) {
296
- return;
297
- }
298
- const propertyIdName = this._trackingPropertyName;
299
- try {
300
- component[propertyIdName];
301
- }
302
- catch (err) {
303
- throw Error(`Property ${propertyIdName} does not exist.`);
304
- }
305
- }
306
303
  dispose() {
307
304
  this._trackMap = null;
308
305
  }
@@ -417,7 +414,7 @@ const MAX_SCROLL_DIRECTION_POOL = 8, CLEAR_SCROLL_DIRECTION_TO = 0;
417
414
  /**
418
415
  * Cache map.
419
416
  * Emits a change event on each mutation.
420
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/16.x/projects/ng-virtual-list/src/lib/utils/cacheMap.ts
417
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/15.x/projects/ng-virtual-list/src/lib/utils/cacheMap.ts
421
418
  * @author Evgenii Grebennikov
422
419
  * @email djonnyx@gmail.com
423
420
  */
@@ -481,7 +478,11 @@ class CacheMap extends EventEmitter {
481
478
  this.dispatch('change', this.version);
482
479
  }
483
480
  set(id, bounds) {
484
- if (this._map.has(id) && JSON.stringify(this._map.get(id)) === JSON.stringify(bounds)) {
481
+ if (this._map.has(id)) {
482
+ const b = this._map.get(id), bb = bounds;
483
+ if (b.width === bb.width && b.height === bb.height) {
484
+ return this._map;
485
+ }
485
486
  return this._map;
486
487
  }
487
488
  const v = this._map.set(id, bounds);
@@ -508,56 +509,6 @@ class CacheMap extends EventEmitter {
508
509
  }
509
510
  }
510
511
 
511
- /**
512
- * Returns the removed or updated elements of a collection.
513
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/16.x/projects/ng-virtual-list/src/lib/utils/collection.ts
514
- * @author Evgenii Grebennikov
515
- * @email djonnyx@gmail.com
516
- */
517
- const getCollectionRemovedOrUpdatedItems = (previousCollection, currentCollection) => {
518
- const result = { deleted: new Array(), updated: new Array(), added: new Array(), notChanged: new Array() };
519
- if (!currentCollection || currentCollection.length === 0) {
520
- return { deleted: (previousCollection ? [...previousCollection] : []), updated: [], added: [], notChanged: [] };
521
- }
522
- if (!previousCollection || previousCollection.length === 0) {
523
- return { deleted: [], updated: [], added: (currentCollection ? [...currentCollection] : []), notChanged: [] };
524
- }
525
- const collectionDict = {};
526
- for (let i = 0, l = currentCollection.length; i < l; i++) {
527
- const item = currentCollection[i];
528
- if (item) {
529
- collectionDict[item.id] = item;
530
- }
531
- }
532
- const notChangedMap = {}, deletedMap = {}, updatedMap = {};
533
- for (let i = 0, l = previousCollection.length; i < l; i++) {
534
- const item = previousCollection[i], id = item.id;
535
- if (item) {
536
- if (collectionDict.hasOwnProperty(id)) {
537
- if (item === collectionDict[id]) {
538
- result.notChanged.push(item);
539
- notChangedMap[item.id] = item;
540
- continue;
541
- }
542
- else {
543
- result.updated.push(item);
544
- updatedMap[item.id] = item;
545
- continue;
546
- }
547
- }
548
- result.deleted.push(item);
549
- deletedMap[item.id] = item;
550
- }
551
- }
552
- for (let i = 0, l = currentCollection.length; i < l; i++) {
553
- const item = currentCollection[i], id = item.id;
554
- if (item && !deletedMap.hasOwnProperty(id) && !updatedMap.hasOwnProperty(id) && !notChangedMap.hasOwnProperty(id)) {
555
- result.added.push(item);
556
- }
557
- }
558
- return result;
559
- };
560
-
561
512
  const TRACK_BOX_CHANGE_EVENT_NAME = 'change';
562
513
  var ItemDisplayMethods;
563
514
  (function (ItemDisplayMethods) {
@@ -568,7 +519,7 @@ var ItemDisplayMethods;
568
519
  })(ItemDisplayMethods || (ItemDisplayMethods = {}));
569
520
  /**
570
521
  * An object that performs tracking, calculations and caching.
571
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/16.x/projects/ng-virtual-list/src/lib/utils/trackBox.ts
522
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/15.x/projects/ng-virtual-list/src/lib/utils/trackBox.ts
572
523
  * @author Evgenii Grebennikov
573
524
  * @email djonnyx@gmail.com
574
525
  */
@@ -588,13 +539,22 @@ class TrackBox extends CacheMap {
588
539
  }
589
540
  this._displayComponents = v;
590
541
  }
542
+ /**
543
+ * Set the trackBy property
544
+ */
545
+ set trackingPropertyName(v) {
546
+ this._tracker.trackingPropertyName = v;
547
+ }
591
548
  constructor(trackingPropertyName) {
592
549
  super();
593
550
  this._tracker = new Tracker(trackingPropertyName);
594
551
  }
595
552
  set(id, bounds) {
596
- if (this._map.has(id) && JSON.stringify(this._map.get(id)) === JSON.stringify(bounds)) {
597
- return this._map;
553
+ if (this._map.has(id)) {
554
+ const b = this._map.get(id);
555
+ if (b?.width === bounds.width && b.height === bounds.height) {
556
+ return this._map;
557
+ }
598
558
  }
599
559
  const v = this._map.set(id, bounds);
600
560
  this.bumpVersion();
@@ -617,31 +577,69 @@ class TrackBox extends CacheMap {
617
577
  console.warn('Attention! The collection must be immutable.');
618
578
  return;
619
579
  }
620
- const { deleted, updated, added } = getCollectionRemovedOrUpdatedItems(this._previousCollection, currentCollection);
621
- this.clearCache(deleted, updated, added, itemSize);
580
+ this.updateCache(this._previousCollection, currentCollection, itemSize);
622
581
  this._previousCollection = currentCollection;
623
582
  }
624
583
  /**
625
- * Clears the cache of items from the list
584
+ * Update the cache of items from the list
626
585
  */
627
- clearCache(deleted, updated, added, itemSize) {
628
- if (deleted) {
629
- for (let i = 0, l = deleted.length; i < l; i++) {
630
- const item = deleted[i], id = item.id;
631
- if (this._map.has(id)) {
632
- this._map.delete(id);
586
+ updateCache(previousCollection, currentCollection, itemSize) {
587
+ if (!currentCollection || currentCollection.length === 0) {
588
+ if (previousCollection) {
589
+ // deleted
590
+ for (let i = 0, l = previousCollection.length; i < l; i++) {
591
+ const item = previousCollection[i], id = item.id;
592
+ if (this._map.has(id)) {
593
+ this._map.delete(id);
594
+ }
633
595
  }
634
596
  }
597
+ return;
635
598
  }
636
- if (updated) {
637
- for (let i = 0, l = updated.length; i < l; i++) {
638
- const item = updated[i], id = item.id;
639
- this._map.set(id, { ...(this._map.get(id) || { x: 0, y: 0, width: itemSize, height: itemSize }), method: ItemDisplayMethods.UPDATE });
599
+ if (!previousCollection || previousCollection.length === 0) {
600
+ if (currentCollection) {
601
+ // added
602
+ for (let i = 0, l = currentCollection.length; i < l; i++) {
603
+ const item = currentCollection[i], id = item.id;
604
+ this._map.set(id, { x: 0, y: 0, width: itemSize, height: itemSize, method: ItemDisplayMethods.CREATE });
605
+ }
606
+ }
607
+ return;
608
+ }
609
+ const collectionDict = {};
610
+ for (let i = 0, l = currentCollection.length; i < l; i++) {
611
+ const item = currentCollection[i];
612
+ if (item) {
613
+ collectionDict[item.id] = item;
640
614
  }
641
615
  }
642
- if (added) {
643
- for (let i = 0, l = added.length; i < l; i++) {
644
- const item = added[i], id = item.id;
616
+ const notChangedMap = {}, deletedMap = {}, updatedMap = {};
617
+ for (let i = 0, l = previousCollection.length; i < l; i++) {
618
+ const item = previousCollection[i], id = item.id;
619
+ if (item) {
620
+ if (collectionDict.hasOwnProperty(id)) {
621
+ if (item === collectionDict[id]) {
622
+ // not changed
623
+ notChangedMap[item.id] = item;
624
+ this._map.set(id, { ...(this._map.get(id) || { x: 0, y: 0, width: itemSize, height: itemSize }), method: ItemDisplayMethods.NOT_CHANGED });
625
+ continue;
626
+ }
627
+ else {
628
+ // updated
629
+ updatedMap[item.id] = item;
630
+ this._map.set(id, { ...(this._map.get(id) || { x: 0, y: 0, width: itemSize, height: itemSize }), method: ItemDisplayMethods.UPDATE });
631
+ continue;
632
+ }
633
+ }
634
+ // deleted
635
+ deletedMap[item.id] = item;
636
+ this._map.delete(id);
637
+ }
638
+ }
639
+ for (let i = 0, l = currentCollection.length; i < l; i++) {
640
+ const item = currentCollection[i], id = item.id;
641
+ if (item && !deletedMap.hasOwnProperty(id) && !updatedMap.hasOwnProperty(id) && !notChangedMap.hasOwnProperty(id)) {
642
+ // added
645
643
  this._map.set(id, { x: 0, y: 0, width: itemSize, height: itemSize, method: ItemDisplayMethods.CREATE });
646
644
  }
647
645
  }
@@ -913,6 +911,10 @@ class TrackBox extends CacheMap {
913
911
  this.clearScrollDirectionCache();
914
912
  }
915
913
  }
914
+ changes() {
915
+ this.bumpVersion();
916
+ this.fireChange();
917
+ }
916
918
  generateDisplayCollection(items, stickyMap, metrics) {
917
919
  const { normalizedItemWidth, normalizedItemHeight, dynamicSize, itemsFromStartToScrollEnd, isVertical, renderItems: renderItemsLength, scrollSize, sizeProperty, snap, snippedPos, startPosition, totalLength, startIndex, typicalItemSize, } = metrics, displayItems = [];
918
920
  if (items.length) {
@@ -1264,6 +1266,19 @@ class NgVirtualListComponent {
1264
1266
  }
1265
1267
  ;
1266
1268
  get itemsOffset() { return this._$itemsOffset.getValue(); }
1269
+ _$trackBy = new BehaviorSubject(TRACK_BY_PROPERTY_NAME);
1270
+ $trackBy = this._$trackBy.asObservable();
1271
+ /**
1272
+ * The name of the property by which tracking is performed
1273
+ */
1274
+ set trackBy(v) {
1275
+ if (this._$trackBy.getValue() === v) {
1276
+ return;
1277
+ }
1278
+ this._$trackBy.next(v);
1279
+ }
1280
+ ;
1281
+ get trackBy() { return this._$trackBy.getValue(); }
1267
1282
  _isVertical = this.getIsVertical();
1268
1283
  _displayComponents = [];
1269
1284
  _$bounds = new BehaviorSubject(null);
@@ -1277,13 +1292,14 @@ class NgVirtualListComponent {
1277
1292
  _onResizeHandler = () => {
1278
1293
  this._$bounds.next(this._container?.nativeElement?.getBoundingClientRect() ?? null);
1279
1294
  };
1295
+ _scrolls = new Map();
1280
1296
  _onScrollHandler = (e) => {
1281
1297
  this._isScrollingDebounces.dispose();
1282
1298
  this.clearScrollToRepeatExecutionTimeout();
1283
1299
  const container = this._container?.nativeElement;
1284
1300
  if (container) {
1285
1301
  const dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft);
1286
- let actualScrollSize = scrollSize, isImmediateScroll = false;
1302
+ let actualScrollSize = scrollSize, isScrollIUmmediate = false;
1287
1303
  if (dynamicSize && delta !== 0) {
1288
1304
  actualScrollSize = scrollSize + delta;
1289
1305
  const params = {
@@ -1292,48 +1308,45 @@ class NgVirtualListComponent {
1292
1308
  };
1293
1309
  const container = this._container;
1294
1310
  if (container) {
1295
- isImmediateScroll = true;
1311
+ isScrollIUmmediate = true;
1296
1312
  this.scrollImmediately(container, params);
1297
- this._trackBox.clearDelta();
1298
1313
  }
1299
1314
  }
1300
- this._$scrollSize.next(actualScrollSize);
1315
+ if (!isScrollIUmmediate) {
1316
+ this._$scrollSize.next(actualScrollSize);
1317
+ }
1301
1318
  }
1302
1319
  };
1303
1320
  scrollImmediately(container, params, cb) {
1304
- this.clearScrollImmediately();
1321
+ if (this._scrolls.size > 0) {
1322
+ container.nativeElement.scrollTo(params);
1323
+ return;
1324
+ }
1325
+ this._trackBox.clearDelta();
1305
1326
  container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
1327
+ container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
1306
1328
  const handler = () => {
1329
+ const container = this._container;
1307
1330
  if (container) {
1308
1331
  container.nativeElement.removeEventListener(SCROLL_END, handler);
1309
- container.nativeElement.scroll(params);
1332
+ this._scrolls.delete(handler);
1333
+ container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
1334
+ container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
1310
1335
  if (cb !== undefined) {
1311
1336
  cb();
1312
1337
  }
1313
- container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
1314
1338
  }
1315
1339
  };
1340
+ this._scrolls.set(handler, true);
1316
1341
  container.nativeElement.addEventListener(SCROLL_END, handler);
1317
- container.nativeElement.scroll(params);
1318
- this._scrollImmediatelyHandler = handler;
1319
- }
1320
- _scrollImmediatelyHandler = undefined;
1321
- clearScrollImmediately() {
1322
- if (this._scrollImmediatelyHandler === undefined) {
1323
- return;
1324
- }
1325
- const container = this._container;
1326
- if (container) {
1327
- container.nativeElement.removeEventListener(SCROLL_END, this._scrollImmediatelyHandler);
1328
- }
1342
+ container.nativeElement.scrollTo(params);
1329
1343
  }
1330
1344
  _onScrollEndHandler = (e) => {
1331
1345
  const container = this._container;
1332
1346
  if (container) {
1333
- this._trackBox.clearDelta();
1334
- this._trackBox.clearDeltaDirection();
1335
1347
  const itemSize = this.itemSize, snapToItem = this.snapToItem, dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
1336
1348
  let actualScrollSize = scrollSize;
1349
+ this._trackBox.clearDeltaDirection();
1337
1350
  if (dynamicSize) {
1338
1351
  actualScrollSize = scrollSize + delta;
1339
1352
  if (snapToItem) {
@@ -1341,15 +1354,17 @@ class NgVirtualListComponent {
1341
1354
  if (targetItem) {
1342
1355
  this.scrollTo(targetItem.id, BEHAVIOR_INSTANT);
1343
1356
  }
1357
+ this._trackBox.clearDelta();
1344
1358
  }
1345
1359
  else if (scrollSize !== actualScrollSize) {
1346
1360
  const params = {
1347
1361
  [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
1348
1362
  behavior: BEHAVIOR_INSTANT
1349
1363
  };
1350
- this._$scrollSize.next(actualScrollSize);
1351
- container.nativeElement.scroll(params);
1352
- return;
1364
+ const container = this._container;
1365
+ if (container) {
1366
+ this.scrollImmediately(container, params);
1367
+ }
1353
1368
  }
1354
1369
  }
1355
1370
  else {
@@ -1360,10 +1375,12 @@ class NgVirtualListComponent {
1360
1375
  [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
1361
1376
  behavior: BEHAVIOR_INSTANT
1362
1377
  };
1363
- container.nativeElement.scroll(params);
1378
+ const container = this._container;
1379
+ if (container) {
1380
+ this.scrollImmediately(container, params);
1381
+ }
1364
1382
  }
1365
1383
  }
1366
- this._$scrollSize.next(actualScrollSize);
1367
1384
  }
1368
1385
  };
1369
1386
  _$initialized = new BehaviorSubject(false);
@@ -1371,7 +1388,7 @@ class NgVirtualListComponent {
1371
1388
  /**
1372
1389
  * Dictionary of element sizes by their id
1373
1390
  */
1374
- _trackBox = new TrackBox(TRACK_BY_PROPERTY_NAME);
1391
+ _trackBox = new TrackBox(this.trackBy);
1375
1392
  _onTrackBoxChangeHandler = (v) => {
1376
1393
  this._$cacheVersion.next(v);
1377
1394
  };
@@ -1386,6 +1403,10 @@ class NgVirtualListComponent {
1386
1403
  this._$initialized = new BehaviorSubject(false);
1387
1404
  this.$initialized = this._$initialized.asObservable();
1388
1405
  this._trackBox.displayComponents = this._displayComponents;
1406
+ const $trackBy = this.$trackBy;
1407
+ $trackBy.pipe(takeUntilDestroyed(), tap(v => {
1408
+ this._trackBox.trackingPropertyName = v;
1409
+ })).subscribe();
1389
1410
  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, $snapToItem = this.$snapToItem, $isVertical = this.$direction.pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $dynamicSize = this.$dynamicSize, $enabledBufferOptimization = this.$enabledBufferOptimization, $cacheVersion = this.$cacheVersion;
1390
1411
  $isVertical.pipe(takeUntilDestroyed(), tap(v => {
1391
1412
  this._isVertical = v;
@@ -1404,7 +1425,7 @@ class NgVirtualListComponent {
1404
1425
  bounds: { width, height }, collection: items, dynamicSize, isVertical, itemSize,
1405
1426
  itemsOffset, scrollSize: scrollSize, snap, enabledBufferOptimization,
1406
1427
  };
1407
- const { displayItems, totalSize, delta } = this._trackBox.updateCollection(items, stickyMap, {
1428
+ const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
1408
1429
  ...opts, scrollSize: actualScrollSize,
1409
1430
  });
1410
1431
  this.resetBoundsSize(isVertical, totalSize);
@@ -1412,17 +1433,20 @@ class NgVirtualListComponent {
1412
1433
  this.tracking();
1413
1434
  const container = this._container;
1414
1435
  if (!this.isScrolling && dynamicSize && container) {
1415
- actualScrollSize = scrollSize + delta;
1436
+ actualScrollSize = scrollSize + this._trackBox.delta;
1416
1437
  if (snapToItem) {
1417
- // etc
1438
+ const items = this.items, isVertical = this._isVertical, targetItem = this._trackBox.getNearestItem(actualScrollSize, items, itemSize, isVertical);
1439
+ if (targetItem) {
1440
+ this.scrollTo(targetItem.id, BEHAVIOR_INSTANT);
1441
+ }
1418
1442
  }
1419
1443
  else if (scrollSize !== actualScrollSize) {
1420
1444
  const params = {
1421
1445
  [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
1422
1446
  behavior: BEHAVIOR_INSTANT
1423
1447
  };
1424
- this.scrollImmediately(container, params);
1425
1448
  this._trackBox.clearDelta();
1449
+ container.nativeElement.scrollTo(params);
1426
1450
  }
1427
1451
  }
1428
1452
  return of(displayItems);
@@ -1450,6 +1474,9 @@ class NgVirtualListComponent {
1450
1474
  const dir = d || this.direction;
1451
1475
  return isDirection(dir, Directions.VERTICAL);
1452
1476
  }
1477
+ _componentsResizeObserver = new ResizeObserver(() => {
1478
+ this._trackBox.changes();
1479
+ });
1453
1480
  createDisplayComponentsIfNeed(displayItems) {
1454
1481
  if (!displayItems || !this._listContainerRef) {
1455
1482
  this._trackBox.setDisplayObjectIndexMapById({});
@@ -1461,15 +1488,19 @@ class NgVirtualListComponent {
1461
1488
  if (_listContainerRef) {
1462
1489
  const comp = _listContainerRef.createComponent(NgVirtualListItemComponent);
1463
1490
  this._displayComponents.push(comp);
1491
+ this._componentsResizeObserver.observe(comp.instance.element);
1464
1492
  }
1465
1493
  }
1466
1494
  const maxLength = displayItems.length;
1467
1495
  while (this._displayComponents.length > maxLength) {
1468
1496
  const comp = this._displayComponents.pop();
1469
- comp?.destroy();
1470
- const id = comp?.instance.item?.id;
1471
- if (id !== undefined) {
1472
- this._trackBox.untrackComponentByIdProperty(comp?.instance);
1497
+ if (comp) {
1498
+ this._componentsResizeObserver.unobserve(comp.instance.element);
1499
+ comp.destroy();
1500
+ const id = comp?.instance.item?.id;
1501
+ if (id !== undefined) {
1502
+ this._trackBox.untrackComponentByIdProperty(comp?.instance);
1503
+ }
1473
1504
  }
1474
1505
  }
1475
1506
  this.resetRenderers();
@@ -1558,12 +1589,12 @@ class NgVirtualListComponent {
1558
1589
  };
1559
1590
  container.nativeElement.addEventListener(SCROLL_END, handler);
1560
1591
  }
1561
- container.nativeElement.scroll(params);
1592
+ container.nativeElement.scrollTo(params);
1562
1593
  }
1563
1594
  else {
1564
1595
  const index = items.findIndex(item => item.id === id), scrollSize = index * this.itemSize;
1565
1596
  const params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
1566
- container.nativeElement.scroll(params);
1597
+ container.nativeElement.scrollTo(params);
1567
1598
  }
1568
1599
  }
1569
1600
  }
@@ -1612,23 +1643,36 @@ class NgVirtualListComponent {
1612
1643
  this._onResizeHandler();
1613
1644
  }
1614
1645
  }
1646
+ clearScrollImmediately() {
1647
+ const container = this._container;
1648
+ if (container) {
1649
+ this._scrolls.forEach((_, handler) => {
1650
+ container.nativeElement.removeEventListener(SCROLL_END, handler);
1651
+ });
1652
+ }
1653
+ this._scrolls.clear();
1654
+ }
1615
1655
  ngOnDestroy() {
1616
1656
  this.clearScrollToRepeatExecutionTimeout();
1657
+ this.clearScrollImmediately();
1617
1658
  if (this._trackBox) {
1618
1659
  this._trackBox.dispose();
1619
1660
  }
1620
1661
  if (this._isScrollingDebounces) {
1621
1662
  this._isScrollingDebounces.dispose();
1622
1663
  }
1664
+ if (this._componentsResizeObserver) {
1665
+ this._componentsResizeObserver.disconnect();
1666
+ }
1667
+ if (this._resizeObserver) {
1668
+ this._resizeObserver.disconnect();
1669
+ }
1623
1670
  const containerEl = this._container;
1624
1671
  if (containerEl) {
1625
1672
  containerEl.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
1626
1673
  containerEl.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
1627
1674
  containerEl.nativeElement.removeEventListener(SCROLL, this._onContainerScrollHandler);
1628
1675
  containerEl.nativeElement.removeEventListener(SCROLL_END, this._onContainerScrollEndHandler);
1629
- if (this._resizeObserver) {
1630
- this._resizeObserver.unobserve(containerEl.nativeElement);
1631
- }
1632
1676
  }
1633
1677
  if (this._displayComponents) {
1634
1678
  while (this._displayComponents.length > 0) {
@@ -1638,7 +1682,7 @@ class NgVirtualListComponent {
1638
1682
  }
1639
1683
  }
1640
1684
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NgVirtualListComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
1641
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: NgVirtualListComponent, selector: "ng-virtual-list", inputs: { items: "items", snap: "snap", snapToItem: "snapToItem", enabledBufferOptimization: "enabledBufferOptimization", itemRenderer: "itemRenderer", stickyMap: "stickyMap", itemSize: "itemSize", dynamicSize: "dynamicSize", direction: "direction", itemsOffset: "itemsOffset" }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd" }, viewQueries: [{ propertyName: "_listContainerRef", first: true, predicate: ["renderersContainer"], descendants: true, read: ViewContainerRef }, { propertyName: "_container", first: true, predicate: ["container"], descendants: true, read: (ElementRef) }, { propertyName: "_list", first: true, predicate: ["list"], descendants: true, read: (ElementRef) }], ngImport: i0, template: "<div #container part=\"scroller\" class=\"ngvl__container\">\r\n <ul #list part=\"list\" class=\"ngvl__list\">\r\n <ng-container #renderersContainer></ng-container>\r\n </ul>\r\n</div>", styles: [":host{display:block;width:400px;overflow:hidden}:host(.horizontal){height:48px}:host(.vertical){height:320px}.ngvl__container{overflow:auto;width:100%;height:100%}.ngvl__list{position:relative;list-style:none;padding:0;margin:0;width:100%;height:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
1685
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: NgVirtualListComponent, selector: "ng-virtual-list", inputs: { items: "items", snap: "snap", snapToItem: "snapToItem", enabledBufferOptimization: "enabledBufferOptimization", itemRenderer: "itemRenderer", stickyMap: "stickyMap", itemSize: "itemSize", dynamicSize: "dynamicSize", direction: "direction", itemsOffset: "itemsOffset", trackBy: "trackBy" }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd" }, viewQueries: [{ propertyName: "_listContainerRef", first: true, predicate: ["renderersContainer"], descendants: true, read: ViewContainerRef }, { propertyName: "_container", first: true, predicate: ["container"], descendants: true, read: (ElementRef) }, { propertyName: "_list", first: true, predicate: ["list"], descendants: true, read: (ElementRef) }], ngImport: i0, template: "<div #container part=\"scroller\" class=\"ngvl__container\">\r\n <ul #list part=\"list\" class=\"ngvl__list\">\r\n <ng-container #renderersContainer></ng-container>\r\n </ul>\r\n</div>", styles: [":host{display:block;width:400px;overflow:hidden}:host(.horizontal){height:48px}:host(.vertical){height:320px}.ngvl__container{overflow:auto;width:100%;height:100%}.ngvl__list{position:relative;list-style:none;padding:0;margin:0;width:100%;height:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
1642
1686
  }
1643
1687
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NgVirtualListComponent, decorators: [{
1644
1688
  type: Component,
@@ -1676,6 +1720,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1676
1720
  type: Input
1677
1721
  }], itemsOffset: [{
1678
1722
  type: Input
1723
+ }], trackBy: [{
1724
+ type: Input
1679
1725
  }] } });
1680
1726
 
1681
1727
  class NgVirtualListModule {