ng-virtual-list 14.0.0 → 14.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/README.md +7 -7
- package/esm2020/lib/components/ng-virtual-list-item.component.mjs +2 -2
- package/esm2020/lib/const/index.mjs +2 -1
- package/esm2020/lib/enums/direction.mjs +1 -1
- package/esm2020/lib/enums/directions.mjs +2 -2
- package/esm2020/lib/models/collection.model.mjs +1 -1
- package/esm2020/lib/models/index.mjs +1 -1
- package/esm2020/lib/models/item.model.mjs +1 -1
- package/esm2020/lib/models/render-collection.model.mjs +1 -1
- package/esm2020/lib/models/render-item-config.model.mjs +1 -1
- package/esm2020/lib/models/render-item.model.mjs +1 -1
- package/esm2020/lib/models/scroll-direction.model.mjs +2 -0
- package/esm2020/lib/models/scroll-event.model.mjs +2 -0
- package/esm2020/lib/models/sticky-map.model.mjs +1 -1
- package/esm2020/lib/ng-virtual-list.component.mjs +43 -46
- package/esm2020/lib/types/id.mjs +1 -1
- package/esm2020/lib/types/rect.mjs +1 -1
- package/esm2020/lib/types/size.mjs +1 -1
- package/esm2020/lib/utils/cacheMap.mjs +43 -2
- package/esm2020/lib/utils/debounce.mjs +2 -2
- package/esm2020/lib/utils/disposableComponent.mjs +2 -2
- package/esm2020/lib/utils/eventEmitter.mjs +2 -2
- package/esm2020/lib/utils/isDirection.mjs +2 -2
- package/esm2020/lib/utils/toggleClassName.mjs +2 -2
- package/esm2020/lib/utils/trackBox.mjs +56 -22
- package/esm2020/lib/utils/tracker.mjs +2 -2
- package/fesm2015/ng-virtual-list.mjs +143 -73
- package/fesm2015/ng-virtual-list.mjs.map +1 -1
- package/fesm2020/ng-virtual-list.mjs +146 -74
- package/fesm2020/ng-virtual-list.mjs.map +1 -1
- package/lib/components/ng-virtual-list-item.component.d.ts +1 -1
- package/lib/const/index.d.ts +1 -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 -9
- 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/disposableComponent.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
|
@@ -6,7 +6,7 @@ import { Subject, BehaviorSubject, filter, map, takeUntil, tap, combineLatest, d
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Axis of the arrangement of virtual list elements.
|
|
9
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
9
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/enums/directions.ts
|
|
10
10
|
* @author Evgenii Grebennikov
|
|
11
11
|
* @email djonnyx@gmail.com
|
|
12
12
|
*/
|
|
@@ -31,6 +31,7 @@ const DEFAULT_DYNAMIC_SIZE = false;
|
|
|
31
31
|
const TRACK_BY_PROPERTY_NAME = 'id';
|
|
32
32
|
const DEFAULT_DIRECTION = Directions.VERTICAL;
|
|
33
33
|
const DISPLAY_OBJECTS_LENGTH_MESUREMENT_ERROR = 1;
|
|
34
|
+
const MAX_SCROLL_TO_ITERATIONS = 5;
|
|
34
35
|
// presets
|
|
35
36
|
const BEHAVIOR_AUTO = 'auto';
|
|
36
37
|
const BEHAVIOR_INSTANT = 'instant';
|
|
@@ -57,7 +58,7 @@ const CLASS_LIST_HORIZONTAL = 'horizontal';
|
|
|
57
58
|
|
|
58
59
|
/**
|
|
59
60
|
* Virtual list item component
|
|
60
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
61
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/components/ng-virtual-list-item.component.ts
|
|
61
62
|
* @author Evgenii Grebennikov
|
|
62
63
|
* @email djonnyx@gmail.com
|
|
63
64
|
*/
|
|
@@ -134,7 +135,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
134
135
|
const HORIZONTAL_ALIASES = [Directions.HORIZONTAL, 'horizontal'], VERTICAL_ALIASES = [Directions.VERTICAL, 'vertical'];
|
|
135
136
|
/**
|
|
136
137
|
* Determines the axis membership of a virtual list
|
|
137
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
138
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/isDirection.ts
|
|
138
139
|
* @author Evgenii Grebennikov
|
|
139
140
|
* @email djonnyx@gmail.com
|
|
140
141
|
*/
|
|
@@ -147,7 +148,7 @@ const isDirection = (src, expected) => {
|
|
|
147
148
|
|
|
148
149
|
/**
|
|
149
150
|
* Simple debounce function.
|
|
150
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
151
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/debounce.ts
|
|
151
152
|
* @author Evgenii Grebennikov
|
|
152
153
|
* @email djonnyx@gmail.com
|
|
153
154
|
*/
|
|
@@ -178,7 +179,7 @@ const debounce = (cb, debounceTime = 0) => {
|
|
|
178
179
|
|
|
179
180
|
/**
|
|
180
181
|
* Switch css classes
|
|
181
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
182
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/toggleClassName.ts
|
|
182
183
|
* @author Evgenii Grebennikov
|
|
183
184
|
* @email djonnyx@gmail.com
|
|
184
185
|
*/
|
|
@@ -193,7 +194,7 @@ const toggleClassName = (el, className, remove = false) => {
|
|
|
193
194
|
|
|
194
195
|
/**
|
|
195
196
|
* Tracks display items by property
|
|
196
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
197
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/tracker.ts
|
|
197
198
|
* @author Evgenii Grebennikov
|
|
198
199
|
* @email djonnyx@gmail.com
|
|
199
200
|
*/
|
|
@@ -301,7 +302,7 @@ class Tracker {
|
|
|
301
302
|
|
|
302
303
|
/**
|
|
303
304
|
* Simple event emitter
|
|
304
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
305
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/eventEmitter.ts
|
|
305
306
|
* @author Evgenii Grebennikov
|
|
306
307
|
* @email djonnyx@gmail.com
|
|
307
308
|
*/
|
|
@@ -405,10 +406,11 @@ class EventEmitter {
|
|
|
405
406
|
}
|
|
406
407
|
}
|
|
407
408
|
|
|
409
|
+
const MAX_SCROLL_DIRECTION_POOL = 10, CLEAR_SCROLL_DIRECTION_TO = 0;
|
|
408
410
|
/**
|
|
409
411
|
* Cache map.
|
|
410
412
|
* Emits a change event on each mutation.
|
|
411
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
413
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/cacheMap.ts
|
|
412
414
|
* @author Evgenii Grebennikov
|
|
413
415
|
* @email djonnyx@gmail.com
|
|
414
416
|
*/
|
|
@@ -419,13 +421,52 @@ class CacheMap extends EventEmitter {
|
|
|
419
421
|
this._version = 0;
|
|
420
422
|
this._previouseFullHeigh = 0;
|
|
421
423
|
this._delta = 0;
|
|
424
|
+
this._deltaDirection = 1;
|
|
425
|
+
this._scrollDirectionCache = [];
|
|
426
|
+
this._scrollDirection = 1;
|
|
427
|
+
this._clearScrollDirectionDebounce = debounce(() => {
|
|
428
|
+
while (this._scrollDirectionCache.length > CLEAR_SCROLL_DIRECTION_TO) {
|
|
429
|
+
this._scrollDirectionCache.shift();
|
|
430
|
+
}
|
|
431
|
+
}, 10);
|
|
422
432
|
}
|
|
423
433
|
get delta() {
|
|
424
434
|
return this._delta;
|
|
425
435
|
}
|
|
436
|
+
set deltaDirection(v) {
|
|
437
|
+
this._deltaDirection = v;
|
|
438
|
+
this._scrollDirection = this.calcScrollDirection(v);
|
|
439
|
+
}
|
|
440
|
+
get deltaDirection() {
|
|
441
|
+
return this._deltaDirection;
|
|
442
|
+
}
|
|
443
|
+
get scrollDirection() {
|
|
444
|
+
return this._scrollDirection;
|
|
445
|
+
}
|
|
426
446
|
get version() {
|
|
427
447
|
return this._version;
|
|
428
448
|
}
|
|
449
|
+
clearScrollDirectionCache() {
|
|
450
|
+
this._clearScrollDirectionDebounce.execute();
|
|
451
|
+
}
|
|
452
|
+
calcScrollDirection(v) {
|
|
453
|
+
while (this._scrollDirectionCache.length >= MAX_SCROLL_DIRECTION_POOL) {
|
|
454
|
+
this._scrollDirectionCache.shift();
|
|
455
|
+
}
|
|
456
|
+
this._scrollDirectionCache.push(v);
|
|
457
|
+
const dict = { [-1]: 0, [0]: 0, [1]: 0 };
|
|
458
|
+
for (let i = 0, l = this._scrollDirectionCache.length; i < l; i++) {
|
|
459
|
+
const dir = this._scrollDirectionCache[i];
|
|
460
|
+
dict[dir] += 1;
|
|
461
|
+
}
|
|
462
|
+
if (dict[-1] > dict[1]) {
|
|
463
|
+
return -1;
|
|
464
|
+
}
|
|
465
|
+
else if (dict[1] > dict[-1]) {
|
|
466
|
+
return 1;
|
|
467
|
+
}
|
|
468
|
+
return -1;
|
|
469
|
+
}
|
|
429
470
|
bumpVersion() {
|
|
430
471
|
this._version = this._version === Number.MAX_SAFE_INTEGER ? 0 : this._version + 1;
|
|
431
472
|
}
|
|
@@ -459,7 +500,7 @@ class CacheMap extends EventEmitter {
|
|
|
459
500
|
const TRACK_BOX_CHANGE_EVENT_NAME = 'change';
|
|
460
501
|
/**
|
|
461
502
|
* An object that performs tracking, calculations and caching.
|
|
462
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
503
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/trackBox.ts
|
|
463
504
|
* @author Evgenii Grebennikov
|
|
464
505
|
* @email djonnyx@gmail.com
|
|
465
506
|
*/
|
|
@@ -497,12 +538,12 @@ class TrackBox extends CacheMap {
|
|
|
497
538
|
this._debounceChanges.execute(this._version);
|
|
498
539
|
}
|
|
499
540
|
getItemPosition(id, stickyMap, options) {
|
|
500
|
-
const opt = { fromItemId: id, stickyMap,
|
|
541
|
+
const opt = { fromItemId: id, stickyMap, ...options };
|
|
501
542
|
const { scrollSize } = this.recalculateMetrics(opt);
|
|
502
543
|
return scrollSize;
|
|
503
544
|
}
|
|
504
545
|
updateCollection(items, stickyMap, options) {
|
|
505
|
-
const opt = { stickyMap,
|
|
546
|
+
const opt = { stickyMap, ...options };
|
|
506
547
|
this.cacheElements();
|
|
507
548
|
const metrics = this.recalculateMetrics({
|
|
508
549
|
...opt,
|
|
@@ -511,14 +552,35 @@ class TrackBox extends CacheMap {
|
|
|
511
552
|
const displayItems = this.generateDisplayCollection(items, stickyMap, metrics);
|
|
512
553
|
return { displayItems, totalSize: metrics.totalSize, delta: metrics.delta };
|
|
513
554
|
}
|
|
555
|
+
getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical) {
|
|
556
|
+
const sizeProperty = isVertical ? HEIGHT_PROP_NAME : WIDTH_PROP_NAME;
|
|
557
|
+
let offset = 0, num = 0;
|
|
558
|
+
for (let j = collection.length - 1; j >= i; j--) {
|
|
559
|
+
const item = collection[j];
|
|
560
|
+
let itemSize = 0;
|
|
561
|
+
if (map.has(item.id)) {
|
|
562
|
+
const bounds = map.get(item.id);
|
|
563
|
+
itemSize = bounds ? bounds[sizeProperty] : typicalItemSize;
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
itemSize = typicalItemSize;
|
|
567
|
+
}
|
|
568
|
+
offset += itemSize;
|
|
569
|
+
num++;
|
|
570
|
+
if (offset > size) {
|
|
571
|
+
return { num: 0, offset };
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return { num, offset };
|
|
575
|
+
}
|
|
514
576
|
/**
|
|
515
577
|
* Calculates list metrics
|
|
516
578
|
*/
|
|
517
579
|
recalculateMetrics(options) {
|
|
518
|
-
const {
|
|
519
|
-
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,
|
|
580
|
+
const { fromItemId, bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, stickyMap } = options;
|
|
581
|
+
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)
|
|
520
582
|
|| (typeof fromItemId === 'string' && fromItemId > '-1');
|
|
521
|
-
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,
|
|
583
|
+
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;
|
|
522
584
|
if (dynamicSize) {
|
|
523
585
|
let y = 0, stickyCollectionItem = undefined, stickyComponentSize = 0;
|
|
524
586
|
for (let i = 0, l = collection.length; i < l; i++) {
|
|
@@ -531,21 +593,31 @@ class TrackBox extends CacheMap {
|
|
|
531
593
|
else {
|
|
532
594
|
componentSize = typicalItemSize;
|
|
533
595
|
}
|
|
596
|
+
totalSize += componentSize;
|
|
534
597
|
if (isFromId) {
|
|
535
598
|
if (itemById === undefined) {
|
|
536
|
-
leftItemsWeights.push(componentSize);
|
|
537
|
-
leftHiddenItemsWeight += componentSize;
|
|
538
|
-
itemsFromStartToScrollEnd = ii;
|
|
539
599
|
if (stickyMap && stickyMap[collectionItem.id] > 0) {
|
|
540
600
|
stickyComponentSize = componentSize;
|
|
541
601
|
stickyCollectionItem = collectionItem;
|
|
542
602
|
}
|
|
543
603
|
if (collectionItem.id === fromItemId) {
|
|
544
|
-
itemById = collectionItem;
|
|
545
|
-
itemByIdPos = y;
|
|
546
604
|
if (stickyCollectionItem && stickyMap && stickyMap[stickyCollectionItem.id] > 0) {
|
|
547
|
-
|
|
605
|
+
const { num } = this.getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical);
|
|
606
|
+
if (num > 0) {
|
|
607
|
+
y -= size - componentSize;
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
y -= stickyComponentSize;
|
|
611
|
+
leftHiddenItemsWeight -= stickyComponentSize;
|
|
612
|
+
}
|
|
548
613
|
}
|
|
614
|
+
itemById = collectionItem;
|
|
615
|
+
itemByIdPos = y;
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
leftItemsWeights.push(componentSize);
|
|
619
|
+
leftHiddenItemsWeight += componentSize;
|
|
620
|
+
itemsFromStartToScrollEnd = ii;
|
|
549
621
|
}
|
|
550
622
|
}
|
|
551
623
|
}
|
|
@@ -573,7 +645,6 @@ class TrackBox extends CacheMap {
|
|
|
573
645
|
}
|
|
574
646
|
y += componentSize;
|
|
575
647
|
}
|
|
576
|
-
fullHeight = y;
|
|
577
648
|
if (itemsFromStartToScrollEnd === -1) {
|
|
578
649
|
itemsFromStartToScrollEnd = 0;
|
|
579
650
|
}
|
|
@@ -599,12 +670,12 @@ class TrackBox extends CacheMap {
|
|
|
599
670
|
rightItemsWeight = rightItemLength * typicalItemSize,
|
|
600
671
|
leftHiddenItemsWeight = itemsFromStartToScrollEnd * typicalItemSize,
|
|
601
672
|
totalItemsToDisplayEndWeight = itemsFromStartToDisplayEnd * typicalItemSize;
|
|
602
|
-
actualScrollSize = scrollSize
|
|
603
|
-
|
|
673
|
+
actualScrollSize = scrollSize;
|
|
674
|
+
totalSize = totalLength * typicalItemSize;
|
|
604
675
|
}
|
|
605
676
|
startIndex = Math.min(itemsFromStartToScrollEnd - leftItemLength, totalLength > 0 ? totalLength - 1 : 0);
|
|
606
|
-
const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta =
|
|
607
|
-
if (scrollDirection === -1) {
|
|
677
|
+
const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta = totalSize - this._previouseFullHeigh;
|
|
678
|
+
if (this.scrollDirection === -1) {
|
|
608
679
|
this._delta += delta;
|
|
609
680
|
}
|
|
610
681
|
const metrics = {
|
|
@@ -637,13 +708,17 @@ class TrackBox extends CacheMap {
|
|
|
637
708
|
totalSize,
|
|
638
709
|
typicalItemSize,
|
|
639
710
|
};
|
|
640
|
-
|
|
641
|
-
this._previouseFullHeigh = fullHeight;
|
|
642
|
-
}
|
|
711
|
+
this._previouseFullHeigh = totalSize;
|
|
643
712
|
return metrics;
|
|
644
713
|
}
|
|
645
|
-
|
|
714
|
+
clearDeltaDirection() {
|
|
715
|
+
this.clearScrollDirectionCache();
|
|
716
|
+
}
|
|
717
|
+
clearDelta(clearDirectionDetector = false) {
|
|
646
718
|
this._delta = 0;
|
|
719
|
+
if (clearDirectionDetector) {
|
|
720
|
+
this.clearScrollDirectionCache();
|
|
721
|
+
}
|
|
647
722
|
}
|
|
648
723
|
generateDisplayCollection(items, stickyMap, metrics) {
|
|
649
724
|
const {
|
|
@@ -806,7 +881,7 @@ class TrackBox extends CacheMap {
|
|
|
806
881
|
|
|
807
882
|
/**
|
|
808
883
|
* Base disposable component
|
|
809
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
884
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/disposableComponent.ts
|
|
810
885
|
* @author Evgenii Grebennikov
|
|
811
886
|
* @email djonnyx@gmail.com
|
|
812
887
|
*/
|
|
@@ -834,7 +909,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
834
909
|
* Virtual list component.
|
|
835
910
|
* Maximum performance for extremely large lists.
|
|
836
911
|
* It is based on algorithms for virtualization of screen objects.
|
|
837
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
912
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/ng-virtual-list.component.ts
|
|
838
913
|
* @author Evgenii Grebennikov
|
|
839
914
|
* @email djonnyx@gmail.com
|
|
840
915
|
*/
|
|
@@ -882,21 +957,15 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
882
957
|
this._$bounds = new BehaviorSubject(null);
|
|
883
958
|
this._$scrollSize = new BehaviorSubject(0);
|
|
884
959
|
this._resizeObserver = null;
|
|
885
|
-
/**
|
|
886
|
-
* only dynamic
|
|
887
|
-
*/
|
|
888
|
-
this._$scrolledItemId = new BehaviorSubject(undefined);
|
|
889
960
|
this._onResizeHandler = () => {
|
|
890
961
|
this._$bounds.next(this._container?.nativeElement?.getBoundingClientRect() ?? null);
|
|
891
962
|
};
|
|
892
|
-
this._scrollDirection = 0;
|
|
893
963
|
this._onScrollHandler = (e) => {
|
|
894
|
-
this._$scrolledItemId.next(undefined);
|
|
895
964
|
const container = this._container?.nativeElement;
|
|
896
965
|
if (container) {
|
|
897
966
|
const dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft), previouseScrollSize = this._$scrollSize.getValue();
|
|
898
967
|
let actualScrollSize = scrollSize;
|
|
899
|
-
this.
|
|
968
|
+
this._trackBox.deltaDirection = previouseScrollSize > scrollSize ? -1 : 1;
|
|
900
969
|
if (dynamicSize && delta !== 0) {
|
|
901
970
|
actualScrollSize = scrollSize + delta;
|
|
902
971
|
const params = {
|
|
@@ -910,13 +979,15 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
910
979
|
}
|
|
911
980
|
}
|
|
912
981
|
this._$scrollSize.next(actualScrollSize);
|
|
913
|
-
this.onScroll.emit(actualScrollSize);
|
|
982
|
+
this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
|
|
914
983
|
}
|
|
915
984
|
};
|
|
916
985
|
this._scrollImmediatelyHandler = undefined;
|
|
917
|
-
this._onScrollEndHandler = (e
|
|
986
|
+
this._onScrollEndHandler = (e) => {
|
|
918
987
|
const container = this._container;
|
|
919
988
|
if (container) {
|
|
989
|
+
this._trackBox.clearDelta();
|
|
990
|
+
this._trackBox.clearDeltaDirection();
|
|
920
991
|
const itemSize = this.itemSize, snapToItem = this.snapToItem, dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
|
|
921
992
|
let actualScrollSize = scrollSize;
|
|
922
993
|
if (dynamicSize && delta !== 0) {
|
|
@@ -926,7 +997,6 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
926
997
|
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
927
998
|
behavior: BEHAVIOR_INSTANT
|
|
928
999
|
};
|
|
929
|
-
this._trackBox.clearDelta();
|
|
930
1000
|
this._$scrollSize.next(actualScrollSize);
|
|
931
1001
|
container.nativeElement.scroll(params);
|
|
932
1002
|
return;
|
|
@@ -944,9 +1014,7 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
944
1014
|
}
|
|
945
1015
|
}
|
|
946
1016
|
this._$scrollSize.next(actualScrollSize);
|
|
947
|
-
|
|
948
|
-
this.onScrollEnd.emit(actualScrollSize);
|
|
949
|
-
}
|
|
1017
|
+
this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
|
|
950
1018
|
}
|
|
951
1019
|
};
|
|
952
1020
|
this._$initialized = new BehaviorSubject(false);
|
|
@@ -964,7 +1032,7 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
964
1032
|
this._$initialized = new BehaviorSubject(false);
|
|
965
1033
|
this.$initialized = this._$initialized.asObservable();
|
|
966
1034
|
this._trackBox.displayComponents = this._displayComponents;
|
|
967
|
-
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
|
|
1035
|
+
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;
|
|
968
1036
|
$isVertical.pipe(takeUntil(this._$unsubscribe), tap(v => {
|
|
969
1037
|
this._isVertical = v;
|
|
970
1038
|
const el = this._elementRef.nativeElement;
|
|
@@ -973,33 +1041,21 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
973
1041
|
$dynamicSize.pipe(takeUntil(this._$unsubscribe), tap(dynamicSize => {
|
|
974
1042
|
this.listenCacheChangesIfNeed(dynamicSize);
|
|
975
1043
|
})).subscribe();
|
|
976
|
-
combineLatest([this.$initialized, $
|
|
1044
|
+
combineLatest([this.$initialized, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
|
|
977
1045
|
$itemsOffset, $snap, $isVertical, $dynamicSize, $cacheVersion,
|
|
978
|
-
]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), debounceTime(0), filter(([initialized]) => !!initialized), switchMap(([,
|
|
1046
|
+
]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), debounceTime(0), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, cacheVersion,]) => {
|
|
979
1047
|
const { width, height } = bounds;
|
|
980
1048
|
let actualScrollSize = scrollSize;
|
|
981
1049
|
const opts = {
|
|
982
1050
|
bounds: { width, height }, collection: items, dynamicSize, isVertical, itemSize,
|
|
983
|
-
itemsOffset, scrollSize: scrollSize, snap,
|
|
1051
|
+
itemsOffset, scrollSize: scrollSize, snap,
|
|
984
1052
|
};
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
actualScrollSize = scrollSize;
|
|
988
|
-
this._$scrollSize.next(actualScrollSize);
|
|
989
|
-
}
|
|
990
|
-
const scrollDirection = this._scrollDirection, { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
991
|
-
...opts, scrollSize: actualScrollSize, scrollDirection
|
|
1053
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
1054
|
+
...opts, scrollSize: actualScrollSize,
|
|
992
1055
|
});
|
|
993
1056
|
this.resetBoundsSize(isVertical, totalSize);
|
|
994
1057
|
this.createDisplayComponentsIfNeed(displayItems);
|
|
995
1058
|
this.tracking();
|
|
996
|
-
if (dynamicSize && scrolledItemId !== undefined) {
|
|
997
|
-
const container = this._container;
|
|
998
|
-
if (container) {
|
|
999
|
-
const params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior: BEHAVIOR_AUTO };
|
|
1000
|
-
this.scrollImmediately(container, params);
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
1059
|
return of(displayItems);
|
|
1004
1060
|
})).subscribe();
|
|
1005
1061
|
combineLatest([this.$initialized, this.$itemRenderer]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), filter(([initialized]) => !!initialized), tap(([, itemRenderer]) => {
|
|
@@ -1213,6 +1269,13 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1213
1269
|
* Behavior accepts the values "auto", "instant" and "smooth".
|
|
1214
1270
|
*/
|
|
1215
1271
|
scrollTo(id, behavior = BEHAVIOR_AUTO) {
|
|
1272
|
+
this.scrollToExecutor(id, behavior);
|
|
1273
|
+
}
|
|
1274
|
+
clearScrollToRepeatExecutionTimeout() {
|
|
1275
|
+
clearTimeout(this._scrollToRepeatExecutionTimeout);
|
|
1276
|
+
}
|
|
1277
|
+
scrollToExecutor(id, behavior, iteration = 0) {
|
|
1278
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1216
1279
|
const items = this.items;
|
|
1217
1280
|
if (!items || !items.length) {
|
|
1218
1281
|
return;
|
|
@@ -1224,24 +1287,35 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1224
1287
|
container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
|
|
1225
1288
|
container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1226
1289
|
}
|
|
1227
|
-
const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, opts = {
|
|
1290
|
+
const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, isVertical = this._isVertical, opts = {
|
|
1228
1291
|
bounds: { width, height }, collection: items, dynamicSize, isVertical: this._isVertical, itemSize,
|
|
1229
|
-
itemsOffset: this.itemsOffset, scrollSize:
|
|
1292
|
+
itemsOffset: this.itemsOffset, scrollSize: isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
|
|
1230
1293
|
snap: this.snap, fromItemId: id,
|
|
1231
|
-
}, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [
|
|
1232
|
-
this._$scrolledItemId.next(id);
|
|
1294
|
+
}, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
|
|
1233
1295
|
this._$scrollSize.next(scrollSize);
|
|
1234
1296
|
if (container) {
|
|
1235
1297
|
const handler = () => {
|
|
1236
1298
|
if (container) {
|
|
1237
1299
|
container.nativeElement.removeEventListener(SCROLL_END, handler);
|
|
1238
|
-
|
|
1239
|
-
|
|
1300
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, {
|
|
1301
|
+
...opts, scrollSize, fromItemId: id,
|
|
1302
|
+
});
|
|
1303
|
+
this.resetBoundsSize(isVertical, totalSize);
|
|
1304
|
+
this.createDisplayComponentsIfNeed(displayItems);
|
|
1305
|
+
this.tracking();
|
|
1306
|
+
const _scrollSize = this._trackBox.getItemPosition(id, stickyMap, { ...opts, scrollSize, fromItemId: id });
|
|
1307
|
+
if (scrollSize < _scrollSize && iteration < MAX_SCROLL_TO_ITERATIONS) {
|
|
1308
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1309
|
+
this._scrollToRepeatExecutionTimeout = setTimeout(() => {
|
|
1310
|
+
this.scrollToExecutor(id, BEHAVIOR_INSTANT, iteration + 1);
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
else {
|
|
1314
|
+
this._$scrollSize.next(scrollSize);
|
|
1315
|
+
this.onScroll.emit({ scrollSize, direction: this._trackBox.scrollDirection });
|
|
1240
1316
|
container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
|
|
1241
1317
|
container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1242
|
-
}
|
|
1243
|
-
this.listenCacheChangesIfNeed(dynamicSize);
|
|
1244
|
-
this.onScroll.emit(scrollSize);
|
|
1318
|
+
}
|
|
1245
1319
|
}
|
|
1246
1320
|
};
|
|
1247
1321
|
container.nativeElement.addEventListener(SCROLL_END, handler);
|
|
@@ -1271,9 +1345,7 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1271
1345
|
}
|
|
1272
1346
|
ngOnDestroy() {
|
|
1273
1347
|
super.ngOnDestroy();
|
|
1274
|
-
|
|
1275
|
-
clearTimeout(this._scrollToTimeout);
|
|
1276
|
-
}
|
|
1348
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1277
1349
|
if (this._trackBox) {
|
|
1278
1350
|
this._trackBox.dispose();
|
|
1279
1351
|
}
|