ng-virtual-list 19.1.24 → 19.1.26

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.
package/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  # NgVirtualList
2
2
  Maximum performance for extremely large lists.
3
- 10x more resource intensive than standard lists
4
3
 
5
4
  Angular version 19.X.X.
6
5
 
@@ -443,7 +442,7 @@ Inputs
443
442
  | Property | Type | Description |
444
443
  |---|---|---|
445
444
  | id | number | Readonly. Returns the unique identifier of the component. |
446
- | items | [IVirtualListCollection](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/models/collection.model.ts) | Collection of list items. |
445
+ | items | [IVirtualListCollection](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/models/collection.model.ts) | Collection of list items. The collection of elements must be immutable. |
447
446
  | itemSize | number? = 24 | If direction = 'vertical', then the height of a typical element. If direction = 'horizontal', then the width of a typical element. Ignored if the dynamicSize property is true. |
448
447
  | itemsOffset | number? = 2 | Number of elements outside the scope of visibility. Default value is 2. |
449
448
  | itemRenderer | TemplateRef | Rendering element template. |
@@ -453,6 +452,7 @@ Inputs
453
452
  | direction | [Direction? = 'vertical'](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/enums/direction.ts) | Determines the direction in which elements are placed. Default value is "vertical". |
454
453
  | dynamicSize | boolean? = false | If true then the items in the list can have different sizes and the itemSize property is ignored. If false then the items in the list have a fixed size specified by the itemSize property. The default value is false. |
455
454
  | enabledBufferOptimization | boolean? = true | Enables buffer optimization. Can only be used if items in the collection are not added or updated. |
455
+ | trackBy | string? = 'id' | The name of the property by which tracking is performed. |
456
456
 
457
457
  <br/>
458
458
 
@@ -3,7 +3,7 @@ import { signal, inject, ElementRef, ChangeDetectionStrategy, Component, viewChi
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
- import { BehaviorSubject, filter, map, tap, combineLatest, distinctUntilChanged, switchMap, of } from 'rxjs';
6
+ import { BehaviorSubject, tap, filter, map, combineLatest, distinctUntilChanged, switchMap, of } from 'rxjs';
7
7
 
8
8
  /**
9
9
  * Axis of the arrangement of virtual list elements.
@@ -79,7 +79,7 @@ class NgVirtualListItemComponent {
79
79
  const data = this._data = v;
80
80
  if (data) {
81
81
  const styles = this._elementRef.nativeElement.style;
82
- styles.zIndex = data.config.sticky;
82
+ styles.zIndex = String(data.config.sticky);
83
83
  if (data.config.snapped) {
84
84
  styles.transform = ZEROS_TRANSLATE_3D;
85
85
  styles.position = POSITION_STICKY;
@@ -93,6 +93,9 @@ class NgVirtualListItemComponent {
93
93
  }
94
94
  this.data.set(v);
95
95
  }
96
+ get item() {
97
+ return this._data;
98
+ }
96
99
  get itemId() {
97
100
  return this._data?.id;
98
101
  }
@@ -101,6 +104,9 @@ class NgVirtualListItemComponent {
101
104
  this.itemRenderer.set(v);
102
105
  }
103
106
  _elementRef = inject((ElementRef));
107
+ get element() {
108
+ return this._elementRef.nativeElement;
109
+ }
104
110
  constructor() {
105
111
  this._id = NgVirtualListItemComponent.__nextId = NgVirtualListItemComponent.__nextId === Number.MAX_SAFE_INTEGER
106
112
  ? 0 : NgVirtualListItemComponent.__nextId + 1;
@@ -221,6 +227,9 @@ class Tracker {
221
227
  return this._trackMap;
222
228
  }
223
229
  _trackingPropertyName;
230
+ set trackingPropertyName(v) {
231
+ this._trackingPropertyName = v;
232
+ }
224
233
  constructor(trackingPropertyName) {
225
234
  this._trackingPropertyName = trackingPropertyName;
226
235
  }
@@ -238,17 +247,17 @@ class Tracker {
238
247
  const diId = this._trackMap[itemTrackingProperty];
239
248
  if (this._trackMap.hasOwnProperty(itemTrackingProperty)) {
240
249
  const lastIndex = this._displayObjectIndexMapById[diId], el = components[lastIndex];
241
- this._checkComponentProperty(el?.instance);
242
- const elId = el?.instance?.[itemTrackingProperty];
250
+ const elId = el?.instance?.id;
243
251
  if (el && elId === diId) {
244
252
  const indexByUntrackedItems = untrackedItems.findIndex(v => {
245
- this._checkComponentProperty(v.instance);
246
- return v.instance[itemTrackingProperty] === elId;
253
+ return v.instance.id === elId;
247
254
  });
248
255
  if (indexByUntrackedItems > -1) {
249
- el.instance.item = item;
250
- if (afterComponentSetup !== undefined) {
251
- afterComponentSetup(el.instance, item);
256
+ if (el.instance.item !== item) {
257
+ el.instance.item = item;
258
+ if (afterComponentSetup !== undefined) {
259
+ afterComponentSetup(el.instance, item);
260
+ }
252
261
  }
253
262
  untrackedItems.splice(indexByUntrackedItems, 1);
254
263
  continue;
@@ -260,13 +269,14 @@ class Tracker {
260
269
  if (untrackedItems.length > 0) {
261
270
  const el = untrackedItems.shift(), item = items[i];
262
271
  if (el) {
263
- el.instance.item = item;
264
- if (this._trackMap) {
265
- this._checkComponentProperty(el.instance);
266
- this._trackMap[itemTrackingProperty] = el.instance[itemTrackingProperty];
267
- }
268
- if (afterComponentSetup !== undefined) {
269
- afterComponentSetup(el.instance, item);
272
+ if (el.instance.item !== item) {
273
+ el.instance.item = item;
274
+ if (this._trackMap) {
275
+ this._trackMap[itemTrackingProperty] = el.instance.id;
276
+ }
277
+ if (afterComponentSetup !== undefined) {
278
+ afterComponentSetup(el.instance, item);
279
+ }
270
280
  }
271
281
  }
272
282
  }
@@ -280,23 +290,10 @@ class Tracker {
280
290
  return;
281
291
  }
282
292
  const propertyIdName = this._trackingPropertyName;
283
- this._checkComponentProperty(component);
284
293
  if (this._trackMap && component[propertyIdName] !== undefined) {
285
294
  delete this._trackMap[propertyIdName];
286
295
  }
287
296
  }
288
- _checkComponentProperty(component) {
289
- if (!component) {
290
- return;
291
- }
292
- const propertyIdName = this._trackingPropertyName;
293
- try {
294
- component[propertyIdName];
295
- }
296
- catch (err) {
297
- throw Error(`Property ${propertyIdName} does not exist.`);
298
- }
299
- }
300
297
  dispose() {
301
298
  this._trackMap = null;
302
299
  }
@@ -475,7 +472,11 @@ class CacheMap extends EventEmitter {
475
472
  this.dispatch('change', this.version);
476
473
  }
477
474
  set(id, bounds) {
478
- if (this._map.has(id) && JSON.stringify(this._map.get(id)) === JSON.stringify(bounds)) {
475
+ if (this._map.has(id)) {
476
+ const b = this._map.get(id), bb = bounds;
477
+ if (b.width === bb.width && b.height === bb.height) {
478
+ return this._map;
479
+ }
479
480
  return this._map;
480
481
  }
481
482
  const v = this._map.set(id, bounds);
@@ -502,56 +503,6 @@ class CacheMap extends EventEmitter {
502
503
  }
503
504
  }
504
505
 
505
- /**
506
- * Returns the removed or updated elements of a collection.
507
- * @link https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/utils/collection.ts
508
- * @author Evgenii Grebennikov
509
- * @email djonnyx@gmail.com
510
- */
511
- const getCollectionRemovedOrUpdatedItems = (previousCollection, currentCollection) => {
512
- const result = { deleted: new Array(), updated: new Array(), added: new Array(), notChanged: new Array() };
513
- if (!currentCollection || currentCollection.length === 0) {
514
- return { deleted: (previousCollection ? [...previousCollection] : []), updated: [], added: [], notChanged: [] };
515
- }
516
- if (!previousCollection || previousCollection.length === 0) {
517
- return { deleted: [], updated: [], added: (currentCollection ? [...currentCollection] : []), notChanged: [] };
518
- }
519
- const collectionDict = {};
520
- for (let i = 0, l = currentCollection.length; i < l; i++) {
521
- const item = currentCollection[i];
522
- if (item) {
523
- collectionDict[item.id] = item;
524
- }
525
- }
526
- const notChangedMap = {}, deletedMap = {}, updatedMap = {};
527
- for (let i = 0, l = previousCollection.length; i < l; i++) {
528
- const item = previousCollection[i], id = item.id;
529
- if (item) {
530
- if (collectionDict.hasOwnProperty(id)) {
531
- if (item === collectionDict[id]) {
532
- result.notChanged.push(item);
533
- notChangedMap[item.id] = item;
534
- continue;
535
- }
536
- else {
537
- result.updated.push(item);
538
- updatedMap[item.id] = item;
539
- continue;
540
- }
541
- }
542
- result.deleted.push(item);
543
- deletedMap[item.id] = item;
544
- }
545
- }
546
- for (let i = 0, l = currentCollection.length; i < l; i++) {
547
- const item = currentCollection[i], id = item.id;
548
- if (item && !deletedMap.hasOwnProperty(id) && !updatedMap.hasOwnProperty(id) && !notChangedMap.hasOwnProperty(id)) {
549
- result.added.push(item);
550
- }
551
- }
552
- return result;
553
- };
554
-
555
506
  const TRACK_BOX_CHANGE_EVENT_NAME = 'change';
556
507
  var ItemDisplayMethods;
557
508
  (function (ItemDisplayMethods) {
@@ -582,13 +533,22 @@ class TrackBox extends CacheMap {
582
533
  }
583
534
  this._displayComponents = v;
584
535
  }
536
+ /**
537
+ * Set the trackBy property
538
+ */
539
+ set trackingPropertyName(v) {
540
+ this._tracker.trackingPropertyName = v;
541
+ }
585
542
  constructor(trackingPropertyName) {
586
543
  super();
587
544
  this._tracker = new Tracker(trackingPropertyName);
588
545
  }
589
546
  set(id, bounds) {
590
- if (this._map.has(id) && JSON.stringify(this._map.get(id)) === JSON.stringify(bounds)) {
591
- return this._map;
547
+ if (this._map.has(id)) {
548
+ const b = this._map.get(id);
549
+ if (b?.width === bounds.width && b.height === bounds.height) {
550
+ return this._map;
551
+ }
592
552
  }
593
553
  const v = this._map.set(id, bounds);
594
554
  this.bumpVersion();
@@ -611,31 +571,69 @@ class TrackBox extends CacheMap {
611
571
  console.warn('Attention! The collection must be immutable.');
612
572
  return;
613
573
  }
614
- const { deleted, updated, added } = getCollectionRemovedOrUpdatedItems(this._previousCollection, currentCollection);
615
- this.clearCache(deleted, updated, added, itemSize);
574
+ this.updateCache(this._previousCollection, currentCollection, itemSize);
616
575
  this._previousCollection = currentCollection;
617
576
  }
618
577
  /**
619
- * Clears the cache of items from the list
578
+ * Update the cache of items from the list
620
579
  */
621
- clearCache(deleted, updated, added, itemSize) {
622
- if (deleted) {
623
- for (let i = 0, l = deleted.length; i < l; i++) {
624
- const item = deleted[i], id = item.id;
625
- if (this._map.has(id)) {
626
- this._map.delete(id);
580
+ updateCache(previousCollection, currentCollection, itemSize) {
581
+ if (!currentCollection || currentCollection.length === 0) {
582
+ if (previousCollection) {
583
+ // deleted
584
+ for (let i = 0, l = previousCollection.length; i < l; i++) {
585
+ const item = previousCollection[i], id = item.id;
586
+ if (this._map.has(id)) {
587
+ this._map.delete(id);
588
+ }
627
589
  }
628
590
  }
591
+ return;
592
+ }
593
+ if (!previousCollection || previousCollection.length === 0) {
594
+ if (currentCollection) {
595
+ // added
596
+ for (let i = 0, l = currentCollection.length; i < l; i++) {
597
+ const item = currentCollection[i], id = item.id;
598
+ this._map.set(id, { x: 0, y: 0, width: itemSize, height: itemSize, method: ItemDisplayMethods.CREATE });
599
+ }
600
+ }
601
+ return;
629
602
  }
630
- if (updated) {
631
- for (let i = 0, l = updated.length; i < l; i++) {
632
- const item = updated[i], id = item.id;
633
- this._map.set(id, { ...(this._map.get(id) || { x: 0, y: 0, width: itemSize, height: itemSize }), method: ItemDisplayMethods.UPDATE });
603
+ const collectionDict = {};
604
+ for (let i = 0, l = currentCollection.length; i < l; i++) {
605
+ const item = currentCollection[i];
606
+ if (item) {
607
+ collectionDict[item.id] = item;
608
+ }
609
+ }
610
+ const notChangedMap = {}, deletedMap = {}, updatedMap = {};
611
+ for (let i = 0, l = previousCollection.length; i < l; i++) {
612
+ const item = previousCollection[i], id = item.id;
613
+ if (item) {
614
+ if (collectionDict.hasOwnProperty(id)) {
615
+ if (item === collectionDict[id]) {
616
+ // not changed
617
+ notChangedMap[item.id] = item;
618
+ this._map.set(id, { ...(this._map.get(id) || { x: 0, y: 0, width: itemSize, height: itemSize }), method: ItemDisplayMethods.NOT_CHANGED });
619
+ continue;
620
+ }
621
+ else {
622
+ // updated
623
+ updatedMap[item.id] = item;
624
+ this._map.set(id, { ...(this._map.get(id) || { x: 0, y: 0, width: itemSize, height: itemSize }), method: ItemDisplayMethods.UPDATE });
625
+ continue;
626
+ }
627
+ }
628
+ // deleted
629
+ deletedMap[item.id] = item;
630
+ this._map.delete(id);
634
631
  }
635
632
  }
636
- if (added) {
637
- for (let i = 0, l = added.length; i < l; i++) {
638
- const item = added[i], id = item.id;
633
+ for (let i = 0, l = currentCollection.length; i < l; i++) {
634
+ const item = currentCollection[i], id = item.id;
635
+ if (item && !deletedMap.hasOwnProperty(id) && !updatedMap.hasOwnProperty(id) && !notChangedMap.hasOwnProperty(id)) {
636
+ // added
639
637
  this._map.set(id, { x: 0, y: 0, width: itemSize, height: itemSize, method: ItemDisplayMethods.CREATE });
640
638
  }
641
639
  }
@@ -907,6 +905,10 @@ class TrackBox extends CacheMap {
907
905
  this.clearScrollDirectionCache();
908
906
  }
909
907
  }
908
+ changes() {
909
+ this.bumpVersion();
910
+ this.fireChange();
911
+ }
910
912
  generateDisplayCollection(items, stickyMap, metrics) {
911
913
  const { normalizedItemWidth, normalizedItemHeight, dynamicSize, itemsFromStartToScrollEnd, isVertical, renderItems: renderItemsLength, scrollSize, sizeProperty, snap, snippedPos, startPosition, totalLength, startIndex, typicalItemSize, } = metrics, displayItems = [];
912
914
  if (items.length) {
@@ -1181,7 +1183,7 @@ class NgVirtualListComponent {
1181
1183
  const container = this._container()?.nativeElement;
1182
1184
  if (container) {
1183
1185
  const dynamicSize = this.dynamicSize(), delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft);
1184
- let actualScrollSize = scrollSize, isImmediateScroll = false;
1186
+ let actualScrollSize = scrollSize, isScrollIUmmediate = false;
1185
1187
  if (dynamicSize && delta !== 0) {
1186
1188
  actualScrollSize = scrollSize + delta;
1187
1189
  const params = {
@@ -1190,12 +1192,16 @@ class NgVirtualListComponent {
1190
1192
  };
1191
1193
  const container = this._container();
1192
1194
  if (container) {
1193
- isImmediateScroll = true;
1194
- this.scrollImmediately(container, params);
1195
- this._trackBox.clearDelta();
1195
+ isScrollIUmmediate = true;
1196
+ this.scrollImmediately(container, params, () => {
1197
+ const scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
1198
+ this._scrollSize.set(scrollSize);
1199
+ });
1196
1200
  }
1197
1201
  }
1198
- this._scrollSize.set(actualScrollSize);
1202
+ if (!isScrollIUmmediate) {
1203
+ this._scrollSize.set(actualScrollSize);
1204
+ }
1199
1205
  }
1200
1206
  };
1201
1207
  scrollImmediately(container, params, cb) {
@@ -1204,7 +1210,6 @@ class NgVirtualListComponent {
1204
1210
  const handler = () => {
1205
1211
  if (container) {
1206
1212
  container.nativeElement.removeEventListener(SCROLL_END, handler);
1207
- container.nativeElement.scroll(params);
1208
1213
  if (cb !== undefined) {
1209
1214
  cb();
1210
1215
  }
@@ -1213,6 +1218,7 @@ class NgVirtualListComponent {
1213
1218
  };
1214
1219
  container.nativeElement.addEventListener(SCROLL_END, handler);
1215
1220
  container.nativeElement.scroll(params);
1221
+ this._trackBox.clearDelta();
1216
1222
  this._scrollImmediatelyHandler = handler;
1217
1223
  }
1218
1224
  _scrollImmediatelyHandler = undefined;
@@ -1228,10 +1234,10 @@ class NgVirtualListComponent {
1228
1234
  _onScrollEndHandler = (e) => {
1229
1235
  const container = this._container();
1230
1236
  if (container) {
1231
- this._trackBox.clearDelta();
1232
- this._trackBox.clearDeltaDirection();
1233
1237
  const itemSize = this.itemSize(), snapToItem = this.snapToItem(), dynamicSize = this.dynamicSize(), delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
1234
1238
  let actualScrollSize = scrollSize;
1239
+ this._trackBox.clearDelta();
1240
+ this._trackBox.clearDeltaDirection();
1235
1241
  if (dynamicSize) {
1236
1242
  actualScrollSize = scrollSize + delta;
1237
1243
  if (snapToItem) {
@@ -1245,8 +1251,13 @@ class NgVirtualListComponent {
1245
1251
  [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
1246
1252
  behavior: BEHAVIOR_INSTANT
1247
1253
  };
1248
- this._scrollSize.set(actualScrollSize);
1249
- container.nativeElement.scroll(params);
1254
+ const container = this._container();
1255
+ if (container) {
1256
+ this.scrollImmediately(container, params, () => {
1257
+ const scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
1258
+ this._scrollSize.set(scrollSize);
1259
+ });
1260
+ }
1250
1261
  return;
1251
1262
  }
1252
1263
  }
@@ -1258,19 +1269,28 @@ class NgVirtualListComponent {
1258
1269
  [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
1259
1270
  behavior: BEHAVIOR_INSTANT
1260
1271
  };
1261
- container.nativeElement.scroll(params);
1272
+ const container = this._container();
1273
+ if (container) {
1274
+ this.scrollImmediately(container, params, () => {
1275
+ const scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
1276
+ this._scrollSize.set(scrollSize);
1277
+ });
1278
+ }
1262
1279
  }
1263
1280
  }
1264
- this._scrollSize.set(actualScrollSize);
1265
1281
  }
1266
1282
  };
1267
1283
  _elementRef = inject((ElementRef));
1268
1284
  _initialized;
1269
1285
  $initialized;
1286
+ /**
1287
+ * The name of the property by which tracking is performed
1288
+ */
1289
+ trackBy = input(TRACK_BY_PROPERTY_NAME);
1270
1290
  /**
1271
1291
  * Dictionary of element sizes by their id
1272
1292
  */
1273
- _trackBox = new TrackBox(TRACK_BY_PROPERTY_NAME);
1293
+ _trackBox = new TrackBox(this.trackBy());
1274
1294
  _onTrackBoxChangeHandler = (v) => {
1275
1295
  this._$cacheVersion.next(v);
1276
1296
  };
@@ -1283,6 +1303,10 @@ class NgVirtualListComponent {
1283
1303
  this._initialized = signal(false);
1284
1304
  this.$initialized = toObservable(this._initialized);
1285
1305
  this._trackBox.displayComponents = this._displayComponents;
1306
+ const $trackBy = toObservable(this.trackBy);
1307
+ $trackBy.pipe(takeUntilDestroyed(), tap(v => {
1308
+ this._trackBox.trackingPropertyName = v;
1309
+ })).subscribe();
1286
1310
  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), $snapToItem = toObservable(this.snapToItem), $isVertical = toObservable(this.direction).pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $dynamicSize = toObservable(this.dynamicSize), $enabledBufferOptimization = toObservable(this.enabledBufferOptimization), $cacheVersion = this.$cacheVersion;
1287
1311
  $isVertical.pipe(takeUntilDestroyed(), tap(v => {
1288
1312
  this._isVertical = v;
@@ -1311,7 +1335,10 @@ class NgVirtualListComponent {
1311
1335
  if (!this.isScrolling && dynamicSize && container) {
1312
1336
  actualScrollSize = scrollSize + delta;
1313
1337
  if (snapToItem) {
1314
- // etc
1338
+ const items = this.items(), isVertical = this._isVertical, targetItem = this._trackBox.getNearestItem(actualScrollSize, items, itemSize, isVertical);
1339
+ if (targetItem) {
1340
+ this.scrollTo(targetItem.id, BEHAVIOR_INSTANT);
1341
+ }
1315
1342
  }
1316
1343
  else if (scrollSize !== actualScrollSize) {
1317
1344
  const params = {
@@ -1319,7 +1346,6 @@ class NgVirtualListComponent {
1319
1346
  behavior: BEHAVIOR_INSTANT
1320
1347
  };
1321
1348
  this.scrollImmediately(container, params);
1322
- this._trackBox.clearDelta();
1323
1349
  }
1324
1350
  }
1325
1351
  return of(displayItems);
@@ -1347,6 +1373,9 @@ class NgVirtualListComponent {
1347
1373
  const dir = d || this.direction();
1348
1374
  return isDirection(dir, Directions.VERTICAL);
1349
1375
  }
1376
+ _componentsResizeObserver = new ResizeObserver(() => {
1377
+ this._trackBox.changes();
1378
+ });
1350
1379
  createDisplayComponentsIfNeed(displayItems) {
1351
1380
  if (!displayItems || !this._listContainerRef) {
1352
1381
  this._trackBox.setDisplayObjectIndexMapById({});
@@ -1358,15 +1387,19 @@ class NgVirtualListComponent {
1358
1387
  if (_listContainerRef) {
1359
1388
  const comp = _listContainerRef.createComponent(NgVirtualListItemComponent);
1360
1389
  this._displayComponents.push(comp);
1390
+ this._componentsResizeObserver.observe(comp.instance.element);
1361
1391
  }
1362
1392
  }
1363
1393
  const maxLength = displayItems.length;
1364
1394
  while (this._displayComponents.length > maxLength) {
1365
1395
  const comp = this._displayComponents.pop();
1366
- comp?.destroy();
1367
- const id = comp?.instance.item?.id;
1368
- if (id !== undefined) {
1369
- this._trackBox.untrackComponentByIdProperty(comp?.instance);
1396
+ if (comp) {
1397
+ this._componentsResizeObserver.unobserve(comp.instance.element);
1398
+ comp?.destroy();
1399
+ const id = comp?.instance.item?.id;
1400
+ if (id !== undefined) {
1401
+ this._trackBox.untrackComponentByIdProperty(comp?.instance);
1402
+ }
1370
1403
  }
1371
1404
  }
1372
1405
  this.resetRenderers();
@@ -1523,8 +1556,11 @@ class NgVirtualListComponent {
1523
1556
  containerEl.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
1524
1557
  containerEl.nativeElement.removeEventListener(SCROLL, this._onContainerScrollHandler);
1525
1558
  containerEl.nativeElement.removeEventListener(SCROLL_END, this._onContainerScrollEndHandler);
1559
+ if (this._componentsResizeObserver) {
1560
+ this._componentsResizeObserver.disconnect();
1561
+ }
1526
1562
  if (this._resizeObserver) {
1527
- this._resizeObserver.unobserve(containerEl.nativeElement);
1563
+ this._resizeObserver.disconnect();
1528
1564
  }
1529
1565
  }
1530
1566
  if (this._displayComponents) {
@@ -1535,7 +1571,7 @@ class NgVirtualListComponent {
1535
1571
  }
1536
1572
  }
1537
1573
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1538
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.14", type: NgVirtualListComponent, isStandalone: true, selector: "ng-virtual-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, snap: { classPropertyName: "snap", publicName: "snap", isSignal: true, isRequired: false, transformFunction: null }, snapToItem: { classPropertyName: "snapToItem", publicName: "snapToItem", isSignal: true, isRequired: false, transformFunction: null }, enabledBufferOptimization: { classPropertyName: "enabledBufferOptimization", publicName: "enabledBufferOptimization", isSignal: true, isRequired: false, transformFunction: null }, itemRenderer: { classPropertyName: "itemRenderer", publicName: "itemRenderer", isSignal: true, isRequired: true, transformFunction: null }, stickyMap: { classPropertyName: "stickyMap", publicName: "stickyMap", isSignal: true, isRequired: false, transformFunction: null }, itemSize: { classPropertyName: "itemSize", publicName: "itemSize", isSignal: true, isRequired: false, transformFunction: null }, dynamicSize: { classPropertyName: "dynamicSize", publicName: "dynamicSize", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, itemsOffset: { classPropertyName: "itemsOffset", publicName: "itemsOffset", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd" }, viewQueries: [{ propertyName: "_container", first: true, predicate: ["container"], descendants: true, isSignal: true }, { propertyName: "_list", first: true, predicate: ["list"], descendants: true, isSignal: true }, { propertyName: "_listContainerRef", first: true, predicate: ["renderersContainer"], descendants: true, read: ViewContainerRef }], 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"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
1574
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.14", type: NgVirtualListComponent, isStandalone: true, selector: "ng-virtual-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, snap: { classPropertyName: "snap", publicName: "snap", isSignal: true, isRequired: false, transformFunction: null }, snapToItem: { classPropertyName: "snapToItem", publicName: "snapToItem", isSignal: true, isRequired: false, transformFunction: null }, enabledBufferOptimization: { classPropertyName: "enabledBufferOptimization", publicName: "enabledBufferOptimization", isSignal: true, isRequired: false, transformFunction: null }, itemRenderer: { classPropertyName: "itemRenderer", publicName: "itemRenderer", isSignal: true, isRequired: true, transformFunction: null }, stickyMap: { classPropertyName: "stickyMap", publicName: "stickyMap", isSignal: true, isRequired: false, transformFunction: null }, itemSize: { classPropertyName: "itemSize", publicName: "itemSize", isSignal: true, isRequired: false, transformFunction: null }, dynamicSize: { classPropertyName: "dynamicSize", publicName: "dynamicSize", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, itemsOffset: { classPropertyName: "itemsOffset", publicName: "itemsOffset", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd" }, viewQueries: [{ propertyName: "_container", first: true, predicate: ["container"], descendants: true, isSignal: true }, { propertyName: "_list", first: true, predicate: ["list"], descendants: true, isSignal: true }, { propertyName: "_listContainerRef", first: true, predicate: ["renderersContainer"], descendants: true, read: ViewContainerRef }], 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"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
1539
1575
  }
1540
1576
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListComponent, decorators: [{
1541
1577
  type: Component,