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.
- package/README.md +2 -1
- package/esm2022/lib/components/ng-virtual-list-item.component.mjs +8 -2
- package/esm2022/lib/ng-virtual-list.component.mjs +86 -42
- package/esm2022/lib/utils/cacheMap.mjs +7 -3
- package/esm2022/lib/utils/trackBox.mjs +71 -21
- package/esm2022/lib/utils/tracker.mjs +20 -29
- package/fesm2022/ng-virtual-list.mjs +188 -142
- package/fesm2022/ng-virtual-list.mjs.map +1 -1
- package/lib/components/ng-virtual-list-item.component.d.ts +3 -1
- package/lib/ng-virtual-list.component.d.ts +11 -3
- package/lib/utils/cacheMap.d.ts +1 -1
- package/lib/utils/trackBox.d.ts +9 -4
- package/lib/utils/tracker.d.ts +2 -2
- package/package.json +1 -1
- package/esm2022/lib/utils/collection.mjs +0 -50
- package/lib/utils/collection.d.ts +0 -19
|
@@ -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,
|
|
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/
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
252
|
-
return v.instance[itemTrackingProperty] === elId;
|
|
259
|
+
return v.instance.id === elId;
|
|
253
260
|
});
|
|
254
261
|
if (indexByUntrackedItems > -1) {
|
|
255
|
-
el.instance.item
|
|
256
|
-
|
|
257
|
-
afterComponentSetup
|
|
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
|
|
270
|
-
|
|
271
|
-
this.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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/
|
|
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)
|
|
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/
|
|
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)
|
|
597
|
-
|
|
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
|
-
|
|
621
|
-
this.clearCache(deleted, updated, added, itemSize);
|
|
580
|
+
this.updateCache(this._previousCollection, currentCollection, itemSize);
|
|
622
581
|
this._previousCollection = currentCollection;
|
|
623
582
|
}
|
|
624
583
|
/**
|
|
625
|
-
*
|
|
584
|
+
* Update the cache of items from the list
|
|
626
585
|
*/
|
|
627
|
-
|
|
628
|
-
if (
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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 (
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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,
|
|
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
|
-
|
|
1311
|
+
isScrollIUmmediate = true;
|
|
1296
1312
|
this.scrollImmediately(container, params);
|
|
1297
|
-
this._trackBox.clearDelta();
|
|
1298
1313
|
}
|
|
1299
1314
|
}
|
|
1300
|
-
|
|
1315
|
+
if (!isScrollIUmmediate) {
|
|
1316
|
+
this._$scrollSize.next(actualScrollSize);
|
|
1317
|
+
}
|
|
1301
1318
|
}
|
|
1302
1319
|
};
|
|
1303
1320
|
scrollImmediately(container, params, cb) {
|
|
1304
|
-
this.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
1351
|
-
container
|
|
1352
|
-
|
|
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.
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
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.
|
|
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.
|
|
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 {
|