ng-virtual-list 16.0.2 → 16.0.4
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 -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 +45 -48
- 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 +147 -75
- 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
|
@@ -3,11 +3,11 @@ import { Component, ChangeDetectionStrategy, EventEmitter as EventEmitter$1, Vie
|
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
5
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
6
|
-
import { BehaviorSubject, filter, map, tap, combineLatest, distinctUntilChanged,
|
|
6
|
+
import { BehaviorSubject, filter, map, tap, combineLatest, distinctUntilChanged, switchMap, of } from 'rxjs';
|
|
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/16.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/16.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: "16.2.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/16.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/16.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/16.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/16.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/16.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/16.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/16.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/16.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
|
*/
|
|
@@ -976,27 +1051,20 @@ class NgVirtualListComponent {
|
|
|
976
1051
|
}
|
|
977
1052
|
;
|
|
978
1053
|
get itemsOffset() { return this._$itemsOffset.getValue(); }
|
|
979
|
-
_scrollToTimeout;
|
|
980
1054
|
_isVertical = this.getIsVertical();
|
|
981
1055
|
_displayComponents = [];
|
|
982
1056
|
_$bounds = new BehaviorSubject(null);
|
|
983
1057
|
_$scrollSize = new BehaviorSubject(0);
|
|
984
1058
|
_resizeObserver = null;
|
|
985
|
-
/**
|
|
986
|
-
* only dynamic
|
|
987
|
-
*/
|
|
988
|
-
_$scrolledItemId = new BehaviorSubject(undefined);
|
|
989
1059
|
_onResizeHandler = () => {
|
|
990
1060
|
this._$bounds.next(this._container?.nativeElement?.getBoundingClientRect() ?? null);
|
|
991
1061
|
};
|
|
992
|
-
_scrollDirection = 0;
|
|
993
1062
|
_onScrollHandler = (e) => {
|
|
994
|
-
this._$scrolledItemId.next(undefined);
|
|
995
1063
|
const container = this._container?.nativeElement;
|
|
996
1064
|
if (container) {
|
|
997
1065
|
const dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft), previouseScrollSize = this._$scrollSize.getValue();
|
|
998
1066
|
let actualScrollSize = scrollSize;
|
|
999
|
-
this.
|
|
1067
|
+
this._trackBox.deltaDirection = previouseScrollSize > scrollSize ? -1 : 1;
|
|
1000
1068
|
if (dynamicSize && delta !== 0) {
|
|
1001
1069
|
actualScrollSize = scrollSize + delta;
|
|
1002
1070
|
const params = {
|
|
@@ -1010,7 +1078,7 @@ class NgVirtualListComponent {
|
|
|
1010
1078
|
}
|
|
1011
1079
|
}
|
|
1012
1080
|
this._$scrollSize.next(actualScrollSize);
|
|
1013
|
-
this.onScroll.emit(actualScrollSize);
|
|
1081
|
+
this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
|
|
1014
1082
|
}
|
|
1015
1083
|
};
|
|
1016
1084
|
scrollImmediately(container, params) {
|
|
@@ -1037,9 +1105,11 @@ class NgVirtualListComponent {
|
|
|
1037
1105
|
container.nativeElement.removeEventListener(SCROLL_END, this._scrollImmediatelyHandler);
|
|
1038
1106
|
}
|
|
1039
1107
|
}
|
|
1040
|
-
_onScrollEndHandler = (e
|
|
1108
|
+
_onScrollEndHandler = (e) => {
|
|
1041
1109
|
const container = this._container;
|
|
1042
1110
|
if (container) {
|
|
1111
|
+
this._trackBox.clearDelta();
|
|
1112
|
+
this._trackBox.clearDeltaDirection();
|
|
1043
1113
|
const itemSize = this.itemSize, snapToItem = this.snapToItem, dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
|
|
1044
1114
|
let actualScrollSize = scrollSize;
|
|
1045
1115
|
if (dynamicSize && delta !== 0) {
|
|
@@ -1049,7 +1119,6 @@ class NgVirtualListComponent {
|
|
|
1049
1119
|
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
1050
1120
|
behavior: BEHAVIOR_INSTANT
|
|
1051
1121
|
};
|
|
1052
|
-
this._trackBox.clearDelta();
|
|
1053
1122
|
this._$scrollSize.next(actualScrollSize);
|
|
1054
1123
|
container.nativeElement.scroll(params);
|
|
1055
1124
|
return;
|
|
@@ -1067,9 +1136,7 @@ class NgVirtualListComponent {
|
|
|
1067
1136
|
}
|
|
1068
1137
|
}
|
|
1069
1138
|
this._$scrollSize.next(actualScrollSize);
|
|
1070
|
-
|
|
1071
|
-
this.onScrollEnd.emit(actualScrollSize);
|
|
1072
|
-
}
|
|
1139
|
+
this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
|
|
1073
1140
|
}
|
|
1074
1141
|
};
|
|
1075
1142
|
_$initialized = new BehaviorSubject(false);
|
|
@@ -1092,7 +1159,7 @@ class NgVirtualListComponent {
|
|
|
1092
1159
|
this._$initialized = new BehaviorSubject(false);
|
|
1093
1160
|
this.$initialized = this._$initialized.asObservable();
|
|
1094
1161
|
this._trackBox.displayComponents = this._displayComponents;
|
|
1095
|
-
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, $cacheVersion = this.$cacheVersion
|
|
1162
|
+
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, $cacheVersion = this.$cacheVersion;
|
|
1096
1163
|
$isVertical.pipe(takeUntilDestroyed(), tap(v => {
|
|
1097
1164
|
this._isVertical = v;
|
|
1098
1165
|
const el = this._elementRef.nativeElement;
|
|
@@ -1101,33 +1168,21 @@ class NgVirtualListComponent {
|
|
|
1101
1168
|
$dynamicSize.pipe(takeUntilDestroyed(), tap(dynamicSize => {
|
|
1102
1169
|
this.listenCacheChangesIfNeed(dynamicSize);
|
|
1103
1170
|
})).subscribe();
|
|
1104
|
-
combineLatest([this.$initialized, $
|
|
1171
|
+
combineLatest([this.$initialized, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
|
|
1105
1172
|
$itemsOffset, $snap, $isVertical, $dynamicSize, $cacheVersion,
|
|
1106
|
-
]).pipe(takeUntilDestroyed(), distinctUntilChanged(),
|
|
1173
|
+
]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, cacheVersion,]) => {
|
|
1107
1174
|
const { width, height } = bounds;
|
|
1108
1175
|
let actualScrollSize = scrollSize;
|
|
1109
1176
|
const opts = {
|
|
1110
1177
|
bounds: { width, height }, collection: items, dynamicSize, isVertical, itemSize,
|
|
1111
|
-
itemsOffset, scrollSize: scrollSize, snap,
|
|
1178
|
+
itemsOffset, scrollSize: scrollSize, snap,
|
|
1112
1179
|
};
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
actualScrollSize = scrollSize;
|
|
1116
|
-
this._$scrollSize.next(actualScrollSize);
|
|
1117
|
-
}
|
|
1118
|
-
const scrollDirection = this._scrollDirection, { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
1119
|
-
...opts, scrollSize: actualScrollSize, scrollDirection
|
|
1180
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
1181
|
+
...opts, scrollSize: actualScrollSize,
|
|
1120
1182
|
});
|
|
1121
1183
|
this.resetBoundsSize(isVertical, totalSize);
|
|
1122
1184
|
this.createDisplayComponentsIfNeed(displayItems);
|
|
1123
1185
|
this.tracking();
|
|
1124
|
-
if (dynamicSize && scrolledItemId !== undefined) {
|
|
1125
|
-
const container = this._container;
|
|
1126
|
-
if (container) {
|
|
1127
|
-
const params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior: BEHAVIOR_AUTO };
|
|
1128
|
-
this.scrollImmediately(container, params);
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
1186
|
return of(displayItems);
|
|
1132
1187
|
})).subscribe();
|
|
1133
1188
|
combineLatest([this.$initialized, this.$itemRenderer]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), tap(([, itemRenderer]) => {
|
|
@@ -1203,6 +1258,14 @@ class NgVirtualListComponent {
|
|
|
1203
1258
|
* Behavior accepts the values "auto", "instant" and "smooth".
|
|
1204
1259
|
*/
|
|
1205
1260
|
scrollTo(id, behavior = BEHAVIOR_AUTO) {
|
|
1261
|
+
this.scrollToExecutor(id, behavior);
|
|
1262
|
+
}
|
|
1263
|
+
_scrollToRepeatExecutionTimeout;
|
|
1264
|
+
clearScrollToRepeatExecutionTimeout() {
|
|
1265
|
+
clearTimeout(this._scrollToRepeatExecutionTimeout);
|
|
1266
|
+
}
|
|
1267
|
+
scrollToExecutor(id, behavior, iteration = 0) {
|
|
1268
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1206
1269
|
const items = this.items;
|
|
1207
1270
|
if (!items || !items.length) {
|
|
1208
1271
|
return;
|
|
@@ -1214,24 +1277,35 @@ class NgVirtualListComponent {
|
|
|
1214
1277
|
container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
|
|
1215
1278
|
container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1216
1279
|
}
|
|
1217
|
-
const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, opts = {
|
|
1280
|
+
const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, isVertical = this._isVertical, opts = {
|
|
1218
1281
|
bounds: { width, height }, collection: items, dynamicSize, isVertical: this._isVertical, itemSize,
|
|
1219
|
-
itemsOffset: this.itemsOffset, scrollSize:
|
|
1282
|
+
itemsOffset: this.itemsOffset, scrollSize: isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
|
|
1220
1283
|
snap: this.snap, fromItemId: id,
|
|
1221
|
-
}, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [
|
|
1222
|
-
this._$scrolledItemId.next(id);
|
|
1284
|
+
}, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
|
|
1223
1285
|
this._$scrollSize.next(scrollSize);
|
|
1224
1286
|
if (container) {
|
|
1225
1287
|
const handler = () => {
|
|
1226
1288
|
if (container) {
|
|
1227
1289
|
container.nativeElement.removeEventListener(SCROLL_END, handler);
|
|
1228
|
-
|
|
1229
|
-
|
|
1290
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
1291
|
+
...opts, scrollSize, fromItemId: id,
|
|
1292
|
+
});
|
|
1293
|
+
this.resetBoundsSize(isVertical, totalSize);
|
|
1294
|
+
this.createDisplayComponentsIfNeed(displayItems);
|
|
1295
|
+
this.tracking();
|
|
1296
|
+
const _scrollSize = this._trackBox.getItemPosition(id, stickyMap, { ...opts, scrollSize, fromItemId: id });
|
|
1297
|
+
if (scrollSize < _scrollSize && iteration < MAX_SCROLL_TO_ITERATIONS) {
|
|
1298
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1299
|
+
this._scrollToRepeatExecutionTimeout = setTimeout(() => {
|
|
1300
|
+
this.scrollToExecutor(id, BEHAVIOR_INSTANT, iteration + 1);
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
else {
|
|
1304
|
+
this._$scrollSize.next(scrollSize);
|
|
1305
|
+
this.onScroll.emit({ scrollSize, direction: this._trackBox.scrollDirection });
|
|
1230
1306
|
container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
|
|
1231
1307
|
container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1232
|
-
}
|
|
1233
|
-
this.listenCacheChangesIfNeed(dynamicSize);
|
|
1234
|
-
this.onScroll.emit(scrollSize);
|
|
1308
|
+
}
|
|
1235
1309
|
}
|
|
1236
1310
|
};
|
|
1237
1311
|
container.nativeElement.addEventListener(SCROLL_END, handler);
|
|
@@ -1260,9 +1334,7 @@ class NgVirtualListComponent {
|
|
|
1260
1334
|
}
|
|
1261
1335
|
}
|
|
1262
1336
|
ngOnDestroy() {
|
|
1263
|
-
|
|
1264
|
-
clearTimeout(this._scrollToTimeout);
|
|
1265
|
-
}
|
|
1337
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1266
1338
|
if (this._trackBox) {
|
|
1267
1339
|
this._trackBox.dispose();
|
|
1268
1340
|
}
|