ng-virtual-list 14.0.13 → 14.0.14
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/esm2020/lib/components/ng-virtual-list-item.component.mjs +2 -2
- package/esm2020/lib/const/index.mjs +2 -3
- package/esm2020/lib/ng-virtual-list.component.mjs +72 -193
- package/esm2020/lib/utils/cacheMap.mjs +15 -7
- package/esm2020/lib/utils/trackBox.mjs +38 -8
- package/esm2020/lib/utils/tracker.mjs +17 -24
- package/fesm2015/ng-virtual-list.mjs +135 -226
- package/fesm2015/ng-virtual-list.mjs.map +1 -1
- package/fesm2020/ng-virtual-list.mjs +136 -227
- package/fesm2020/ng-virtual-list.mjs.map +1 -1
- package/lib/components/ng-virtual-list-item.component.d.ts +1 -1
- package/lib/const/index.d.ts +1 -2
- package/lib/ng-virtual-list.component.d.ts +4 -17
- package/lib/utils/tracker.d.ts +10 -4
- package/package.json +1 -1
|
@@ -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 { Subject, BehaviorSubject, takeUntil, tap, filter, map, combineLatest, distinctUntilChanged,
|
|
5
|
+
import { Subject, BehaviorSubject, takeUntil, tap, filter, map, combineLatest, distinctUntilChanged, switchMap, of } from 'rxjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Axis of the arrangement of virtual list elements.
|
|
@@ -26,8 +26,7 @@ const DEFAULT_ITEM_SIZE = 24;
|
|
|
26
26
|
const DEFAULT_ITEMS_OFFSET = 2;
|
|
27
27
|
const DEFAULT_LIST_SIZE = 400;
|
|
28
28
|
const DEFAULT_SNAP = false;
|
|
29
|
-
const DEFAULT_ENABLED_BUFFER_OPTIMIZATION =
|
|
30
|
-
const DEFAULT_SNAP_TO_ITEM = false;
|
|
29
|
+
const DEFAULT_ENABLED_BUFFER_OPTIMIZATION = false;
|
|
31
30
|
const DEFAULT_DYNAMIC_SIZE = false;
|
|
32
31
|
const TRACK_BY_PROPERTY_NAME = 'id';
|
|
33
32
|
const DEFAULT_DIRECTION = Directions.VERTICAL;
|
|
@@ -114,7 +113,7 @@ class NgVirtualListItemComponent {
|
|
|
114
113
|
const el = this._elementRef.nativeElement, { width, height, left, top } = el.getBoundingClientRect();
|
|
115
114
|
return { width, height, x: left, y: top };
|
|
116
115
|
}
|
|
117
|
-
|
|
116
|
+
show() {
|
|
118
117
|
const styles = this._elementRef.nativeElement.style;
|
|
119
118
|
if (styles.visibility === VISIBILITY_VISIBLE) {
|
|
120
119
|
return;
|
|
@@ -235,29 +234,24 @@ class Tracker {
|
|
|
235
234
|
/**
|
|
236
235
|
* tracking by propName
|
|
237
236
|
*/
|
|
238
|
-
track(items, components,
|
|
237
|
+
track(items, components, direction) {
|
|
239
238
|
if (!items) {
|
|
240
239
|
return;
|
|
241
240
|
}
|
|
242
|
-
const idPropName = this._trackingPropertyName, untrackedItems = [...components];
|
|
243
|
-
for (let i = 0, l = items.length; i < l; i++) {
|
|
241
|
+
const idPropName = this._trackingPropertyName, untrackedItems = [...components], isDown = direction === 0 || direction === 1;
|
|
242
|
+
for (let i = isDown ? 0 : items.length - 1, l = isDown ? items.length : 0; isDown ? i < l : i >= l; isDown ? i++ : i--) {
|
|
244
243
|
const item = items[i], itemTrackingProperty = item[idPropName];
|
|
245
244
|
if (this._trackMap) {
|
|
246
|
-
const diId = this._trackMap[itemTrackingProperty];
|
|
247
245
|
if (this._trackMap.hasOwnProperty(itemTrackingProperty)) {
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
if (
|
|
246
|
+
const diId = this._trackMap[itemTrackingProperty], compIndex = this._displayObjectIndexMapById[diId], comp = components[compIndex];
|
|
247
|
+
const compId = comp?.instance?.id;
|
|
248
|
+
if (comp !== undefined && compId == diId) {
|
|
251
249
|
const indexByUntrackedItems = untrackedItems.findIndex(v => {
|
|
252
|
-
return v.instance.id
|
|
250
|
+
return v.instance.id == compId;
|
|
253
251
|
});
|
|
254
252
|
if (indexByUntrackedItems > -1) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (afterComponentSetup !== undefined) {
|
|
258
|
-
afterComponentSetup(el.instance, item);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
253
|
+
comp.instance.item = item;
|
|
254
|
+
comp.instance.show();
|
|
261
255
|
untrackedItems.splice(indexByUntrackedItems, 1);
|
|
262
256
|
continue;
|
|
263
257
|
}
|
|
@@ -268,20 +262,18 @@ class Tracker {
|
|
|
268
262
|
if (untrackedItems.length > 0) {
|
|
269
263
|
const el = untrackedItems.shift(), item = items[i];
|
|
270
264
|
if (el) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
this._trackMap[itemTrackingProperty] = el.instance.id;
|
|
275
|
-
}
|
|
276
|
-
if (afterComponentSetup !== undefined) {
|
|
277
|
-
afterComponentSetup(el.instance, item);
|
|
278
|
-
}
|
|
265
|
+
el.instance.item = item;
|
|
266
|
+
if (this._trackMap) {
|
|
267
|
+
this._trackMap[itemTrackingProperty] = el.instance.id;
|
|
279
268
|
}
|
|
280
269
|
}
|
|
281
270
|
}
|
|
282
271
|
}
|
|
283
272
|
if (untrackedItems.length) {
|
|
284
|
-
|
|
273
|
+
for (let i = 0, l = untrackedItems.length; i < l; i++) {
|
|
274
|
+
const comp = untrackedItems[i];
|
|
275
|
+
comp.instance.hide();
|
|
276
|
+
}
|
|
285
277
|
}
|
|
286
278
|
}
|
|
287
279
|
untrackComponentByIdProperty(component) {
|
|
@@ -404,7 +396,7 @@ class EventEmitter {
|
|
|
404
396
|
}
|
|
405
397
|
}
|
|
406
398
|
|
|
407
|
-
const MAX_SCROLL_DIRECTION_POOL =
|
|
399
|
+
const MAX_SCROLL_DIRECTION_POOL = 50, CLEAR_SCROLL_DIRECTION_TO = 10;
|
|
408
400
|
/**
|
|
409
401
|
* Cache map.
|
|
410
402
|
* Emits a change event on each mutation.
|
|
@@ -452,15 +444,23 @@ class CacheMap extends EventEmitter {
|
|
|
452
444
|
this._scrollDirectionCache.shift();
|
|
453
445
|
}
|
|
454
446
|
this._scrollDirectionCache.push(v);
|
|
455
|
-
const dict = { [-1]: 0, [0]: 0, [1]: 0 };
|
|
456
|
-
for (let i = 0, l = this._scrollDirectionCache.length; i < l; i++) {
|
|
457
|
-
const dir = this._scrollDirectionCache[i];
|
|
447
|
+
const dict = { ['-1']: 0, ['0']: 0, ['1']: 0 };
|
|
448
|
+
for (let i = 0, l = this._scrollDirectionCache.length, li = l - 1; i < l; i++) {
|
|
449
|
+
const dir = String(this._scrollDirectionCache[i]);
|
|
458
450
|
dict[dir] += 1;
|
|
451
|
+
if (i === li) {
|
|
452
|
+
for (let d in dict) {
|
|
453
|
+
if (d === String(v)) {
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
dict[d] -= 1;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
459
|
}
|
|
460
|
-
if (dict[-1] > dict[0] && dict[-1] > dict[1]) {
|
|
460
|
+
if (dict['-1'] > dict['0'] && dict['-1'] > dict['1']) {
|
|
461
461
|
return -1;
|
|
462
462
|
}
|
|
463
|
-
else if (dict[1] > dict[-1] && dict[1] > dict[0]) {
|
|
463
|
+
else if (dict['1'] > dict['-1'] && dict['1'] > dict['0']) {
|
|
464
464
|
return 1;
|
|
465
465
|
}
|
|
466
466
|
return 0;
|
|
@@ -716,12 +716,34 @@ class TrackBox extends CacheMap {
|
|
|
716
716
|
*/
|
|
717
717
|
recalculateMetrics(options) {
|
|
718
718
|
const { fromItemId, bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, stickyMap, enabledBufferOptimization } = options;
|
|
719
|
-
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, map = this._map, snapshot = this._snapshot,
|
|
719
|
+
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, map = this._map, snapshot = this._snapshot, checkOverscrollItemsLimit = Math.ceil(size / typicalItemSize), snippedPos = Math.floor(scrollSize), leftItemsWeights = [], isFromId = fromItemId !== undefined && (typeof fromItemId === 'number' && fromItemId > -1)
|
|
720
720
|
|| (typeof fromItemId === 'string' && fromItemId > '-1');
|
|
721
|
+
let leftItemsOffset = 0, rightItemsOffset = 0;
|
|
722
|
+
if (enabledBufferOptimization) {
|
|
723
|
+
switch (this.scrollDirection) {
|
|
724
|
+
case 1: {
|
|
725
|
+
leftItemsOffset = 0;
|
|
726
|
+
rightItemsOffset = itemsOffset;
|
|
727
|
+
break;
|
|
728
|
+
}
|
|
729
|
+
case -1: {
|
|
730
|
+
leftItemsOffset = itemsOffset;
|
|
731
|
+
rightItemsOffset = 0;
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
case 0:
|
|
735
|
+
default: {
|
|
736
|
+
leftItemsOffset = rightItemsOffset = itemsOffset;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
leftItemsOffset = rightItemsOffset = itemsOffset;
|
|
742
|
+
}
|
|
721
743
|
let itemsFromStartToScrollEnd = -1, itemsFromDisplayEndToOffsetEnd = 0, itemsFromStartToDisplayEnd = -1, leftItemLength = 0, rightItemLength = 0, leftItemsWeight = 0, rightItemsWeight = 0, leftHiddenItemsWeight = 0, totalItemsToDisplayEndWeight = 0, rightSizeOfAddedItems = 0, leftSizeOfAddedItems = 0, rightSizeOfUpdatedItems = 0, leftSizeOfUpdatedItems = 0, itemById = undefined, itemByIdPos = 0, targetDisplayItemIndex = -1, isTargetInOverscroll = false, actualScrollSize = itemByIdPos, totalSize = 0, startIndex;
|
|
722
744
|
// If the list is dynamic or there are new elements in the collection, then it switches to the long algorithm.
|
|
723
745
|
if (dynamicSize) {
|
|
724
|
-
let y = 0, stickyCollectionItem = undefined, stickyComponentSize = 0;
|
|
746
|
+
let y = 0, stickyCollectionItem = undefined, stickyComponentSize = 0, stickyComponentIndex = -1;
|
|
725
747
|
for (let i = 0, l = collection.length; i < l; i++) {
|
|
726
748
|
const ii = i + 1, collectionItem = collection[i], id = collectionItem.id;
|
|
727
749
|
let componentSize = 0, componentSizeDelta = 0, itemDisplayMethod = ItemDisplayMethods.NOT_CHANGED;
|
|
@@ -752,18 +774,26 @@ class TrackBox extends CacheMap {
|
|
|
752
774
|
if (id !== fromItemId && stickyMap && stickyMap[id] > 0) {
|
|
753
775
|
stickyComponentSize = componentSize;
|
|
754
776
|
stickyCollectionItem = collectionItem;
|
|
777
|
+
stickyComponentIndex = i;
|
|
755
778
|
}
|
|
756
779
|
if (id === fromItemId) {
|
|
757
780
|
targetDisplayItemIndex = i;
|
|
758
|
-
if (stickyCollectionItem && stickyMap
|
|
781
|
+
if (stickyCollectionItem && stickyMap) {
|
|
759
782
|
const { num } = this.getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical);
|
|
760
783
|
if (num > 0) {
|
|
761
784
|
isTargetInOverscroll = true;
|
|
762
785
|
y -= size - componentSize;
|
|
763
786
|
}
|
|
764
787
|
else {
|
|
765
|
-
y
|
|
766
|
-
|
|
788
|
+
if (stickyMap && !stickyMap[collectionItem.id] && y >= scrollSize && y < scrollSize + stickyComponentSize) {
|
|
789
|
+
const snappedY = scrollSize - stickyComponentSize;
|
|
790
|
+
leftHiddenItemsWeight -= (snappedY - y);
|
|
791
|
+
y = snappedY;
|
|
792
|
+
}
|
|
793
|
+
else {
|
|
794
|
+
y -= stickyComponentSize;
|
|
795
|
+
leftHiddenItemsWeight -= stickyComponentSize;
|
|
796
|
+
}
|
|
767
797
|
}
|
|
768
798
|
}
|
|
769
799
|
itemById = collectionItem;
|
|
@@ -992,7 +1022,7 @@ class TrackBox extends CacheMap {
|
|
|
992
1022
|
if (!this._items || !this._displayComponents) {
|
|
993
1023
|
return;
|
|
994
1024
|
}
|
|
995
|
-
this._tracker.track(this._items, this._displayComponents);
|
|
1025
|
+
this._tracker.track(this._items, this._displayComponents, this.scrollDirection);
|
|
996
1026
|
}
|
|
997
1027
|
setDisplayObjectIndexMapById(v) {
|
|
998
1028
|
this._tracker.displayObjectIndexMapById = v;
|
|
@@ -1128,8 +1158,6 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1128
1158
|
};
|
|
1129
1159
|
this._$snap = new BehaviorSubject(DEFAULT_SNAP);
|
|
1130
1160
|
this.$snap = this._$snap.asObservable();
|
|
1131
|
-
this._$snapToItem = new BehaviorSubject(DEFAULT_SNAP_TO_ITEM);
|
|
1132
|
-
this.$snapToItem = this._$snapToItem.asObservable();
|
|
1133
1161
|
this._$enabledBufferOptimization = new BehaviorSubject(DEFAULT_ENABLED_BUFFER_OPTIMIZATION);
|
|
1134
1162
|
this.$enabledBufferOptimization = this._$enabledBufferOptimization.asObservable();
|
|
1135
1163
|
this._$itemRenderer = new BehaviorSubject(undefined);
|
|
@@ -1157,79 +1185,20 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1157
1185
|
this._displayComponents = [];
|
|
1158
1186
|
this._$bounds = new BehaviorSubject(null);
|
|
1159
1187
|
this._$scrollSize = new BehaviorSubject(0);
|
|
1160
|
-
this._isScrollingDebounces = debounce((v) => {
|
|
1161
|
-
this._isScrolling = v;
|
|
1162
|
-
}, 250);
|
|
1163
|
-
this._isScrolling = false;
|
|
1164
1188
|
this._resizeObserver = null;
|
|
1165
1189
|
this._onResizeHandler = () => {
|
|
1166
1190
|
this._$bounds.next(this._container?.nativeElement?.getBoundingClientRect() ?? null);
|
|
1167
1191
|
};
|
|
1168
|
-
this._scrolls = new Map();
|
|
1169
1192
|
this._onScrollHandler = (e) => {
|
|
1170
|
-
this._isScrollingDebounces.dispose();
|
|
1171
1193
|
this.clearScrollToRepeatExecutionTimeout();
|
|
1172
1194
|
const container = this._container?.nativeElement;
|
|
1173
1195
|
if (container) {
|
|
1174
1196
|
const dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft);
|
|
1175
|
-
let actualScrollSize = scrollSize, isScrollIUmmediate = false;
|
|
1176
|
-
if (dynamicSize && delta !== 0) {
|
|
1177
|
-
actualScrollSize = scrollSize + delta;
|
|
1178
|
-
const params = {
|
|
1179
|
-
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1180
|
-
behavior: BEHAVIOR_INSTANT
|
|
1181
|
-
};
|
|
1182
|
-
const container = this._container;
|
|
1183
|
-
if (container) {
|
|
1184
|
-
isScrollIUmmediate = true;
|
|
1185
|
-
this.scrollImmediately(container, params);
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
if (!isScrollIUmmediate) {
|
|
1189
|
-
this._$scrollSize.next(actualScrollSize);
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
};
|
|
1193
|
-
this._onScrollEndHandler = (e) => {
|
|
1194
|
-
const container = this._container;
|
|
1195
|
-
if (container) {
|
|
1196
|
-
const itemSize = this.itemSize, snapToItem = this.snapToItem, dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
|
|
1197
1197
|
let actualScrollSize = scrollSize;
|
|
1198
|
-
|
|
1199
|
-
if (dynamicSize) {
|
|
1198
|
+
if (dynamicSize && delta !== 0) {
|
|
1200
1199
|
actualScrollSize = scrollSize + delta;
|
|
1201
|
-
if (snapToItem) {
|
|
1202
|
-
const items = this.items, isVertical = this._isVertical, targetItem = this._trackBox.getNearestItem(actualScrollSize, items, itemSize, isVertical);
|
|
1203
|
-
if (targetItem) {
|
|
1204
|
-
this.scrollTo(targetItem.id, BEHAVIOR_INSTANT);
|
|
1205
|
-
}
|
|
1206
|
-
this._trackBox.clearDelta();
|
|
1207
|
-
}
|
|
1208
|
-
else if (scrollSize !== actualScrollSize) {
|
|
1209
|
-
const params = {
|
|
1210
|
-
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1211
|
-
behavior: BEHAVIOR_INSTANT
|
|
1212
|
-
};
|
|
1213
|
-
const container = this._container;
|
|
1214
|
-
if (container) {
|
|
1215
|
-
this.scrollImmediately(container, params);
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
else {
|
|
1220
|
-
const scrollItems = Math.round(scrollSize / itemSize);
|
|
1221
|
-
actualScrollSize = snapToItem ? scrollItems * itemSize : scrollSize;
|
|
1222
|
-
if (scrollSize !== actualScrollSize) {
|
|
1223
|
-
const params = {
|
|
1224
|
-
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1225
|
-
behavior: BEHAVIOR_INSTANT
|
|
1226
|
-
};
|
|
1227
|
-
const container = this._container;
|
|
1228
|
-
if (container) {
|
|
1229
|
-
this.scrollImmediately(container, params);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
1200
|
}
|
|
1201
|
+
this._$scrollSize.next(actualScrollSize);
|
|
1233
1202
|
}
|
|
1234
1203
|
};
|
|
1235
1204
|
this._$initialized = new BehaviorSubject(false);
|
|
@@ -1245,7 +1214,6 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1245
1214
|
this._trackBox.changes();
|
|
1246
1215
|
});
|
|
1247
1216
|
this._onContainerScrollHandler = (e) => {
|
|
1248
|
-
this._isScrolling = true;
|
|
1249
1217
|
const containerEl = this._container;
|
|
1250
1218
|
if (containerEl) {
|
|
1251
1219
|
const scrollSize = (this._isVertical ? containerEl.nativeElement.scrollTop : containerEl.nativeElement.scrollLeft);
|
|
@@ -1259,7 +1227,6 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1259
1227
|
}
|
|
1260
1228
|
};
|
|
1261
1229
|
this._onContainerScrollEndHandler = (e) => {
|
|
1262
|
-
this._isScrollingDebounces.execute(false);
|
|
1263
1230
|
const containerEl = this._container;
|
|
1264
1231
|
if (containerEl) {
|
|
1265
1232
|
const scrollSize = (this._isVertical ? containerEl.nativeElement.scrollTop : containerEl.nativeElement.scrollLeft);
|
|
@@ -1282,7 +1249,7 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1282
1249
|
$trackBy.pipe(takeUntil(this._$unsubscribe), tap(v => {
|
|
1283
1250
|
this._trackBox.trackingPropertyName = v;
|
|
1284
1251
|
})).subscribe();
|
|
1285
|
-
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, $
|
|
1252
|
+
const $bounds = this._$bounds.asObservable().pipe(filter(b => !!b)), $items = this.$items.pipe(map(i => !i ? [] : i)), $scrollSize = this._$scrollSize.asObservable(), $itemSize = this.$itemSize.pipe(map(v => v <= 0 ? DEFAULT_ITEM_SIZE : v)), $itemsOffset = this.$itemsOffset.pipe(map(v => v < 0 ? DEFAULT_ITEMS_OFFSET : v)), $stickyMap = this.$stickyMap.pipe(map(v => !v ? {} : v)), $snap = this.$snap, $isVertical = this.$direction.pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $dynamicSize = this.$dynamicSize, $enabledBufferOptimization = this.$enabledBufferOptimization, $cacheVersion = this.$cacheVersion;
|
|
1286
1253
|
$isVertical.pipe(takeUntil(this._$unsubscribe), tap(v => {
|
|
1287
1254
|
this._isVertical = v;
|
|
1288
1255
|
const el = this._elementRef.nativeElement;
|
|
@@ -1292,10 +1259,10 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1292
1259
|
this.listenCacheChangesIfNeed(dynamicSize);
|
|
1293
1260
|
})).subscribe();
|
|
1294
1261
|
combineLatest([this.$initialized, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
|
|
1295
|
-
$itemsOffset, $snap, $
|
|
1296
|
-
]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(),
|
|
1262
|
+
$itemsOffset, $snap, $isVertical, $dynamicSize, $enabledBufferOptimization, $cacheVersion,
|
|
1263
|
+
]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, enabledBufferOptimization, cacheVersion,]) => {
|
|
1297
1264
|
const { width, height } = bounds;
|
|
1298
|
-
let actualScrollSize =
|
|
1265
|
+
let actualScrollSize = (this._isVertical ? this._container?.nativeElement.scrollTop ?? 0 : this._container?.nativeElement.scrollLeft) ?? 0;
|
|
1299
1266
|
const opts = {
|
|
1300
1267
|
bounds: { width, height }, collection: items, dynamicSize, isVertical, itemSize,
|
|
1301
1268
|
itemsOffset, scrollSize: scrollSize, snap, enabledBufferOptimization,
|
|
@@ -1307,21 +1274,29 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1307
1274
|
this.createDisplayComponentsIfNeed(displayItems);
|
|
1308
1275
|
this.tracking();
|
|
1309
1276
|
const container = this._container;
|
|
1310
|
-
if (
|
|
1311
|
-
actualScrollSize =
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
if (
|
|
1315
|
-
|
|
1277
|
+
if (container) {
|
|
1278
|
+
actualScrollSize = actualScrollSize + this._trackBox.delta;
|
|
1279
|
+
this._trackBox.clearDelta();
|
|
1280
|
+
if (dynamicSize) {
|
|
1281
|
+
if (scrollSize !== actualScrollSize) {
|
|
1282
|
+
const params = {
|
|
1283
|
+
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1284
|
+
behavior: BEHAVIOR_INSTANT
|
|
1285
|
+
};
|
|
1286
|
+
container.nativeElement.scrollTo(params);
|
|
1316
1287
|
}
|
|
1317
1288
|
}
|
|
1318
|
-
else
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1289
|
+
else {
|
|
1290
|
+
if (scrollSize !== actualScrollSize) {
|
|
1291
|
+
const params = {
|
|
1292
|
+
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1293
|
+
behavior: BEHAVIOR_INSTANT
|
|
1294
|
+
};
|
|
1295
|
+
const container = this._container;
|
|
1296
|
+
if (container) {
|
|
1297
|
+
container.nativeElement.scrollTo(params);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1325
1300
|
}
|
|
1326
1301
|
}
|
|
1327
1302
|
return of(displayItems);
|
|
@@ -1360,18 +1335,7 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1360
1335
|
;
|
|
1361
1336
|
get snap() { return this._$snap.getValue(); }
|
|
1362
1337
|
/**
|
|
1363
|
-
*
|
|
1364
|
-
*/
|
|
1365
|
-
set snapToItem(v) {
|
|
1366
|
-
if (this._$snapToItem.getValue() === v) {
|
|
1367
|
-
return;
|
|
1368
|
-
}
|
|
1369
|
-
this._$snapToItem.next(v);
|
|
1370
|
-
this._cdr.markForCheck();
|
|
1371
|
-
}
|
|
1372
|
-
;
|
|
1373
|
-
get snapToItem() { return this._$snapToItem.getValue(); }
|
|
1374
|
-
/**
|
|
1338
|
+
* Experimental!
|
|
1375
1339
|
* Enables buffer optimization.
|
|
1376
1340
|
* Can only be used if items in the collection are not added or updated. Otherwise, artifacts in the form of twitching of the scroll area are possible.
|
|
1377
1341
|
* Works only if the property dynamic = true
|
|
@@ -1470,31 +1434,6 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1470
1434
|
}
|
|
1471
1435
|
;
|
|
1472
1436
|
get trackBy() { return this._$trackBy.getValue(); }
|
|
1473
|
-
get isScrolling() { return this._isScrolling; }
|
|
1474
|
-
scrollImmediately(container, params, cb) {
|
|
1475
|
-
if (this._scrolls.size > 0) {
|
|
1476
|
-
container.nativeElement.scrollTo(params);
|
|
1477
|
-
return;
|
|
1478
|
-
}
|
|
1479
|
-
this._trackBox.clearDelta();
|
|
1480
|
-
container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1481
|
-
container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
|
|
1482
|
-
const handler = () => {
|
|
1483
|
-
const container = this._container;
|
|
1484
|
-
if (container) {
|
|
1485
|
-
container.nativeElement.removeEventListener(SCROLL_END, handler);
|
|
1486
|
-
this._scrolls.delete(handler);
|
|
1487
|
-
container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1488
|
-
container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
|
|
1489
|
-
if (cb !== undefined) {
|
|
1490
|
-
cb();
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
};
|
|
1494
|
-
this._scrolls.set(handler, true);
|
|
1495
|
-
container.nativeElement.addEventListener(SCROLL_END, handler);
|
|
1496
|
-
container.nativeElement.scrollTo(params);
|
|
1497
|
-
}
|
|
1498
1437
|
get $cacheVersion() { return this._$cacheVersion.asObservable(); }
|
|
1499
1438
|
ngOnInit() {
|
|
1500
1439
|
this._$initialized.next(true);
|
|
@@ -1522,25 +1461,14 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1522
1461
|
}
|
|
1523
1462
|
this._trackBox.items = displayItems;
|
|
1524
1463
|
const _listContainerRef = this._listContainerRef;
|
|
1525
|
-
|
|
1464
|
+
const maxLength = displayItems.length, components = this._displayComponents;
|
|
1465
|
+
while (components.length < maxLength) {
|
|
1526
1466
|
if (_listContainerRef) {
|
|
1527
1467
|
const comp = _listContainerRef.createComponent(NgVirtualListItemComponent);
|
|
1528
|
-
|
|
1468
|
+
components.push(comp);
|
|
1529
1469
|
this._componentsResizeObserver.observe(comp.instance.element);
|
|
1530
1470
|
}
|
|
1531
1471
|
}
|
|
1532
|
-
const maxLength = displayItems.length;
|
|
1533
|
-
while (this._displayComponents.length > maxLength) {
|
|
1534
|
-
const comp = this._displayComponents.pop();
|
|
1535
|
-
if (comp) {
|
|
1536
|
-
this._componentsResizeObserver.unobserve(comp.instance.element);
|
|
1537
|
-
comp.destroy();
|
|
1538
|
-
const id = comp?.instance.item?.id;
|
|
1539
|
-
if (id !== undefined) {
|
|
1540
|
-
this._trackBox.untrackComponentByIdProperty(comp?.instance);
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
1472
|
this.resetRenderers();
|
|
1545
1473
|
}
|
|
1546
1474
|
resetRenderers(itemRenderer) {
|
|
@@ -1580,53 +1508,51 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1580
1508
|
clearScrollToRepeatExecutionTimeout() {
|
|
1581
1509
|
clearTimeout(this._scrollToRepeatExecutionTimeout);
|
|
1582
1510
|
}
|
|
1583
|
-
scrollToExecutor(id, behavior, iteration = 0) {
|
|
1584
|
-
this._isScrolling = true;
|
|
1585
|
-
this.clearScrollToRepeatExecutionTimeout();
|
|
1511
|
+
scrollToExecutor(id, behavior, iteration = 0, isLastIteration = false) {
|
|
1586
1512
|
const items = this.items;
|
|
1587
1513
|
if (!items || !items.length) {
|
|
1588
1514
|
return;
|
|
1589
1515
|
}
|
|
1590
1516
|
const dynamicSize = this.dynamicSize, container = this._container, itemSize = this.itemSize;
|
|
1591
1517
|
if (container) {
|
|
1518
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1592
1519
|
if (dynamicSize) {
|
|
1593
1520
|
if (container) {
|
|
1594
1521
|
container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
|
|
1595
|
-
container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1596
1522
|
}
|
|
1597
|
-
const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, isVertical = this._isVertical, opts = {
|
|
1523
|
+
const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, isVertical = this._isVertical, delta = this._trackBox.delta, opts = {
|
|
1598
1524
|
bounds: { width, height }, collection: items, dynamicSize, isVertical: this._isVertical, itemSize,
|
|
1599
|
-
itemsOffset: this.itemsOffset, scrollSize: isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
|
|
1525
|
+
itemsOffset: this.itemsOffset, scrollSize: (isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft) + delta,
|
|
1600
1526
|
snap: this.snap, fromItemId: id, enabledBufferOptimization: this.enabledBufferOptimization,
|
|
1601
1527
|
}, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
|
|
1602
|
-
this.
|
|
1528
|
+
this._trackBox.clearDelta();
|
|
1603
1529
|
if (container) {
|
|
1604
|
-
const
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
}
|
|
1627
|
-
container.nativeElement.addEventListener(SCROLL_END, handler);
|
|
1530
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
1531
|
+
...opts, scrollSize, fromItemId: isLastIteration ? undefined : id,
|
|
1532
|
+
}), delta = this._trackBox.delta;
|
|
1533
|
+
this._trackBox.clearDelta();
|
|
1534
|
+
let actualScrollSize = scrollSize + delta;
|
|
1535
|
+
this.resetBoundsSize(isVertical, totalSize);
|
|
1536
|
+
this.createDisplayComponentsIfNeed(displayItems);
|
|
1537
|
+
this.tracking();
|
|
1538
|
+
const _scrollSize = this._trackBox.getItemPosition(id, stickyMap, { ...opts, scrollSize: actualScrollSize, fromItemId: id });
|
|
1539
|
+
const notChanged = actualScrollSize === _scrollSize;
|
|
1540
|
+
if (notChanged) {
|
|
1541
|
+
iteration += 1;
|
|
1542
|
+
}
|
|
1543
|
+
if (iteration < MAX_SCROLL_TO_ITERATIONS) {
|
|
1544
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1545
|
+
this._scrollToRepeatExecutionTimeout = setTimeout(() => {
|
|
1546
|
+
this.scrollToExecutor(id, BEHAVIOR_INSTANT, iteration + 1, notChanged);
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
else {
|
|
1550
|
+
this._$scrollSize.next(actualScrollSize);
|
|
1551
|
+
container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
|
|
1552
|
+
}
|
|
1628
1553
|
}
|
|
1629
1554
|
container.nativeElement.scrollTo(params);
|
|
1555
|
+
this._$scrollSize.next(scrollSize);
|
|
1630
1556
|
}
|
|
1631
1557
|
else {
|
|
1632
1558
|
const index = items.findIndex(item => item.id === id), scrollSize = index * this.itemSize;
|
|
@@ -1646,43 +1572,28 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1646
1572
|
containerEl.nativeElement.addEventListener(SCROLL, this._onContainerScrollHandler);
|
|
1647
1573
|
containerEl.nativeElement.addEventListener(SCROLL_END, this._onContainerScrollEndHandler);
|
|
1648
1574
|
containerEl.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
|
|
1649
|
-
containerEl.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1650
1575
|
this._resizeObserver = new ResizeObserver(this._onResizeHandler);
|
|
1651
1576
|
this._resizeObserver.observe(containerEl.nativeElement);
|
|
1652
1577
|
this._onResizeHandler();
|
|
1653
1578
|
}
|
|
1654
1579
|
}
|
|
1655
|
-
clearScrollImmediately() {
|
|
1656
|
-
const container = this._container;
|
|
1657
|
-
if (container) {
|
|
1658
|
-
this._scrolls.forEach((_, handler) => {
|
|
1659
|
-
container.nativeElement.removeEventListener(SCROLL_END, handler);
|
|
1660
|
-
});
|
|
1661
|
-
}
|
|
1662
|
-
this._scrolls.clear();
|
|
1663
|
-
}
|
|
1664
1580
|
ngOnDestroy() {
|
|
1665
1581
|
super.ngOnDestroy();
|
|
1666
1582
|
this.clearScrollToRepeatExecutionTimeout();
|
|
1667
|
-
this.clearScrollImmediately();
|
|
1668
1583
|
if (this._trackBox) {
|
|
1669
1584
|
this._trackBox.dispose();
|
|
1670
1585
|
}
|
|
1671
|
-
if (this._isScrollingDebounces) {
|
|
1672
|
-
this._isScrollingDebounces.dispose();
|
|
1673
|
-
}
|
|
1674
|
-
if (this._componentsResizeObserver) {
|
|
1675
|
-
this._componentsResizeObserver.disconnect();
|
|
1676
|
-
}
|
|
1677
|
-
if (this._resizeObserver) {
|
|
1678
|
-
this._resizeObserver.disconnect();
|
|
1679
|
-
}
|
|
1680
1586
|
const containerEl = this._container;
|
|
1681
1587
|
if (containerEl) {
|
|
1682
1588
|
containerEl.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
|
|
1683
|
-
containerEl.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1684
1589
|
containerEl.nativeElement.removeEventListener(SCROLL, this._onContainerScrollHandler);
|
|
1685
1590
|
containerEl.nativeElement.removeEventListener(SCROLL_END, this._onContainerScrollEndHandler);
|
|
1591
|
+
if (this._componentsResizeObserver) {
|
|
1592
|
+
this._componentsResizeObserver.disconnect();
|
|
1593
|
+
}
|
|
1594
|
+
if (this._resizeObserver) {
|
|
1595
|
+
this._resizeObserver.disconnect();
|
|
1596
|
+
}
|
|
1686
1597
|
}
|
|
1687
1598
|
if (this._displayComponents) {
|
|
1688
1599
|
while (this._displayComponents.length > 0) {
|
|
@@ -1694,7 +1605,7 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1694
1605
|
}
|
|
1695
1606
|
NgVirtualListComponent.__nextId = 0;
|
|
1696
1607
|
NgVirtualListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NgVirtualListComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
1697
|
-
NgVirtualListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: NgVirtualListComponent, selector: "ng-virtual-list", inputs: { items: "items", snap: "snap",
|
|
1608
|
+
NgVirtualListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: NgVirtualListComponent, selector: "ng-virtual-list", inputs: { items: "items", snap: "snap", 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) }], usesInheritance: true, 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 });
|
|
1698
1609
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NgVirtualListComponent, decorators: [{
|
|
1699
1610
|
type: Component,
|
|
1700
1611
|
args: [{ selector: 'ng-virtual-list', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, 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"] }]
|
|
@@ -1715,8 +1626,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
1715
1626
|
type: Input
|
|
1716
1627
|
}], snap: [{
|
|
1717
1628
|
type: Input
|
|
1718
|
-
}], snapToItem: [{
|
|
1719
|
-
type: Input
|
|
1720
1629
|
}], enabledBufferOptimization: [{
|
|
1721
1630
|
type: Input
|
|
1722
1631
|
}], itemRenderer: [{
|