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;
|
|
@@ -115,7 +114,7 @@ class NgVirtualListItemComponent {
|
|
|
115
114
|
const el = this._elementRef.nativeElement, { width, height, left, top } = el.getBoundingClientRect();
|
|
116
115
|
return { width, height, x: left, y: top };
|
|
117
116
|
}
|
|
118
|
-
|
|
117
|
+
show() {
|
|
119
118
|
const styles = this._elementRef.nativeElement.style;
|
|
120
119
|
if (styles.visibility === VISIBILITY_VISIBLE) {
|
|
121
120
|
return;
|
|
@@ -236,30 +235,25 @@ class Tracker {
|
|
|
236
235
|
/**
|
|
237
236
|
* tracking by propName
|
|
238
237
|
*/
|
|
239
|
-
track(items, components,
|
|
238
|
+
track(items, components, direction) {
|
|
240
239
|
var _a;
|
|
241
240
|
if (!items) {
|
|
242
241
|
return;
|
|
243
242
|
}
|
|
244
|
-
const idPropName = this._trackingPropertyName, untrackedItems = [...components];
|
|
245
|
-
for (let i = 0, l = items.length; i < l; i++) {
|
|
243
|
+
const idPropName = this._trackingPropertyName, untrackedItems = [...components], isDown = direction === 0 || direction === 1;
|
|
244
|
+
for (let i = isDown ? 0 : items.length - 1, l = isDown ? items.length : 0; isDown ? i < l : i >= l; isDown ? i++ : i--) {
|
|
246
245
|
const item = items[i], itemTrackingProperty = item[idPropName];
|
|
247
246
|
if (this._trackMap) {
|
|
248
|
-
const diId = this._trackMap[itemTrackingProperty];
|
|
249
247
|
if (this._trackMap.hasOwnProperty(itemTrackingProperty)) {
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
if (
|
|
248
|
+
const diId = this._trackMap[itemTrackingProperty], compIndex = this._displayObjectIndexMapById[diId], comp = components[compIndex];
|
|
249
|
+
const compId = (_a = comp === null || comp === void 0 ? void 0 : comp.instance) === null || _a === void 0 ? void 0 : _a.id;
|
|
250
|
+
if (comp !== undefined && compId == diId) {
|
|
253
251
|
const indexByUntrackedItems = untrackedItems.findIndex(v => {
|
|
254
|
-
return v.instance.id
|
|
252
|
+
return v.instance.id == compId;
|
|
255
253
|
});
|
|
256
254
|
if (indexByUntrackedItems > -1) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (afterComponentSetup !== undefined) {
|
|
260
|
-
afterComponentSetup(el.instance, item);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
255
|
+
comp.instance.item = item;
|
|
256
|
+
comp.instance.show();
|
|
263
257
|
untrackedItems.splice(indexByUntrackedItems, 1);
|
|
264
258
|
continue;
|
|
265
259
|
}
|
|
@@ -270,20 +264,18 @@ class Tracker {
|
|
|
270
264
|
if (untrackedItems.length > 0) {
|
|
271
265
|
const el = untrackedItems.shift(), item = items[i];
|
|
272
266
|
if (el) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
this._trackMap[itemTrackingProperty] = el.instance.id;
|
|
277
|
-
}
|
|
278
|
-
if (afterComponentSetup !== undefined) {
|
|
279
|
-
afterComponentSetup(el.instance, item);
|
|
280
|
-
}
|
|
267
|
+
el.instance.item = item;
|
|
268
|
+
if (this._trackMap) {
|
|
269
|
+
this._trackMap[itemTrackingProperty] = el.instance.id;
|
|
281
270
|
}
|
|
282
271
|
}
|
|
283
272
|
}
|
|
284
273
|
}
|
|
285
274
|
if (untrackedItems.length) {
|
|
286
|
-
|
|
275
|
+
for (let i = 0, l = untrackedItems.length; i < l; i++) {
|
|
276
|
+
const comp = untrackedItems[i];
|
|
277
|
+
comp.instance.hide();
|
|
278
|
+
}
|
|
287
279
|
}
|
|
288
280
|
}
|
|
289
281
|
untrackComponentByIdProperty(component) {
|
|
@@ -406,7 +398,7 @@ class EventEmitter {
|
|
|
406
398
|
}
|
|
407
399
|
}
|
|
408
400
|
|
|
409
|
-
const MAX_SCROLL_DIRECTION_POOL =
|
|
401
|
+
const MAX_SCROLL_DIRECTION_POOL = 50, CLEAR_SCROLL_DIRECTION_TO = 10;
|
|
410
402
|
/**
|
|
411
403
|
* Cache map.
|
|
412
404
|
* Emits a change event on each mutation.
|
|
@@ -454,15 +446,23 @@ class CacheMap extends EventEmitter {
|
|
|
454
446
|
this._scrollDirectionCache.shift();
|
|
455
447
|
}
|
|
456
448
|
this._scrollDirectionCache.push(v);
|
|
457
|
-
const dict = { [-1]: 0, [0]: 0, [1]: 0 };
|
|
458
|
-
for (let i = 0, l = this._scrollDirectionCache.length; i < l; i++) {
|
|
459
|
-
const dir = this._scrollDirectionCache[i];
|
|
449
|
+
const dict = { ['-1']: 0, ['0']: 0, ['1']: 0 };
|
|
450
|
+
for (let i = 0, l = this._scrollDirectionCache.length, li = l - 1; i < l; i++) {
|
|
451
|
+
const dir = String(this._scrollDirectionCache[i]);
|
|
460
452
|
dict[dir] += 1;
|
|
453
|
+
if (i === li) {
|
|
454
|
+
for (let d in dict) {
|
|
455
|
+
if (d === String(v)) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
dict[d] -= 1;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
461
|
}
|
|
462
|
-
if (dict[-1] > dict[0] && dict[-1] > dict[1]) {
|
|
462
|
+
if (dict['-1'] > dict['0'] && dict['-1'] > dict['1']) {
|
|
463
463
|
return -1;
|
|
464
464
|
}
|
|
465
|
-
else if (dict[1] > dict[-1] && dict[1] > dict[0]) {
|
|
465
|
+
else if (dict['1'] > dict['-1'] && dict['1'] > dict['0']) {
|
|
466
466
|
return 1;
|
|
467
467
|
}
|
|
468
468
|
return 0;
|
|
@@ -716,12 +716,34 @@ class TrackBox extends CacheMap {
|
|
|
716
716
|
recalculateMetrics(options) {
|
|
717
717
|
var _a;
|
|
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;
|
|
@@ -993,7 +1023,7 @@ class TrackBox extends CacheMap {
|
|
|
993
1023
|
if (!this._items || !this._displayComponents) {
|
|
994
1024
|
return;
|
|
995
1025
|
}
|
|
996
|
-
this._tracker.track(this._items, this._displayComponents);
|
|
1026
|
+
this._tracker.track(this._items, this._displayComponents, this.scrollDirection);
|
|
997
1027
|
}
|
|
998
1028
|
setDisplayObjectIndexMapById(v) {
|
|
999
1029
|
this._tracker.displayObjectIndexMapById = v;
|
|
@@ -1129,8 +1159,6 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1129
1159
|
};
|
|
1130
1160
|
this._$snap = new BehaviorSubject(DEFAULT_SNAP);
|
|
1131
1161
|
this.$snap = this._$snap.asObservable();
|
|
1132
|
-
this._$snapToItem = new BehaviorSubject(DEFAULT_SNAP_TO_ITEM);
|
|
1133
|
-
this.$snapToItem = this._$snapToItem.asObservable();
|
|
1134
1162
|
this._$enabledBufferOptimization = new BehaviorSubject(DEFAULT_ENABLED_BUFFER_OPTIMIZATION);
|
|
1135
1163
|
this.$enabledBufferOptimization = this._$enabledBufferOptimization.asObservable();
|
|
1136
1164
|
this._$itemRenderer = new BehaviorSubject(undefined);
|
|
@@ -1158,81 +1186,22 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1158
1186
|
this._displayComponents = [];
|
|
1159
1187
|
this._$bounds = new BehaviorSubject(null);
|
|
1160
1188
|
this._$scrollSize = new BehaviorSubject(0);
|
|
1161
|
-
this._isScrollingDebounces = debounce((v) => {
|
|
1162
|
-
this._isScrolling = v;
|
|
1163
|
-
}, 250);
|
|
1164
|
-
this._isScrolling = false;
|
|
1165
1189
|
this._resizeObserver = null;
|
|
1166
1190
|
this._onResizeHandler = () => {
|
|
1167
1191
|
var _a, _b, _c;
|
|
1168
1192
|
this._$bounds.next((_c = (_b = (_a = this._container) === null || _a === void 0 ? void 0 : _a.nativeElement) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect()) !== null && _c !== void 0 ? _c : null);
|
|
1169
1193
|
};
|
|
1170
|
-
this._scrolls = new Map();
|
|
1171
1194
|
this._onScrollHandler = (e) => {
|
|
1172
1195
|
var _a;
|
|
1173
|
-
this._isScrollingDebounces.dispose();
|
|
1174
1196
|
this.clearScrollToRepeatExecutionTimeout();
|
|
1175
1197
|
const container = (_a = this._container) === null || _a === void 0 ? void 0 : _a.nativeElement;
|
|
1176
1198
|
if (container) {
|
|
1177
1199
|
const dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft);
|
|
1178
|
-
let actualScrollSize = scrollSize, isScrollIUmmediate = false;
|
|
1179
|
-
if (dynamicSize && delta !== 0) {
|
|
1180
|
-
actualScrollSize = scrollSize + delta;
|
|
1181
|
-
const params = {
|
|
1182
|
-
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1183
|
-
behavior: BEHAVIOR_INSTANT
|
|
1184
|
-
};
|
|
1185
|
-
const container = this._container;
|
|
1186
|
-
if (container) {
|
|
1187
|
-
isScrollIUmmediate = true;
|
|
1188
|
-
this.scrollImmediately(container, params);
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
if (!isScrollIUmmediate) {
|
|
1192
|
-
this._$scrollSize.next(actualScrollSize);
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
};
|
|
1196
|
-
this._onScrollEndHandler = (e) => {
|
|
1197
|
-
const container = this._container;
|
|
1198
|
-
if (container) {
|
|
1199
|
-
const itemSize = this.itemSize, snapToItem = this.snapToItem, dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
|
|
1200
1200
|
let actualScrollSize = scrollSize;
|
|
1201
|
-
|
|
1202
|
-
if (dynamicSize) {
|
|
1201
|
+
if (dynamicSize && delta !== 0) {
|
|
1203
1202
|
actualScrollSize = scrollSize + delta;
|
|
1204
|
-
if (snapToItem) {
|
|
1205
|
-
const items = this.items, isVertical = this._isVertical, targetItem = this._trackBox.getNearestItem(actualScrollSize, items, itemSize, isVertical);
|
|
1206
|
-
if (targetItem) {
|
|
1207
|
-
this.scrollTo(targetItem.id, BEHAVIOR_INSTANT);
|
|
1208
|
-
}
|
|
1209
|
-
this._trackBox.clearDelta();
|
|
1210
|
-
}
|
|
1211
|
-
else if (scrollSize !== actualScrollSize) {
|
|
1212
|
-
const params = {
|
|
1213
|
-
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1214
|
-
behavior: BEHAVIOR_INSTANT
|
|
1215
|
-
};
|
|
1216
|
-
const container = this._container;
|
|
1217
|
-
if (container) {
|
|
1218
|
-
this.scrollImmediately(container, params);
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
else {
|
|
1223
|
-
const scrollItems = Math.round(scrollSize / itemSize);
|
|
1224
|
-
actualScrollSize = snapToItem ? scrollItems * itemSize : scrollSize;
|
|
1225
|
-
if (scrollSize !== actualScrollSize) {
|
|
1226
|
-
const params = {
|
|
1227
|
-
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1228
|
-
behavior: BEHAVIOR_INSTANT
|
|
1229
|
-
};
|
|
1230
|
-
const container = this._container;
|
|
1231
|
-
if (container) {
|
|
1232
|
-
this.scrollImmediately(container, params);
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
1203
|
}
|
|
1204
|
+
this._$scrollSize.next(actualScrollSize);
|
|
1236
1205
|
}
|
|
1237
1206
|
};
|
|
1238
1207
|
this._$initialized = new BehaviorSubject(false);
|
|
@@ -1248,7 +1217,6 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1248
1217
|
this._trackBox.changes();
|
|
1249
1218
|
});
|
|
1250
1219
|
this._onContainerScrollHandler = (e) => {
|
|
1251
|
-
this._isScrolling = true;
|
|
1252
1220
|
const containerEl = this._container;
|
|
1253
1221
|
if (containerEl) {
|
|
1254
1222
|
const scrollSize = (this._isVertical ? containerEl.nativeElement.scrollTop : containerEl.nativeElement.scrollLeft);
|
|
@@ -1262,7 +1230,6 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1262
1230
|
}
|
|
1263
1231
|
};
|
|
1264
1232
|
this._onContainerScrollEndHandler = (e) => {
|
|
1265
|
-
this._isScrollingDebounces.execute(false);
|
|
1266
1233
|
const containerEl = this._container;
|
|
1267
1234
|
if (containerEl) {
|
|
1268
1235
|
const scrollSize = (this._isVertical ? containerEl.nativeElement.scrollTop : containerEl.nativeElement.scrollLeft);
|
|
@@ -1285,7 +1252,7 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1285
1252
|
$trackBy.pipe(takeUntil(this._$unsubscribe), tap(v => {
|
|
1286
1253
|
this._trackBox.trackingPropertyName = v;
|
|
1287
1254
|
})).subscribe();
|
|
1288
|
-
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, $
|
|
1255
|
+
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;
|
|
1289
1256
|
$isVertical.pipe(takeUntil(this._$unsubscribe), tap(v => {
|
|
1290
1257
|
this._isVertical = v;
|
|
1291
1258
|
const el = this._elementRef.nativeElement;
|
|
@@ -1295,10 +1262,11 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1295
1262
|
this.listenCacheChangesIfNeed(dynamicSize);
|
|
1296
1263
|
})).subscribe();
|
|
1297
1264
|
combineLatest([this.$initialized, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
|
|
1298
|
-
$itemsOffset, $snap, $
|
|
1299
|
-
]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(),
|
|
1265
|
+
$itemsOffset, $snap, $isVertical, $dynamicSize, $enabledBufferOptimization, $cacheVersion,
|
|
1266
|
+
]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, enabledBufferOptimization, cacheVersion,]) => {
|
|
1267
|
+
var _a, _b, _c, _d;
|
|
1300
1268
|
const { width, height } = bounds;
|
|
1301
|
-
let actualScrollSize =
|
|
1269
|
+
let actualScrollSize = (_d = (this._isVertical ? (_b = (_a = this._container) === null || _a === void 0 ? void 0 : _a.nativeElement.scrollTop) !== null && _b !== void 0 ? _b : 0 : (_c = this._container) === null || _c === void 0 ? void 0 : _c.nativeElement.scrollLeft)) !== null && _d !== void 0 ? _d : 0;
|
|
1302
1270
|
const opts = {
|
|
1303
1271
|
bounds: { width, height }, collection: items, dynamicSize, isVertical, itemSize,
|
|
1304
1272
|
itemsOffset, scrollSize: scrollSize, snap, enabledBufferOptimization,
|
|
@@ -1308,21 +1276,29 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1308
1276
|
this.createDisplayComponentsIfNeed(displayItems);
|
|
1309
1277
|
this.tracking();
|
|
1310
1278
|
const container = this._container;
|
|
1311
|
-
if (
|
|
1312
|
-
actualScrollSize =
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
if (
|
|
1316
|
-
|
|
1279
|
+
if (container) {
|
|
1280
|
+
actualScrollSize = actualScrollSize + this._trackBox.delta;
|
|
1281
|
+
this._trackBox.clearDelta();
|
|
1282
|
+
if (dynamicSize) {
|
|
1283
|
+
if (scrollSize !== actualScrollSize) {
|
|
1284
|
+
const params = {
|
|
1285
|
+
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1286
|
+
behavior: BEHAVIOR_INSTANT
|
|
1287
|
+
};
|
|
1288
|
+
container.nativeElement.scrollTo(params);
|
|
1317
1289
|
}
|
|
1318
1290
|
}
|
|
1319
|
-
else
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1291
|
+
else {
|
|
1292
|
+
if (scrollSize !== actualScrollSize) {
|
|
1293
|
+
const params = {
|
|
1294
|
+
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1295
|
+
behavior: BEHAVIOR_INSTANT
|
|
1296
|
+
};
|
|
1297
|
+
const container = this._container;
|
|
1298
|
+
if (container) {
|
|
1299
|
+
container.nativeElement.scrollTo(params);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1326
1302
|
}
|
|
1327
1303
|
}
|
|
1328
1304
|
return of(displayItems);
|
|
@@ -1361,18 +1337,7 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1361
1337
|
;
|
|
1362
1338
|
get snap() { return this._$snap.getValue(); }
|
|
1363
1339
|
/**
|
|
1364
|
-
*
|
|
1365
|
-
*/
|
|
1366
|
-
set snapToItem(v) {
|
|
1367
|
-
if (this._$snapToItem.getValue() === v) {
|
|
1368
|
-
return;
|
|
1369
|
-
}
|
|
1370
|
-
this._$snapToItem.next(v);
|
|
1371
|
-
this._cdr.markForCheck();
|
|
1372
|
-
}
|
|
1373
|
-
;
|
|
1374
|
-
get snapToItem() { return this._$snapToItem.getValue(); }
|
|
1375
|
-
/**
|
|
1340
|
+
* Experimental!
|
|
1376
1341
|
* Enables buffer optimization.
|
|
1377
1342
|
* 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.
|
|
1378
1343
|
* Works only if the property dynamic = true
|
|
@@ -1471,31 +1436,6 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1471
1436
|
}
|
|
1472
1437
|
;
|
|
1473
1438
|
get trackBy() { return this._$trackBy.getValue(); }
|
|
1474
|
-
get isScrolling() { return this._isScrolling; }
|
|
1475
|
-
scrollImmediately(container, params, cb) {
|
|
1476
|
-
if (this._scrolls.size > 0) {
|
|
1477
|
-
container.nativeElement.scrollTo(params);
|
|
1478
|
-
return;
|
|
1479
|
-
}
|
|
1480
|
-
this._trackBox.clearDelta();
|
|
1481
|
-
container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1482
|
-
container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
|
|
1483
|
-
const handler = () => {
|
|
1484
|
-
const container = this._container;
|
|
1485
|
-
if (container) {
|
|
1486
|
-
container.nativeElement.removeEventListener(SCROLL_END, handler);
|
|
1487
|
-
this._scrolls.delete(handler);
|
|
1488
|
-
container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1489
|
-
container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
|
|
1490
|
-
if (cb !== undefined) {
|
|
1491
|
-
cb();
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
};
|
|
1495
|
-
this._scrolls.set(handler, true);
|
|
1496
|
-
container.nativeElement.addEventListener(SCROLL_END, handler);
|
|
1497
|
-
container.nativeElement.scrollTo(params);
|
|
1498
|
-
}
|
|
1499
1439
|
get $cacheVersion() { return this._$cacheVersion.asObservable(); }
|
|
1500
1440
|
ngOnInit() {
|
|
1501
1441
|
this._$initialized.next(true);
|
|
@@ -1517,32 +1457,20 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1517
1457
|
return isDirection(dir, Directions.VERTICAL);
|
|
1518
1458
|
}
|
|
1519
1459
|
createDisplayComponentsIfNeed(displayItems) {
|
|
1520
|
-
var _a;
|
|
1521
1460
|
if (!displayItems || !this._listContainerRef) {
|
|
1522
1461
|
this._trackBox.setDisplayObjectIndexMapById({});
|
|
1523
1462
|
return;
|
|
1524
1463
|
}
|
|
1525
1464
|
this._trackBox.items = displayItems;
|
|
1526
1465
|
const _listContainerRef = this._listContainerRef;
|
|
1527
|
-
|
|
1466
|
+
const maxLength = displayItems.length, components = this._displayComponents;
|
|
1467
|
+
while (components.length < maxLength) {
|
|
1528
1468
|
if (_listContainerRef) {
|
|
1529
1469
|
const comp = _listContainerRef.createComponent(NgVirtualListItemComponent);
|
|
1530
|
-
|
|
1470
|
+
components.push(comp);
|
|
1531
1471
|
this._componentsResizeObserver.observe(comp.instance.element);
|
|
1532
1472
|
}
|
|
1533
1473
|
}
|
|
1534
|
-
const maxLength = displayItems.length;
|
|
1535
|
-
while (this._displayComponents.length > maxLength) {
|
|
1536
|
-
const comp = this._displayComponents.pop();
|
|
1537
|
-
if (comp) {
|
|
1538
|
-
this._componentsResizeObserver.unobserve(comp.instance.element);
|
|
1539
|
-
comp.destroy();
|
|
1540
|
-
const id = (_a = comp === null || comp === void 0 ? void 0 : comp.instance.item) === null || _a === void 0 ? void 0 : _a.id;
|
|
1541
|
-
if (id !== undefined) {
|
|
1542
|
-
this._trackBox.untrackComponentByIdProperty(comp === null || comp === void 0 ? void 0 : comp.instance);
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
1474
|
this.resetRenderers();
|
|
1547
1475
|
}
|
|
1548
1476
|
resetRenderers(itemRenderer) {
|
|
@@ -1582,51 +1510,49 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1582
1510
|
clearScrollToRepeatExecutionTimeout() {
|
|
1583
1511
|
clearTimeout(this._scrollToRepeatExecutionTimeout);
|
|
1584
1512
|
}
|
|
1585
|
-
scrollToExecutor(id, behavior, iteration = 0) {
|
|
1586
|
-
this._isScrolling = true;
|
|
1587
|
-
this.clearScrollToRepeatExecutionTimeout();
|
|
1513
|
+
scrollToExecutor(id, behavior, iteration = 0, isLastIteration = false) {
|
|
1588
1514
|
const items = this.items;
|
|
1589
1515
|
if (!items || !items.length) {
|
|
1590
1516
|
return;
|
|
1591
1517
|
}
|
|
1592
1518
|
const dynamicSize = this.dynamicSize, container = this._container, itemSize = this.itemSize;
|
|
1593
1519
|
if (container) {
|
|
1520
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1594
1521
|
if (dynamicSize) {
|
|
1595
1522
|
if (container) {
|
|
1596
1523
|
container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
|
|
1597
|
-
container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1598
1524
|
}
|
|
1599
|
-
const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, isVertical = this._isVertical, opts = {
|
|
1525
|
+
const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, isVertical = this._isVertical, delta = this._trackBox.delta, opts = {
|
|
1600
1526
|
bounds: { width, height }, collection: items, dynamicSize, isVertical: this._isVertical, itemSize,
|
|
1601
|
-
itemsOffset: this.itemsOffset, scrollSize: isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
|
|
1527
|
+
itemsOffset: this.itemsOffset, scrollSize: (isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft) + delta,
|
|
1602
1528
|
snap: this.snap, fromItemId: id, enabledBufferOptimization: this.enabledBufferOptimization,
|
|
1603
1529
|
}, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
|
|
1604
|
-
this.
|
|
1530
|
+
this._trackBox.clearDelta();
|
|
1605
1531
|
if (container) {
|
|
1606
|
-
const
|
|
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);
|
|
1532
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, Object.assign(Object.assign({}, opts), { scrollSize, fromItemId: isLastIteration ? undefined : id })), 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, Object.assign(Object.assign({}, 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: [{
|