ng-virtual-list 17.0.0 → 17.0.2
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/LICENSE +21 -0
- package/README.md +33 -7
- package/esm2022/lib/components/ng-virtual-list-item.component.mjs +2 -2
- package/esm2022/lib/const/index.mjs +2 -1
- package/esm2022/lib/enums/direction.mjs +1 -1
- package/esm2022/lib/enums/directions.mjs +2 -2
- package/esm2022/lib/models/collection.model.mjs +1 -1
- package/esm2022/lib/models/index.mjs +1 -1
- package/esm2022/lib/models/item.model.mjs +1 -1
- package/esm2022/lib/models/render-collection.model.mjs +1 -1
- package/esm2022/lib/models/render-item-config.model.mjs +1 -1
- package/esm2022/lib/models/render-item.model.mjs +1 -1
- package/esm2022/lib/models/scroll-direction.model.mjs +2 -0
- package/esm2022/lib/models/scroll-event.model.mjs +2 -0
- package/esm2022/lib/models/sticky-map.model.mjs +1 -1
- package/esm2022/lib/ng-virtual-list.component.mjs +44 -47
- package/esm2022/lib/types/id.mjs +1 -1
- package/esm2022/lib/types/rect.mjs +1 -1
- package/esm2022/lib/types/size.mjs +1 -1
- package/esm2022/lib/utils/cacheMap.mjs +43 -2
- package/esm2022/lib/utils/debounce.mjs +2 -2
- package/esm2022/lib/utils/eventEmitter.mjs +2 -2
- package/esm2022/lib/utils/isDirection.mjs +2 -2
- package/esm2022/lib/utils/toggleClassName.mjs +2 -2
- package/esm2022/lib/utils/trackBox.mjs +56 -22
- package/esm2022/lib/utils/tracker.mjs +2 -2
- package/fesm2022/ng-virtual-list.mjs +146 -74
- package/fesm2022/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 -0
- package/lib/enums/direction.d.ts +1 -1
- package/lib/enums/directions.d.ts +1 -1
- package/lib/models/collection.model.d.ts +1 -1
- package/lib/models/index.d.ts +3 -1
- package/lib/models/item.model.d.ts +1 -1
- package/lib/models/render-collection.model.d.ts +1 -1
- package/lib/models/render-item-config.model.d.ts +1 -1
- package/lib/models/render-item.model.d.ts +1 -1
- package/lib/models/scroll-direction.model.d.ts +5 -0
- package/lib/models/scroll-event.model.d.ts +18 -0
- package/lib/models/sticky-map.model.d.ts +1 -1
- package/lib/ng-virtual-list.component.d.ts +7 -10
- package/lib/types/id.d.ts +1 -1
- package/lib/types/rect.d.ts +1 -1
- package/lib/types/size.d.ts +1 -1
- package/lib/utils/cacheMap.d.ts +11 -1
- package/lib/utils/debounce.d.ts +1 -1
- package/lib/utils/eventEmitter.d.ts +1 -1
- package/lib/utils/isDirection.d.ts +1 -1
- package/lib/utils/toggleClassName.d.ts +1 -1
- package/lib/utils/trackBox.d.ts +4 -4
- package/lib/utils/tracker.d.ts +1 -1
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ import { BehaviorSubject, filter, map, tap, combineLatest, distinctUntilChanged,
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Axis of the arrangement of virtual list elements.
|
|
10
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
10
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/enums/directions.ts
|
|
11
11
|
* @author Evgenii Grebennikov
|
|
12
12
|
* @email djonnyx@gmail.com
|
|
13
13
|
*/
|
|
@@ -32,6 +32,7 @@ const DEFAULT_DYNAMIC_SIZE = false;
|
|
|
32
32
|
const TRACK_BY_PROPERTY_NAME = 'id';
|
|
33
33
|
const DEFAULT_DIRECTION = Directions.VERTICAL;
|
|
34
34
|
const DISPLAY_OBJECTS_LENGTH_MESUREMENT_ERROR = 1;
|
|
35
|
+
const MAX_SCROLL_TO_ITERATIONS = 5;
|
|
35
36
|
// presets
|
|
36
37
|
const BEHAVIOR_AUTO = 'auto';
|
|
37
38
|
const BEHAVIOR_INSTANT = 'instant';
|
|
@@ -58,7 +59,7 @@ const CLASS_LIST_HORIZONTAL = 'horizontal';
|
|
|
58
59
|
|
|
59
60
|
/**
|
|
60
61
|
* Virtual list item component
|
|
61
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
62
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/components/ng-virtual-list-item.component.ts
|
|
62
63
|
* @author Evgenii Grebennikov
|
|
63
64
|
* @email djonnyx@gmail.com
|
|
64
65
|
*/
|
|
@@ -140,7 +141,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
140
141
|
const HORIZONTAL_ALIASES = [Directions.HORIZONTAL, 'horizontal'], VERTICAL_ALIASES = [Directions.VERTICAL, 'vertical'];
|
|
141
142
|
/**
|
|
142
143
|
* Determines the axis membership of a virtual list
|
|
143
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
144
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/isDirection.ts
|
|
144
145
|
* @author Evgenii Grebennikov
|
|
145
146
|
* @email djonnyx@gmail.com
|
|
146
147
|
*/
|
|
@@ -153,7 +154,7 @@ const isDirection = (src, expected) => {
|
|
|
153
154
|
|
|
154
155
|
/**
|
|
155
156
|
* Simple debounce function.
|
|
156
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
157
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/debounce.ts
|
|
157
158
|
* @author Evgenii Grebennikov
|
|
158
159
|
* @email djonnyx@gmail.com
|
|
159
160
|
*/
|
|
@@ -184,7 +185,7 @@ const debounce = (cb, debounceTime = 0) => {
|
|
|
184
185
|
|
|
185
186
|
/**
|
|
186
187
|
* Switch css classes
|
|
187
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
188
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/toggleClassName.ts
|
|
188
189
|
* @author Evgenii Grebennikov
|
|
189
190
|
* @email djonnyx@gmail.com
|
|
190
191
|
*/
|
|
@@ -199,7 +200,7 @@ const toggleClassName = (el, className, remove = false) => {
|
|
|
199
200
|
|
|
200
201
|
/**
|
|
201
202
|
* Tracks display items by property
|
|
202
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
203
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/tracker.ts
|
|
203
204
|
* @author Evgenii Grebennikov
|
|
204
205
|
* @email djonnyx@gmail.com
|
|
205
206
|
*/
|
|
@@ -308,7 +309,7 @@ class Tracker {
|
|
|
308
309
|
|
|
309
310
|
/**
|
|
310
311
|
* Simple event emitter
|
|
311
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
312
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/eventEmitter.ts
|
|
312
313
|
* @author Evgenii Grebennikov
|
|
313
314
|
* @email djonnyx@gmail.com
|
|
314
315
|
*/
|
|
@@ -411,10 +412,11 @@ class EventEmitter {
|
|
|
411
412
|
}
|
|
412
413
|
}
|
|
413
414
|
|
|
415
|
+
const MAX_SCROLL_DIRECTION_POOL = 10, CLEAR_SCROLL_DIRECTION_TO = 0;
|
|
414
416
|
/**
|
|
415
417
|
* Cache map.
|
|
416
418
|
* Emits a change event on each mutation.
|
|
417
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
419
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/cacheMap.ts
|
|
418
420
|
* @author Evgenii Grebennikov
|
|
419
421
|
* @email djonnyx@gmail.com
|
|
420
422
|
*/
|
|
@@ -426,12 +428,51 @@ class CacheMap extends EventEmitter {
|
|
|
426
428
|
get delta() {
|
|
427
429
|
return this._delta;
|
|
428
430
|
}
|
|
431
|
+
_deltaDirection = 1;
|
|
432
|
+
set deltaDirection(v) {
|
|
433
|
+
this._deltaDirection = v;
|
|
434
|
+
this._scrollDirection = this.calcScrollDirection(v);
|
|
435
|
+
}
|
|
436
|
+
get deltaDirection() {
|
|
437
|
+
return this._deltaDirection;
|
|
438
|
+
}
|
|
439
|
+
_scrollDirectionCache = [];
|
|
440
|
+
_scrollDirection = 1;
|
|
441
|
+
get scrollDirection() {
|
|
442
|
+
return this._scrollDirection;
|
|
443
|
+
}
|
|
429
444
|
get version() {
|
|
430
445
|
return this._version;
|
|
431
446
|
}
|
|
447
|
+
_clearScrollDirectionDebounce = debounce(() => {
|
|
448
|
+
while (this._scrollDirectionCache.length > CLEAR_SCROLL_DIRECTION_TO) {
|
|
449
|
+
this._scrollDirectionCache.shift();
|
|
450
|
+
}
|
|
451
|
+
}, 10);
|
|
432
452
|
constructor() {
|
|
433
453
|
super();
|
|
434
454
|
}
|
|
455
|
+
clearScrollDirectionCache() {
|
|
456
|
+
this._clearScrollDirectionDebounce.execute();
|
|
457
|
+
}
|
|
458
|
+
calcScrollDirection(v) {
|
|
459
|
+
while (this._scrollDirectionCache.length >= MAX_SCROLL_DIRECTION_POOL) {
|
|
460
|
+
this._scrollDirectionCache.shift();
|
|
461
|
+
}
|
|
462
|
+
this._scrollDirectionCache.push(v);
|
|
463
|
+
const dict = { [-1]: 0, [0]: 0, [1]: 0 };
|
|
464
|
+
for (let i = 0, l = this._scrollDirectionCache.length; i < l; i++) {
|
|
465
|
+
const dir = this._scrollDirectionCache[i];
|
|
466
|
+
dict[dir] += 1;
|
|
467
|
+
}
|
|
468
|
+
if (dict[-1] > dict[1]) {
|
|
469
|
+
return -1;
|
|
470
|
+
}
|
|
471
|
+
else if (dict[1] > dict[-1]) {
|
|
472
|
+
return 1;
|
|
473
|
+
}
|
|
474
|
+
return -1;
|
|
475
|
+
}
|
|
435
476
|
bumpVersion() {
|
|
436
477
|
this._version = this._version === Number.MAX_SAFE_INTEGER ? 0 : this._version + 1;
|
|
437
478
|
}
|
|
@@ -465,7 +506,7 @@ class CacheMap extends EventEmitter {
|
|
|
465
506
|
const TRACK_BOX_CHANGE_EVENT_NAME = 'change';
|
|
466
507
|
/**
|
|
467
508
|
* An object that performs tracking, calculations and caching.
|
|
468
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
509
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/utils/trackBox.ts
|
|
469
510
|
* @author Evgenii Grebennikov
|
|
470
511
|
* @email djonnyx@gmail.com
|
|
471
512
|
*/
|
|
@@ -506,12 +547,12 @@ class TrackBox extends CacheMap {
|
|
|
506
547
|
this._debounceChanges.execute(this._version);
|
|
507
548
|
}
|
|
508
549
|
getItemPosition(id, stickyMap, options) {
|
|
509
|
-
const opt = { fromItemId: id, stickyMap,
|
|
550
|
+
const opt = { fromItemId: id, stickyMap, ...options };
|
|
510
551
|
const { scrollSize } = this.recalculateMetrics(opt);
|
|
511
552
|
return scrollSize;
|
|
512
553
|
}
|
|
513
554
|
updateCollection(items, stickyMap, options) {
|
|
514
|
-
const opt = { stickyMap,
|
|
555
|
+
const opt = { stickyMap, ...options };
|
|
515
556
|
this.cacheElements();
|
|
516
557
|
const metrics = this.recalculateMetrics({
|
|
517
558
|
...opt,
|
|
@@ -520,14 +561,35 @@ class TrackBox extends CacheMap {
|
|
|
520
561
|
const displayItems = this.generateDisplayCollection(items, stickyMap, metrics);
|
|
521
562
|
return { displayItems, totalSize: metrics.totalSize, delta: metrics.delta };
|
|
522
563
|
}
|
|
564
|
+
getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical) {
|
|
565
|
+
const sizeProperty = isVertical ? HEIGHT_PROP_NAME : WIDTH_PROP_NAME;
|
|
566
|
+
let offset = 0, num = 0;
|
|
567
|
+
for (let j = collection.length - 1; j >= i; j--) {
|
|
568
|
+
const item = collection[j];
|
|
569
|
+
let itemSize = 0;
|
|
570
|
+
if (map.has(item.id)) {
|
|
571
|
+
const bounds = map.get(item.id);
|
|
572
|
+
itemSize = bounds ? bounds[sizeProperty] : typicalItemSize;
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
itemSize = typicalItemSize;
|
|
576
|
+
}
|
|
577
|
+
offset += itemSize;
|
|
578
|
+
num++;
|
|
579
|
+
if (offset > size) {
|
|
580
|
+
return { num: 0, offset };
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return { num, offset };
|
|
584
|
+
}
|
|
523
585
|
/**
|
|
524
586
|
* Calculates list metrics
|
|
525
587
|
*/
|
|
526
588
|
recalculateMetrics(options) {
|
|
527
|
-
const {
|
|
528
|
-
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,
|
|
589
|
+
const { fromItemId, bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, stickyMap } = options;
|
|
590
|
+
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, snippedPos = Math.floor(scrollSize), leftItemsWeights = [], isFromId = fromItemId !== undefined && (typeof fromItemId === 'number' && fromItemId > -1)
|
|
529
591
|
|| (typeof fromItemId === 'string' && fromItemId > '-1');
|
|
530
|
-
let itemsFromStartToScrollEnd = -1, itemsFromDisplayEndToOffsetEnd = 0, itemsFromStartToDisplayEnd = -1, leftItemLength = 0, rightItemLength = 0, leftItemsWeight = 0, rightItemsWeight = 0, leftHiddenItemsWeight = 0, totalItemsToDisplayEndWeight = 0, itemById = undefined, itemByIdPos = 0, lastDisplayItemId = undefined, actualScrollSize = itemByIdPos,
|
|
592
|
+
let itemsFromStartToScrollEnd = -1, itemsFromDisplayEndToOffsetEnd = 0, itemsFromStartToDisplayEnd = -1, leftItemLength = 0, rightItemLength = 0, leftItemsWeight = 0, rightItemsWeight = 0, leftHiddenItemsWeight = 0, totalItemsToDisplayEndWeight = 0, itemById = undefined, itemByIdPos = 0, lastDisplayItemId = undefined, actualScrollSize = itemByIdPos, totalSize = 0, startIndex;
|
|
531
593
|
if (dynamicSize) {
|
|
532
594
|
let y = 0, stickyCollectionItem = undefined, stickyComponentSize = 0;
|
|
533
595
|
for (let i = 0, l = collection.length; i < l; i++) {
|
|
@@ -540,21 +602,31 @@ class TrackBox extends CacheMap {
|
|
|
540
602
|
else {
|
|
541
603
|
componentSize = typicalItemSize;
|
|
542
604
|
}
|
|
605
|
+
totalSize += componentSize;
|
|
543
606
|
if (isFromId) {
|
|
544
607
|
if (itemById === undefined) {
|
|
545
|
-
leftItemsWeights.push(componentSize);
|
|
546
|
-
leftHiddenItemsWeight += componentSize;
|
|
547
|
-
itemsFromStartToScrollEnd = ii;
|
|
548
608
|
if (stickyMap && stickyMap[collectionItem.id] > 0) {
|
|
549
609
|
stickyComponentSize = componentSize;
|
|
550
610
|
stickyCollectionItem = collectionItem;
|
|
551
611
|
}
|
|
552
612
|
if (collectionItem.id === fromItemId) {
|
|
553
|
-
itemById = collectionItem;
|
|
554
|
-
itemByIdPos = y;
|
|
555
613
|
if (stickyCollectionItem && stickyMap && stickyMap[stickyCollectionItem.id] > 0) {
|
|
556
|
-
|
|
614
|
+
const { num } = this.getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical);
|
|
615
|
+
if (num > 0) {
|
|
616
|
+
y -= size - componentSize;
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
y -= stickyComponentSize;
|
|
620
|
+
leftHiddenItemsWeight -= stickyComponentSize;
|
|
621
|
+
}
|
|
557
622
|
}
|
|
623
|
+
itemById = collectionItem;
|
|
624
|
+
itemByIdPos = y;
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
leftItemsWeights.push(componentSize);
|
|
628
|
+
leftHiddenItemsWeight += componentSize;
|
|
629
|
+
itemsFromStartToScrollEnd = ii;
|
|
558
630
|
}
|
|
559
631
|
}
|
|
560
632
|
}
|
|
@@ -582,7 +654,6 @@ class TrackBox extends CacheMap {
|
|
|
582
654
|
}
|
|
583
655
|
y += componentSize;
|
|
584
656
|
}
|
|
585
|
-
fullHeight = y;
|
|
586
657
|
if (itemsFromStartToScrollEnd === -1) {
|
|
587
658
|
itemsFromStartToScrollEnd = 0;
|
|
588
659
|
}
|
|
@@ -608,12 +679,12 @@ class TrackBox extends CacheMap {
|
|
|
608
679
|
rightItemsWeight = rightItemLength * typicalItemSize,
|
|
609
680
|
leftHiddenItemsWeight = itemsFromStartToScrollEnd * typicalItemSize,
|
|
610
681
|
totalItemsToDisplayEndWeight = itemsFromStartToDisplayEnd * typicalItemSize;
|
|
611
|
-
actualScrollSize = scrollSize
|
|
612
|
-
|
|
682
|
+
actualScrollSize = scrollSize;
|
|
683
|
+
totalSize = totalLength * typicalItemSize;
|
|
613
684
|
}
|
|
614
685
|
startIndex = Math.min(itemsFromStartToScrollEnd - leftItemLength, totalLength > 0 ? totalLength - 1 : 0);
|
|
615
|
-
const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta =
|
|
616
|
-
if (scrollDirection === -1) {
|
|
686
|
+
const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta = totalSize - this._previouseFullHeigh;
|
|
687
|
+
if (this.scrollDirection === -1) {
|
|
617
688
|
this._delta += delta;
|
|
618
689
|
}
|
|
619
690
|
const metrics = {
|
|
@@ -646,13 +717,17 @@ class TrackBox extends CacheMap {
|
|
|
646
717
|
totalSize,
|
|
647
718
|
typicalItemSize,
|
|
648
719
|
};
|
|
649
|
-
|
|
650
|
-
this._previouseFullHeigh = fullHeight;
|
|
651
|
-
}
|
|
720
|
+
this._previouseFullHeigh = totalSize;
|
|
652
721
|
return metrics;
|
|
653
722
|
}
|
|
654
|
-
|
|
723
|
+
clearDeltaDirection() {
|
|
724
|
+
this.clearScrollDirectionCache();
|
|
725
|
+
}
|
|
726
|
+
clearDelta(clearDirectionDetector = false) {
|
|
655
727
|
this._delta = 0;
|
|
728
|
+
if (clearDirectionDetector) {
|
|
729
|
+
this.clearScrollDirectionCache();
|
|
730
|
+
}
|
|
656
731
|
}
|
|
657
732
|
generateDisplayCollection(items, stickyMap, metrics) {
|
|
658
733
|
const {
|
|
@@ -817,7 +892,7 @@ class TrackBox extends CacheMap {
|
|
|
817
892
|
* Virtual list component.
|
|
818
893
|
* Maximum performance for extremely large lists.
|
|
819
894
|
* It is based on algorithms for virtualization of screen objects.
|
|
820
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
895
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/17.x/projects/ng-virtual-list/src/lib/ng-virtual-list.component.ts
|
|
821
896
|
* @author Evgenii Grebennikov
|
|
822
897
|
* @email djonnyx@gmail.com
|
|
823
898
|
*/
|
|
@@ -887,27 +962,20 @@ class NgVirtualListComponent {
|
|
|
887
962
|
* Number of elements outside the scope of visibility. Default value is 2.
|
|
888
963
|
*/
|
|
889
964
|
itemsOffset = input(DEFAULT_ITEMS_OFFSET);
|
|
890
|
-
_scrollToTimeout;
|
|
891
965
|
_isVertical = this.getIsVertical();
|
|
892
966
|
_displayComponents = [];
|
|
893
967
|
_bounds = signal(null);
|
|
894
968
|
_scrollSize = signal(0);
|
|
895
969
|
_resizeObserver = null;
|
|
896
|
-
/**
|
|
897
|
-
* only dynamic
|
|
898
|
-
*/
|
|
899
|
-
_scrolledItemId = signal(undefined);
|
|
900
970
|
_onResizeHandler = () => {
|
|
901
971
|
this._bounds.set(this._container()?.nativeElement?.getBoundingClientRect() ?? null);
|
|
902
972
|
};
|
|
903
|
-
_scrollDirection = 0;
|
|
904
973
|
_onScrollHandler = (e) => {
|
|
905
|
-
this._scrolledItemId.set(undefined);
|
|
906
974
|
const container = this._container()?.nativeElement;
|
|
907
975
|
if (container) {
|
|
908
976
|
const dynamicSize = this.dynamicSize(), delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft), previouseScrollSize = this._scrollSize();
|
|
909
977
|
let actualScrollSize = scrollSize;
|
|
910
|
-
this.
|
|
978
|
+
this._trackBox.deltaDirection = previouseScrollSize > scrollSize ? -1 : 1;
|
|
911
979
|
if (dynamicSize && delta !== 0) {
|
|
912
980
|
actualScrollSize = scrollSize + delta;
|
|
913
981
|
const params = {
|
|
@@ -921,7 +989,7 @@ class NgVirtualListComponent {
|
|
|
921
989
|
}
|
|
922
990
|
}
|
|
923
991
|
this._scrollSize.set(actualScrollSize);
|
|
924
|
-
this.onScroll.emit(actualScrollSize);
|
|
992
|
+
this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
|
|
925
993
|
}
|
|
926
994
|
};
|
|
927
995
|
scrollImmediately(container, params) {
|
|
@@ -948,9 +1016,11 @@ class NgVirtualListComponent {
|
|
|
948
1016
|
container.nativeElement.removeEventListener(SCROLL_END, this._scrollImmediatelyHandler);
|
|
949
1017
|
}
|
|
950
1018
|
}
|
|
951
|
-
_onScrollEndHandler = (e
|
|
1019
|
+
_onScrollEndHandler = (e) => {
|
|
952
1020
|
const container = this._container();
|
|
953
1021
|
if (container) {
|
|
1022
|
+
this._trackBox.clearDelta();
|
|
1023
|
+
this._trackBox.clearDeltaDirection();
|
|
954
1024
|
const itemSize = this.itemSize(), snapToItem = this.snapToItem(), dynamicSize = this.dynamicSize(), delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
|
|
955
1025
|
let actualScrollSize = scrollSize;
|
|
956
1026
|
if (dynamicSize && delta !== 0) {
|
|
@@ -960,7 +1030,6 @@ class NgVirtualListComponent {
|
|
|
960
1030
|
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
961
1031
|
behavior: BEHAVIOR_INSTANT
|
|
962
1032
|
};
|
|
963
|
-
this._trackBox.clearDelta();
|
|
964
1033
|
this._scrollSize.set(actualScrollSize);
|
|
965
1034
|
container.nativeElement.scroll(params);
|
|
966
1035
|
return;
|
|
@@ -978,9 +1047,7 @@ class NgVirtualListComponent {
|
|
|
978
1047
|
}
|
|
979
1048
|
}
|
|
980
1049
|
this._scrollSize.set(actualScrollSize);
|
|
981
|
-
|
|
982
|
-
this.onScrollEnd.emit(actualScrollSize);
|
|
983
|
-
}
|
|
1050
|
+
this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
|
|
984
1051
|
}
|
|
985
1052
|
};
|
|
986
1053
|
_elementRef = inject((ElementRef));
|
|
@@ -1002,7 +1069,7 @@ class NgVirtualListComponent {
|
|
|
1002
1069
|
this._initialized = signal(false);
|
|
1003
1070
|
this.$initialized = toObservable(this._initialized);
|
|
1004
1071
|
this._trackBox.displayComponents = this._displayComponents;
|
|
1005
|
-
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), $cacheVersion = this.$cacheVersion
|
|
1072
|
+
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), $cacheVersion = this.$cacheVersion;
|
|
1006
1073
|
$isVertical.pipe(takeUntilDestroyed(), tap(v => {
|
|
1007
1074
|
this._isVertical = v;
|
|
1008
1075
|
const el = this._elementRef.nativeElement;
|
|
@@ -1011,33 +1078,21 @@ class NgVirtualListComponent {
|
|
|
1011
1078
|
$dynamicSize.pipe(takeUntilDestroyed(), tap(dynamicSize => {
|
|
1012
1079
|
this.listenCacheChangesIfNeed(dynamicSize);
|
|
1013
1080
|
})).subscribe();
|
|
1014
|
-
combineLatest([this.$initialized, $
|
|
1081
|
+
combineLatest([this.$initialized, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
|
|
1015
1082
|
$itemsOffset, $snap, $isVertical, $dynamicSize, $cacheVersion,
|
|
1016
|
-
]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), switchMap(([,
|
|
1083
|
+
]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, cacheVersion,]) => {
|
|
1017
1084
|
const { width, height } = bounds;
|
|
1018
1085
|
let actualScrollSize = scrollSize;
|
|
1019
1086
|
const opts = {
|
|
1020
1087
|
bounds: { width, height }, collection: items, dynamicSize, isVertical, itemSize,
|
|
1021
|
-
itemsOffset, scrollSize: scrollSize, snap,
|
|
1088
|
+
itemsOffset, scrollSize: scrollSize, snap,
|
|
1022
1089
|
};
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
actualScrollSize = scrollSize;
|
|
1026
|
-
this._scrollSize.set(actualScrollSize);
|
|
1027
|
-
}
|
|
1028
|
-
const scrollDirection = this._scrollDirection, { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
1029
|
-
...opts, scrollSize: actualScrollSize, scrollDirection
|
|
1090
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
1091
|
+
...opts, scrollSize: actualScrollSize,
|
|
1030
1092
|
});
|
|
1031
1093
|
this.resetBoundsSize(isVertical, totalSize);
|
|
1032
1094
|
this.createDisplayComponentsIfNeed(displayItems);
|
|
1033
1095
|
this.tracking();
|
|
1034
|
-
if (dynamicSize && scrolledItemId !== undefined) {
|
|
1035
|
-
const container = this._container();
|
|
1036
|
-
if (container) {
|
|
1037
|
-
const params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior: BEHAVIOR_AUTO };
|
|
1038
|
-
this.scrollImmediately(container, params);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
1096
|
return of(displayItems);
|
|
1042
1097
|
})).subscribe();
|
|
1043
1098
|
combineLatest([this.$initialized, toObservable(this.itemRenderer)]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), tap(([, itemRenderer]) => {
|
|
@@ -1113,6 +1168,14 @@ class NgVirtualListComponent {
|
|
|
1113
1168
|
* Behavior accepts the values "auto", "instant" and "smooth".
|
|
1114
1169
|
*/
|
|
1115
1170
|
scrollTo(id, behavior = BEHAVIOR_AUTO) {
|
|
1171
|
+
this.scrollToExecutor(id, behavior);
|
|
1172
|
+
}
|
|
1173
|
+
_scrollToRepeatExecutionTimeout;
|
|
1174
|
+
clearScrollToRepeatExecutionTimeout() {
|
|
1175
|
+
clearTimeout(this._scrollToRepeatExecutionTimeout);
|
|
1176
|
+
}
|
|
1177
|
+
scrollToExecutor(id, behavior, iteration = 0) {
|
|
1178
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1116
1179
|
const items = this.items();
|
|
1117
1180
|
if (!items || !items.length) {
|
|
1118
1181
|
return;
|
|
@@ -1124,24 +1187,35 @@ class NgVirtualListComponent {
|
|
|
1124
1187
|
container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
|
|
1125
1188
|
container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1126
1189
|
}
|
|
1127
|
-
const { width, height } = this._bounds() || { width: 0, height: 0 }, stickyMap = this.stickyMap(), items = this.items(), opts = {
|
|
1190
|
+
const { width, height } = this._bounds() || { width: 0, height: 0 }, stickyMap = this.stickyMap(), items = this.items(), isVertical = this._isVertical, opts = {
|
|
1128
1191
|
bounds: { width, height }, collection: items, dynamicSize, isVertical: this._isVertical, itemSize,
|
|
1129
|
-
itemsOffset: this.itemsOffset(), scrollSize:
|
|
1192
|
+
itemsOffset: this.itemsOffset(), scrollSize: isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
|
|
1130
1193
|
snap: this.snap(), fromItemId: id,
|
|
1131
|
-
}, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [
|
|
1132
|
-
this._scrolledItemId.set(id);
|
|
1194
|
+
}, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
|
|
1133
1195
|
this._scrollSize.set(scrollSize);
|
|
1134
1196
|
if (container) {
|
|
1135
1197
|
const handler = () => {
|
|
1136
1198
|
if (container) {
|
|
1137
1199
|
container.nativeElement.removeEventListener(SCROLL_END, handler);
|
|
1138
|
-
|
|
1139
|
-
|
|
1200
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
1201
|
+
...opts, scrollSize, fromItemId: id,
|
|
1202
|
+
});
|
|
1203
|
+
this.resetBoundsSize(isVertical, totalSize);
|
|
1204
|
+
this.createDisplayComponentsIfNeed(displayItems);
|
|
1205
|
+
this.tracking();
|
|
1206
|
+
const _scrollSize = this._trackBox.getItemPosition(id, stickyMap, { ...opts, scrollSize, fromItemId: id });
|
|
1207
|
+
if (scrollSize < _scrollSize && iteration < MAX_SCROLL_TO_ITERATIONS) {
|
|
1208
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1209
|
+
this._scrollToRepeatExecutionTimeout = setTimeout(() => {
|
|
1210
|
+
this.scrollToExecutor(id, BEHAVIOR_INSTANT, iteration + 1);
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
else {
|
|
1214
|
+
this._scrollSize.set(scrollSize);
|
|
1215
|
+
this.onScroll.emit({ scrollSize, direction: this._trackBox.scrollDirection });
|
|
1140
1216
|
container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
|
|
1141
1217
|
container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1142
|
-
}
|
|
1143
|
-
this.listenCacheChangesIfNeed(dynamicSize);
|
|
1144
|
-
this.onScroll.emit(scrollSize);
|
|
1218
|
+
}
|
|
1145
1219
|
}
|
|
1146
1220
|
};
|
|
1147
1221
|
container.nativeElement.addEventListener(SCROLL_END, handler);
|
|
@@ -1170,9 +1244,7 @@ class NgVirtualListComponent {
|
|
|
1170
1244
|
}
|
|
1171
1245
|
}
|
|
1172
1246
|
ngOnDestroy() {
|
|
1173
|
-
|
|
1174
|
-
clearTimeout(this._scrollToTimeout);
|
|
1175
|
-
}
|
|
1247
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1176
1248
|
if (this._trackBox) {
|
|
1177
1249
|
this._trackBox.dispose();
|
|
1178
1250
|
}
|