ng-virtual-list 20.0.22 → 20.0.24
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 +7 -8
- package/fesm2022/ng-virtual-list.mjs +131 -198
- package/fesm2022/ng-virtual-list.mjs.map +1 -1
- package/index.d.ts +4 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -449,10 +449,9 @@ Inputs
|
|
|
449
449
|
| itemRenderer | TemplateRef | Rendering element template. |
|
|
450
450
|
| stickyMap | [IVirtualListStickyMap?](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/models/sticky-map.model.ts) | Dictionary zIndex by id of the list element. If the value is not set or equal to 0, then a simple element is displayed, if the value is greater than 0, then the sticky position mode is enabled for the element. |
|
|
451
451
|
| snap | boolean? = false | Determines whether elements will snap. Default value is "false". |
|
|
452
|
-
| snapToItem | boolean? = false | Determines whether scroll positions will be snapped to the element. Default value is "false". |
|
|
453
452
|
| direction | [Direction? = 'vertical'](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/enums/direction.ts) | Determines the direction in which elements are placed. Default value is "vertical". |
|
|
454
453
|
| dynamicSize | boolean? = false | If true then the items in the list can have different sizes and the itemSize property is ignored. If false then the items in the list have a fixed size specified by the itemSize property. The default value is false. |
|
|
455
|
-
| enabledBufferOptimization | boolean? = true | Enables buffer optimization. Can only be used if items in the collection are not added or updated. |
|
|
454
|
+
| enabledBufferOptimization | boolean? = true | Experimental! Enables buffer optimization. Can only be used if items in the collection are not added or updated. |
|
|
456
455
|
| trackBy | string? = 'id' | The name of the property by which tracking is performed. |
|
|
457
456
|
|
|
458
457
|
<br/>
|
|
@@ -478,12 +477,12 @@ Methods
|
|
|
478
477
|
|
|
479
478
|
| Angular version | ng-virtual-list version | git | npm |
|
|
480
479
|
|--|--|--|--|
|
|
481
|
-
| 19.x | 19.1.
|
|
482
|
-
| 18.x | 18.0.
|
|
483
|
-
| 17.x | 17.0.
|
|
484
|
-
| 16.x | 16.0.
|
|
485
|
-
| 15.x | 15.0.
|
|
486
|
-
| 14.x | 14.0.
|
|
480
|
+
| 19.x | 19.1.28 | [19.x](https://github.com/DjonnyX/ng-virtual-list/tree/19.x) | [19.1.28](https://www.npmjs.com/package/ng-virtual-list/v/19.1.28) |
|
|
481
|
+
| 18.x | 18.0.16 | [18.x](https://github.com/DjonnyX/ng-virtual-list/tree/18.x) | [18.0.16](https://www.npmjs.com/package/ng-virtual-list/v/18.0.16) |
|
|
482
|
+
| 17.x | 17.0.13 | [17.x](https://github.com/DjonnyX/ng-virtual-list/tree/17.x) | [17.0.13](https://www.npmjs.com/package/ng-virtual-list/v/17.0.13) |
|
|
483
|
+
| 16.x | 16.0.16 | [16.x](https://github.com/DjonnyX/ng-virtual-list/tree/16.x) | [16.0.16](https://www.npmjs.com/package/ng-virtual-list/v/16.0.16) |
|
|
484
|
+
| 15.x | 15.0.14 | [15.x](https://github.com/DjonnyX/ng-virtual-list/tree/15.x) | [15.0.14](https://www.npmjs.com/package/ng-virtual-list/v/15.0.14) |
|
|
485
|
+
| 14.x | 14.0.14 | [14.x](https://github.com/DjonnyX/ng-virtual-list/tree/14.x) | [14.0.14](https://www.npmjs.com/package/ng-virtual-list/v/14.0.14) |
|
|
487
486
|
|
|
488
487
|
<br/>
|
|
489
488
|
|
|
@@ -27,8 +27,7 @@ const DEFAULT_ITEM_SIZE = 24;
|
|
|
27
27
|
const DEFAULT_ITEMS_OFFSET = 2;
|
|
28
28
|
const DEFAULT_LIST_SIZE = 400;
|
|
29
29
|
const DEFAULT_SNAP = false;
|
|
30
|
-
const DEFAULT_ENABLED_BUFFER_OPTIMIZATION =
|
|
31
|
-
const DEFAULT_SNAP_TO_ITEM = false;
|
|
30
|
+
const DEFAULT_ENABLED_BUFFER_OPTIMIZATION = false;
|
|
32
31
|
const DEFAULT_DYNAMIC_SIZE = false;
|
|
33
32
|
const TRACK_BY_PROPERTY_NAME = 'id';
|
|
34
33
|
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,29 +235,24 @@ class Tracker {
|
|
|
236
235
|
/**
|
|
237
236
|
* tracking by propName
|
|
238
237
|
*/
|
|
239
|
-
track(items, components,
|
|
238
|
+
track(items, components, direction) {
|
|
240
239
|
if (!items) {
|
|
241
240
|
return;
|
|
242
241
|
}
|
|
243
|
-
const idPropName = this._trackingPropertyName, untrackedItems = [...components];
|
|
244
|
-
for (let i = 0, l = items.length; i < l; i++) {
|
|
242
|
+
const idPropName = this._trackingPropertyName, untrackedItems = [...components], isDown = direction === 0 || direction === 1;
|
|
243
|
+
for (let i = isDown ? 0 : items.length - 1, l = isDown ? items.length : 0; isDown ? i < l : i >= l; isDown ? i++ : i--) {
|
|
245
244
|
const item = items[i], itemTrackingProperty = item[idPropName];
|
|
246
245
|
if (this._trackMap) {
|
|
247
|
-
const diId = this._trackMap[itemTrackingProperty];
|
|
248
246
|
if (this._trackMap.hasOwnProperty(itemTrackingProperty)) {
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
if (
|
|
247
|
+
const diId = this._trackMap[itemTrackingProperty], compIndex = this._displayObjectIndexMapById[diId], comp = components[compIndex];
|
|
248
|
+
const compId = comp?.instance?.id;
|
|
249
|
+
if (comp !== undefined && compId == diId) {
|
|
252
250
|
const indexByUntrackedItems = untrackedItems.findIndex(v => {
|
|
253
|
-
return v.instance.id
|
|
251
|
+
return v.instance.id == compId;
|
|
254
252
|
});
|
|
255
253
|
if (indexByUntrackedItems > -1) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if (afterComponentSetup !== undefined) {
|
|
259
|
-
afterComponentSetup(el.instance, item);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
254
|
+
comp.instance.item = item;
|
|
255
|
+
comp.instance.show();
|
|
262
256
|
untrackedItems.splice(indexByUntrackedItems, 1);
|
|
263
257
|
continue;
|
|
264
258
|
}
|
|
@@ -269,20 +263,18 @@ class Tracker {
|
|
|
269
263
|
if (untrackedItems.length > 0) {
|
|
270
264
|
const el = untrackedItems.shift(), item = items[i];
|
|
271
265
|
if (el) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
this._trackMap[itemTrackingProperty] = el.instance.id;
|
|
276
|
-
}
|
|
277
|
-
if (afterComponentSetup !== undefined) {
|
|
278
|
-
afterComponentSetup(el.instance, item);
|
|
279
|
-
}
|
|
266
|
+
el.instance.item = item;
|
|
267
|
+
if (this._trackMap) {
|
|
268
|
+
this._trackMap[itemTrackingProperty] = el.instance.id;
|
|
280
269
|
}
|
|
281
270
|
}
|
|
282
271
|
}
|
|
283
272
|
}
|
|
284
273
|
if (untrackedItems.length) {
|
|
285
|
-
|
|
274
|
+
for (let i = 0, l = untrackedItems.length; i < l; i++) {
|
|
275
|
+
const comp = untrackedItems[i];
|
|
276
|
+
comp.instance.hide();
|
|
277
|
+
}
|
|
286
278
|
}
|
|
287
279
|
}
|
|
288
280
|
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;
|
|
@@ -719,12 +719,34 @@ class TrackBox extends CacheMap {
|
|
|
719
719
|
*/
|
|
720
720
|
recalculateMetrics(options) {
|
|
721
721
|
const { fromItemId, bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, stickyMap, enabledBufferOptimization } = options;
|
|
722
|
-
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,
|
|
722
|
+
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)
|
|
723
723
|
|| (typeof fromItemId === 'string' && fromItemId > '-1');
|
|
724
|
+
let leftItemsOffset = 0, rightItemsOffset = 0;
|
|
725
|
+
if (enabledBufferOptimization) {
|
|
726
|
+
switch (this.scrollDirection) {
|
|
727
|
+
case 1: {
|
|
728
|
+
leftItemsOffset = 0;
|
|
729
|
+
rightItemsOffset = itemsOffset;
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
732
|
+
case -1: {
|
|
733
|
+
leftItemsOffset = itemsOffset;
|
|
734
|
+
rightItemsOffset = 0;
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
case 0:
|
|
738
|
+
default: {
|
|
739
|
+
leftItemsOffset = rightItemsOffset = itemsOffset;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
leftItemsOffset = rightItemsOffset = itemsOffset;
|
|
745
|
+
}
|
|
724
746
|
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;
|
|
725
747
|
// If the list is dynamic or there are new elements in the collection, then it switches to the long algorithm.
|
|
726
748
|
if (dynamicSize) {
|
|
727
|
-
let y = 0, stickyCollectionItem = undefined, stickyComponentSize = 0;
|
|
749
|
+
let y = 0, stickyCollectionItem = undefined, stickyComponentSize = 0, stickyComponentIndex = -1;
|
|
728
750
|
for (let i = 0, l = collection.length; i < l; i++) {
|
|
729
751
|
const ii = i + 1, collectionItem = collection[i], id = collectionItem.id;
|
|
730
752
|
let componentSize = 0, componentSizeDelta = 0, itemDisplayMethod = ItemDisplayMethods.NOT_CHANGED;
|
|
@@ -755,18 +777,26 @@ class TrackBox extends CacheMap {
|
|
|
755
777
|
if (id !== fromItemId && stickyMap && stickyMap[id] > 0) {
|
|
756
778
|
stickyComponentSize = componentSize;
|
|
757
779
|
stickyCollectionItem = collectionItem;
|
|
780
|
+
stickyComponentIndex = i;
|
|
758
781
|
}
|
|
759
782
|
if (id === fromItemId) {
|
|
760
783
|
targetDisplayItemIndex = i;
|
|
761
|
-
if (stickyCollectionItem && stickyMap
|
|
784
|
+
if (stickyCollectionItem && stickyMap) {
|
|
762
785
|
const { num } = this.getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical);
|
|
763
786
|
if (num > 0) {
|
|
764
787
|
isTargetInOverscroll = true;
|
|
765
788
|
y -= size - componentSize;
|
|
766
789
|
}
|
|
767
790
|
else {
|
|
768
|
-
y
|
|
769
|
-
|
|
791
|
+
if (stickyMap && !stickyMap[collectionItem.id] && y >= scrollSize && y < scrollSize + stickyComponentSize) {
|
|
792
|
+
const snappedY = scrollSize - stickyComponentSize;
|
|
793
|
+
leftHiddenItemsWeight -= (snappedY - y);
|
|
794
|
+
y = snappedY;
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
y -= stickyComponentSize;
|
|
798
|
+
leftHiddenItemsWeight -= stickyComponentSize;
|
|
799
|
+
}
|
|
770
800
|
}
|
|
771
801
|
}
|
|
772
802
|
itemById = collectionItem;
|
|
@@ -996,7 +1026,7 @@ class TrackBox extends CacheMap {
|
|
|
996
1026
|
if (!this._items || !this._displayComponents) {
|
|
997
1027
|
return;
|
|
998
1028
|
}
|
|
999
|
-
this._tracker.track(this._items, this._displayComponents);
|
|
1029
|
+
this._tracker.track(this._items, this._displayComponents, this.scrollDirection);
|
|
1000
1030
|
}
|
|
1001
1031
|
setDisplayObjectIndexMapById(v) {
|
|
1002
1032
|
this._tracker.displayObjectIndexMapById = v;
|
|
@@ -1119,10 +1149,7 @@ class NgVirtualListComponent {
|
|
|
1119
1149
|
*/
|
|
1120
1150
|
snap = input(DEFAULT_SNAP);
|
|
1121
1151
|
/**
|
|
1122
|
-
*
|
|
1123
|
-
*/
|
|
1124
|
-
snapToItem = input(DEFAULT_SNAP_TO_ITEM);
|
|
1125
|
-
/**
|
|
1152
|
+
* Experimental!
|
|
1126
1153
|
* Enables buffer optimization.
|
|
1127
1154
|
* 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.
|
|
1128
1155
|
* Works only if the property dynamic = true
|
|
@@ -1168,100 +1195,18 @@ class NgVirtualListComponent {
|
|
|
1168
1195
|
_displayComponents = [];
|
|
1169
1196
|
_bounds = signal(null);
|
|
1170
1197
|
_scrollSize = signal(0);
|
|
1171
|
-
_isScrollingDebounces = debounce((v) => {
|
|
1172
|
-
this._isScrolling = v;
|
|
1173
|
-
}, 250);
|
|
1174
|
-
_isScrolling = false;
|
|
1175
|
-
get isScrolling() { return this._isScrolling; }
|
|
1176
1198
|
_resizeObserver = null;
|
|
1177
1199
|
_onResizeHandler = () => {
|
|
1178
1200
|
this._bounds.set(this._container()?.nativeElement?.getBoundingClientRect() ?? null);
|
|
1179
1201
|
};
|
|
1180
1202
|
_onScrollHandler = (e) => {
|
|
1181
|
-
this._isScrollingDebounces.dispose();
|
|
1182
1203
|
this.clearScrollToRepeatExecutionTimeout();
|
|
1183
1204
|
const container = this._container()?.nativeElement;
|
|
1184
1205
|
if (container) {
|
|
1185
1206
|
const dynamicSize = this.dynamicSize(), delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft);
|
|
1186
|
-
let actualScrollSize = scrollSize,
|
|
1207
|
+
let actualScrollSize = scrollSize, isScrollIUmmediate = false;
|
|
1187
1208
|
if (dynamicSize && delta !== 0) {
|
|
1188
1209
|
actualScrollSize = scrollSize + delta;
|
|
1189
|
-
const params = {
|
|
1190
|
-
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1191
|
-
behavior: BEHAVIOR_INSTANT
|
|
1192
|
-
};
|
|
1193
|
-
const container = this._container();
|
|
1194
|
-
if (container) {
|
|
1195
|
-
isImmediateScroll = true;
|
|
1196
|
-
this.scrollImmediately(container, params);
|
|
1197
|
-
this._trackBox.clearDelta();
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
this._scrollSize.set(actualScrollSize);
|
|
1201
|
-
}
|
|
1202
|
-
};
|
|
1203
|
-
scrollImmediately(container, params, cb) {
|
|
1204
|
-
this.clearScrollImmediately();
|
|
1205
|
-
container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1206
|
-
const handler = () => {
|
|
1207
|
-
if (container) {
|
|
1208
|
-
container.nativeElement.removeEventListener(SCROLL_END, handler);
|
|
1209
|
-
container.nativeElement.scroll(params);
|
|
1210
|
-
if (cb !== undefined) {
|
|
1211
|
-
cb();
|
|
1212
|
-
}
|
|
1213
|
-
container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1214
|
-
}
|
|
1215
|
-
};
|
|
1216
|
-
container.nativeElement.addEventListener(SCROLL_END, handler);
|
|
1217
|
-
container.nativeElement.scroll(params);
|
|
1218
|
-
this._scrollImmediatelyHandler = handler;
|
|
1219
|
-
}
|
|
1220
|
-
_scrollImmediatelyHandler = undefined;
|
|
1221
|
-
clearScrollImmediately() {
|
|
1222
|
-
if (this._scrollImmediatelyHandler === undefined) {
|
|
1223
|
-
return;
|
|
1224
|
-
}
|
|
1225
|
-
const container = this._container();
|
|
1226
|
-
if (container) {
|
|
1227
|
-
container.nativeElement.removeEventListener(SCROLL_END, this._scrollImmediatelyHandler);
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
_onScrollEndHandler = (e) => {
|
|
1231
|
-
const container = this._container();
|
|
1232
|
-
if (container) {
|
|
1233
|
-
this._trackBox.clearDelta();
|
|
1234
|
-
this._trackBox.clearDeltaDirection();
|
|
1235
|
-
const itemSize = this.itemSize(), snapToItem = this.snapToItem(), dynamicSize = this.dynamicSize(), delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
|
|
1236
|
-
let actualScrollSize = scrollSize;
|
|
1237
|
-
if (dynamicSize) {
|
|
1238
|
-
actualScrollSize = scrollSize + delta;
|
|
1239
|
-
if (snapToItem) {
|
|
1240
|
-
const items = this.items(), isVertical = this._isVertical, targetItem = this._trackBox.getNearestItem(actualScrollSize, items, itemSize, isVertical);
|
|
1241
|
-
if (targetItem) {
|
|
1242
|
-
this.scrollTo(targetItem.id, BEHAVIOR_INSTANT);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
else if (scrollSize !== actualScrollSize) {
|
|
1246
|
-
const params = {
|
|
1247
|
-
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1248
|
-
behavior: BEHAVIOR_INSTANT
|
|
1249
|
-
};
|
|
1250
|
-
this._scrollSize.set(actualScrollSize);
|
|
1251
|
-
container.nativeElement.scroll(params);
|
|
1252
|
-
return;
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
else {
|
|
1256
|
-
const scrollItems = Math.round(scrollSize / itemSize);
|
|
1257
|
-
actualScrollSize = snapToItem ? scrollItems * itemSize : scrollSize;
|
|
1258
|
-
if (scrollSize !== actualScrollSize) {
|
|
1259
|
-
const params = {
|
|
1260
|
-
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1261
|
-
behavior: BEHAVIOR_INSTANT
|
|
1262
|
-
};
|
|
1263
|
-
container.nativeElement.scroll(params);
|
|
1264
|
-
}
|
|
1265
1210
|
}
|
|
1266
1211
|
this._scrollSize.set(actualScrollSize);
|
|
1267
1212
|
}
|
|
@@ -1293,7 +1238,7 @@ class NgVirtualListComponent {
|
|
|
1293
1238
|
$trackBy.pipe(takeUntilDestroyed(), tap(v => {
|
|
1294
1239
|
this._trackBox.trackingPropertyName = v;
|
|
1295
1240
|
})).subscribe();
|
|
1296
|
-
const $bounds = toObservable(this._bounds).pipe(filter(b => !!b)), $items = toObservable(this.items).pipe(map(i => !i ? [] : i)), $scrollSize = toObservable(this._scrollSize), $itemSize = toObservable(this.itemSize).pipe(map(v => v <= 0 ? DEFAULT_ITEM_SIZE : v)), $itemsOffset = toObservable(this.itemsOffset).pipe(map(v => v < 0 ? DEFAULT_ITEMS_OFFSET : v)), $stickyMap = toObservable(this.stickyMap).pipe(map(v => !v ? {} : v)), $snap = toObservable(this.snap), $
|
|
1241
|
+
const $bounds = toObservable(this._bounds).pipe(filter(b => !!b)), $items = toObservable(this.items).pipe(map(i => !i ? [] : i)), $scrollSize = toObservable(this._scrollSize), $itemSize = toObservable(this.itemSize).pipe(map(v => v <= 0 ? DEFAULT_ITEM_SIZE : v)), $itemsOffset = toObservable(this.itemsOffset).pipe(map(v => v < 0 ? DEFAULT_ITEMS_OFFSET : v)), $stickyMap = toObservable(this.stickyMap).pipe(map(v => !v ? {} : v)), $snap = toObservable(this.snap), $isVertical = toObservable(this.direction).pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $dynamicSize = toObservable(this.dynamicSize), $enabledBufferOptimization = toObservable(this.enabledBufferOptimization), $cacheVersion = this.$cacheVersion;
|
|
1297
1242
|
$isVertical.pipe(takeUntilDestroyed(), tap(v => {
|
|
1298
1243
|
this._isVertical = v;
|
|
1299
1244
|
const el = this._elementRef.nativeElement;
|
|
@@ -1303,36 +1248,44 @@ class NgVirtualListComponent {
|
|
|
1303
1248
|
this.listenCacheChangesIfNeed(dynamicSize);
|
|
1304
1249
|
})).subscribe();
|
|
1305
1250
|
combineLatest([this.$initialized, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
|
|
1306
|
-
$itemsOffset, $snap, $
|
|
1307
|
-
]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap,
|
|
1251
|
+
$itemsOffset, $snap, $isVertical, $dynamicSize, $enabledBufferOptimization, $cacheVersion,
|
|
1252
|
+
]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, enabledBufferOptimization, cacheVersion,]) => {
|
|
1308
1253
|
const { width, height } = bounds;
|
|
1309
|
-
let actualScrollSize =
|
|
1254
|
+
let actualScrollSize = (this._isVertical ? this._container()?.nativeElement.scrollTop ?? 0 : this._container()?.nativeElement.scrollLeft) ?? 0;
|
|
1310
1255
|
const opts = {
|
|
1311
1256
|
bounds: { width, height }, collection: items, dynamicSize, isVertical, itemSize,
|
|
1312
1257
|
itemsOffset, scrollSize: scrollSize, snap, enabledBufferOptimization,
|
|
1313
1258
|
};
|
|
1314
|
-
const { displayItems, totalSize
|
|
1259
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
1315
1260
|
...opts, scrollSize: actualScrollSize,
|
|
1316
1261
|
});
|
|
1317
1262
|
this.resetBoundsSize(isVertical, totalSize);
|
|
1318
1263
|
this.createDisplayComponentsIfNeed(displayItems);
|
|
1319
1264
|
this.tracking();
|
|
1320
1265
|
const container = this._container();
|
|
1321
|
-
if (
|
|
1322
|
-
actualScrollSize =
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
if (
|
|
1326
|
-
|
|
1266
|
+
if (container) {
|
|
1267
|
+
actualScrollSize = actualScrollSize + this._trackBox.delta;
|
|
1268
|
+
this._trackBox.clearDelta();
|
|
1269
|
+
if (dynamicSize) {
|
|
1270
|
+
if (scrollSize !== actualScrollSize) {
|
|
1271
|
+
const params = {
|
|
1272
|
+
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1273
|
+
behavior: BEHAVIOR_INSTANT
|
|
1274
|
+
};
|
|
1275
|
+
container.nativeElement.scrollTo(params);
|
|
1327
1276
|
}
|
|
1328
1277
|
}
|
|
1329
|
-
else
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1278
|
+
else {
|
|
1279
|
+
if (scrollSize !== actualScrollSize) {
|
|
1280
|
+
const params = {
|
|
1281
|
+
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1282
|
+
behavior: BEHAVIOR_INSTANT
|
|
1283
|
+
};
|
|
1284
|
+
const container = this._container();
|
|
1285
|
+
if (container) {
|
|
1286
|
+
container.nativeElement.scrollTo(params);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1336
1289
|
}
|
|
1337
1290
|
}
|
|
1338
1291
|
return of(displayItems);
|
|
@@ -1370,25 +1323,14 @@ class NgVirtualListComponent {
|
|
|
1370
1323
|
}
|
|
1371
1324
|
this._trackBox.items = displayItems;
|
|
1372
1325
|
const _listContainerRef = this._listContainerRef;
|
|
1373
|
-
|
|
1326
|
+
const maxLength = displayItems.length, components = this._displayComponents;
|
|
1327
|
+
while (components.length < maxLength) {
|
|
1374
1328
|
if (_listContainerRef) {
|
|
1375
1329
|
const comp = _listContainerRef.createComponent(NgVirtualListItemComponent);
|
|
1376
|
-
|
|
1330
|
+
components.push(comp);
|
|
1377
1331
|
this._componentsResizeObserver.observe(comp.instance.element);
|
|
1378
1332
|
}
|
|
1379
1333
|
}
|
|
1380
|
-
const maxLength = displayItems.length;
|
|
1381
|
-
while (this._displayComponents.length > maxLength) {
|
|
1382
|
-
const comp = this._displayComponents.pop();
|
|
1383
|
-
if (comp) {
|
|
1384
|
-
this._componentsResizeObserver.unobserve(comp.instance.element);
|
|
1385
|
-
comp?.destroy();
|
|
1386
|
-
const id = comp?.instance.item?.id;
|
|
1387
|
-
if (id !== undefined) {
|
|
1388
|
-
this._trackBox.untrackComponentByIdProperty(comp?.instance);
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
1334
|
this.resetRenderers();
|
|
1393
1335
|
}
|
|
1394
1336
|
resetRenderers(itemRenderer) {
|
|
@@ -1429,58 +1371,56 @@ class NgVirtualListComponent {
|
|
|
1429
1371
|
clearScrollToRepeatExecutionTimeout() {
|
|
1430
1372
|
clearTimeout(this._scrollToRepeatExecutionTimeout);
|
|
1431
1373
|
}
|
|
1432
|
-
scrollToExecutor(id, behavior, iteration = 0) {
|
|
1433
|
-
this._isScrolling = true;
|
|
1434
|
-
this.clearScrollToRepeatExecutionTimeout();
|
|
1374
|
+
scrollToExecutor(id, behavior, iteration = 0, isLastIteration = false) {
|
|
1435
1375
|
const items = this.items();
|
|
1436
1376
|
if (!items || !items.length) {
|
|
1437
1377
|
return;
|
|
1438
1378
|
}
|
|
1439
1379
|
const dynamicSize = this.dynamicSize(), container = this._container(), itemSize = this.itemSize();
|
|
1440
1380
|
if (container) {
|
|
1381
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1441
1382
|
if (dynamicSize) {
|
|
1442
1383
|
if (container) {
|
|
1443
1384
|
container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
|
|
1444
|
-
container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1445
1385
|
}
|
|
1446
|
-
const { width, height } = this._bounds() || { width: 0, height: 0 }, stickyMap = this.stickyMap(), items = this.items(), isVertical = this._isVertical, opts = {
|
|
1386
|
+
const { width, height } = this._bounds() || { width: 0, height: 0 }, stickyMap = this.stickyMap(), items = this.items(), isVertical = this._isVertical, delta = this._trackBox.delta, opts = {
|
|
1447
1387
|
bounds: { width, height }, collection: items, dynamicSize, isVertical: this._isVertical, itemSize,
|
|
1448
|
-
itemsOffset: this.itemsOffset(), scrollSize: isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
|
|
1388
|
+
itemsOffset: this.itemsOffset(), scrollSize: (isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft) + delta,
|
|
1449
1389
|
snap: this.snap(), fromItemId: id, enabledBufferOptimization: this.enabledBufferOptimization(),
|
|
1450
1390
|
}, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
|
|
1451
|
-
this.
|
|
1391
|
+
this._trackBox.clearDelta();
|
|
1452
1392
|
if (container) {
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
}
|
|
1476
|
-
container.nativeElement.addEventListener(SCROLL_END, handler);
|
|
1393
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
1394
|
+
...opts, scrollSize, fromItemId: isLastIteration ? undefined : id,
|
|
1395
|
+
}), delta = this._trackBox.delta;
|
|
1396
|
+
this._trackBox.clearDelta();
|
|
1397
|
+
let actualScrollSize = scrollSize + delta;
|
|
1398
|
+
this.resetBoundsSize(isVertical, totalSize);
|
|
1399
|
+
this.createDisplayComponentsIfNeed(displayItems);
|
|
1400
|
+
this.tracking();
|
|
1401
|
+
const _scrollSize = this._trackBox.getItemPosition(id, stickyMap, { ...opts, scrollSize: actualScrollSize, fromItemId: id });
|
|
1402
|
+
const notChanged = actualScrollSize === _scrollSize;
|
|
1403
|
+
if (notChanged) {
|
|
1404
|
+
iteration += 1;
|
|
1405
|
+
}
|
|
1406
|
+
if (iteration < MAX_SCROLL_TO_ITERATIONS) {
|
|
1407
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1408
|
+
this._scrollToRepeatExecutionTimeout = setTimeout(() => {
|
|
1409
|
+
this.scrollToExecutor(id, BEHAVIOR_INSTANT, iteration + 1, notChanged);
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
else {
|
|
1413
|
+
this._scrollSize.set(actualScrollSize);
|
|
1414
|
+
container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
|
|
1415
|
+
}
|
|
1477
1416
|
}
|
|
1478
|
-
container.nativeElement.
|
|
1417
|
+
container.nativeElement.scrollTo(params);
|
|
1418
|
+
this._scrollSize.set(scrollSize);
|
|
1479
1419
|
}
|
|
1480
1420
|
else {
|
|
1481
1421
|
const index = items.findIndex(item => item.id === id), scrollSize = index * this.itemSize();
|
|
1482
1422
|
const params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
|
|
1483
|
-
container.nativeElement.
|
|
1423
|
+
container.nativeElement.scrollTo(params);
|
|
1484
1424
|
}
|
|
1485
1425
|
}
|
|
1486
1426
|
}
|
|
@@ -1489,7 +1429,6 @@ class NgVirtualListComponent {
|
|
|
1489
1429
|
this.scrollTo(latItem.id, behavior);
|
|
1490
1430
|
}
|
|
1491
1431
|
_onContainerScrollHandler = (e) => {
|
|
1492
|
-
this._isScrolling = true;
|
|
1493
1432
|
const containerEl = this._container();
|
|
1494
1433
|
if (containerEl) {
|
|
1495
1434
|
const scrollSize = (this._isVertical ? containerEl.nativeElement.scrollTop : containerEl.nativeElement.scrollLeft);
|
|
@@ -1503,7 +1442,6 @@ class NgVirtualListComponent {
|
|
|
1503
1442
|
}
|
|
1504
1443
|
};
|
|
1505
1444
|
_onContainerScrollEndHandler = (e) => {
|
|
1506
|
-
this._isScrollingDebounces.execute(false);
|
|
1507
1445
|
const containerEl = this._container();
|
|
1508
1446
|
if (containerEl) {
|
|
1509
1447
|
const scrollSize = (this._isVertical ? containerEl.nativeElement.scrollTop : containerEl.nativeElement.scrollLeft);
|
|
@@ -1523,7 +1461,6 @@ class NgVirtualListComponent {
|
|
|
1523
1461
|
containerEl.nativeElement.addEventListener(SCROLL, this._onContainerScrollHandler);
|
|
1524
1462
|
containerEl.nativeElement.addEventListener(SCROLL_END, this._onContainerScrollEndHandler);
|
|
1525
1463
|
containerEl.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
|
|
1526
|
-
containerEl.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1527
1464
|
this._resizeObserver = new ResizeObserver(this._onResizeHandler);
|
|
1528
1465
|
this._resizeObserver.observe(containerEl.nativeElement);
|
|
1529
1466
|
this._onResizeHandler();
|
|
@@ -1534,13 +1471,9 @@ class NgVirtualListComponent {
|
|
|
1534
1471
|
if (this._trackBox) {
|
|
1535
1472
|
this._trackBox.dispose();
|
|
1536
1473
|
}
|
|
1537
|
-
if (this._isScrollingDebounces) {
|
|
1538
|
-
this._isScrollingDebounces.dispose();
|
|
1539
|
-
}
|
|
1540
1474
|
const containerEl = this._container();
|
|
1541
1475
|
if (containerEl) {
|
|
1542
1476
|
containerEl.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
|
|
1543
|
-
containerEl.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1544
1477
|
containerEl.nativeElement.removeEventListener(SCROLL, this._onContainerScrollHandler);
|
|
1545
1478
|
containerEl.nativeElement.removeEventListener(SCROLL_END, this._onContainerScrollEndHandler);
|
|
1546
1479
|
if (this._componentsResizeObserver) {
|
|
@@ -1558,7 +1491,7 @@ class NgVirtualListComponent {
|
|
|
1558
1491
|
}
|
|
1559
1492
|
}
|
|
1560
1493
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgVirtualListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1561
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.0.4", type: NgVirtualListComponent, isStandalone: true, selector: "ng-virtual-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, snap: { classPropertyName: "snap", publicName: "snap", isSignal: true, isRequired: false, transformFunction: null },
|
|
1494
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.0.4", type: NgVirtualListComponent, isStandalone: true, selector: "ng-virtual-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, snap: { classPropertyName: "snap", publicName: "snap", isSignal: true, isRequired: false, transformFunction: null }, enabledBufferOptimization: { classPropertyName: "enabledBufferOptimization", publicName: "enabledBufferOptimization", isSignal: true, isRequired: false, transformFunction: null }, itemRenderer: { classPropertyName: "itemRenderer", publicName: "itemRenderer", isSignal: true, isRequired: true, transformFunction: null }, stickyMap: { classPropertyName: "stickyMap", publicName: "stickyMap", isSignal: true, isRequired: false, transformFunction: null }, itemSize: { classPropertyName: "itemSize", publicName: "itemSize", isSignal: true, isRequired: false, transformFunction: null }, dynamicSize: { classPropertyName: "dynamicSize", publicName: "dynamicSize", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, itemsOffset: { classPropertyName: "itemsOffset", publicName: "itemsOffset", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd" }, viewQueries: [{ propertyName: "_container", first: true, predicate: ["container"], descendants: true, isSignal: true }, { propertyName: "_list", first: true, predicate: ["list"], descendants: true, isSignal: true }, { propertyName: "_listContainerRef", first: true, predicate: ["renderersContainer"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "<div #container part=\"scroller\" class=\"ngvl__container\">\r\n <ul #list part=\"list\" class=\"ngvl__list\">\r\n <ng-container #renderersContainer></ng-container>\r\n </ul>\r\n</div>", styles: [":host{display:block;width:400px;overflow:hidden}:host(.horizontal){height:48px}:host(.vertical){height:320px}.ngvl__container{overflow:auto;width:100%;height:100%}.ngvl__list{position:relative;list-style:none;padding:0;margin:0;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
1562
1495
|
}
|
|
1563
1496
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgVirtualListComponent, decorators: [{
|
|
1564
1497
|
type: Component,
|