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
|
*/
|
|
@@ -135,7 +136,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
135
136
|
const HORIZONTAL_ALIASES = [Directions.HORIZONTAL, 'horizontal'], VERTICAL_ALIASES = [Directions.VERTICAL, 'vertical'];
|
|
136
137
|
/**
|
|
137
138
|
* Determines the axis membership of a virtual list
|
|
138
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
139
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/isDirection.ts
|
|
139
140
|
* @author Evgenii Grebennikov
|
|
140
141
|
* @email djonnyx@gmail.com
|
|
141
142
|
*/
|
|
@@ -148,7 +149,7 @@ const isDirection = (src, expected) => {
|
|
|
148
149
|
|
|
149
150
|
/**
|
|
150
151
|
* Simple debounce function.
|
|
151
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
152
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/debounce.ts
|
|
152
153
|
* @author Evgenii Grebennikov
|
|
153
154
|
* @email djonnyx@gmail.com
|
|
154
155
|
*/
|
|
@@ -179,7 +180,7 @@ const debounce = (cb, debounceTime = 0) => {
|
|
|
179
180
|
|
|
180
181
|
/**
|
|
181
182
|
* Switch css classes
|
|
182
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
183
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/toggleClassName.ts
|
|
183
184
|
* @author Evgenii Grebennikov
|
|
184
185
|
* @email djonnyx@gmail.com
|
|
185
186
|
*/
|
|
@@ -194,7 +195,7 @@ const toggleClassName = (el, className, remove = false) => {
|
|
|
194
195
|
|
|
195
196
|
/**
|
|
196
197
|
* Tracks display items by property
|
|
197
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
198
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/tracker.ts
|
|
198
199
|
* @author Evgenii Grebennikov
|
|
199
200
|
* @email djonnyx@gmail.com
|
|
200
201
|
*/
|
|
@@ -303,7 +304,7 @@ class Tracker {
|
|
|
303
304
|
|
|
304
305
|
/**
|
|
305
306
|
* Simple event emitter
|
|
306
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
307
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/eventEmitter.ts
|
|
307
308
|
* @author Evgenii Grebennikov
|
|
308
309
|
* @email djonnyx@gmail.com
|
|
309
310
|
*/
|
|
@@ -407,10 +408,11 @@ class EventEmitter {
|
|
|
407
408
|
}
|
|
408
409
|
}
|
|
409
410
|
|
|
411
|
+
const MAX_SCROLL_DIRECTION_POOL = 10, CLEAR_SCROLL_DIRECTION_TO = 0;
|
|
410
412
|
/**
|
|
411
413
|
* Cache map.
|
|
412
414
|
* Emits a change event on each mutation.
|
|
413
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
415
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/cacheMap.ts
|
|
414
416
|
* @author Evgenii Grebennikov
|
|
415
417
|
* @email djonnyx@gmail.com
|
|
416
418
|
*/
|
|
@@ -421,13 +423,52 @@ class CacheMap extends EventEmitter {
|
|
|
421
423
|
this._version = 0;
|
|
422
424
|
this._previouseFullHeigh = 0;
|
|
423
425
|
this._delta = 0;
|
|
426
|
+
this._deltaDirection = 1;
|
|
427
|
+
this._scrollDirectionCache = [];
|
|
428
|
+
this._scrollDirection = 1;
|
|
429
|
+
this._clearScrollDirectionDebounce = debounce(() => {
|
|
430
|
+
while (this._scrollDirectionCache.length > CLEAR_SCROLL_DIRECTION_TO) {
|
|
431
|
+
this._scrollDirectionCache.shift();
|
|
432
|
+
}
|
|
433
|
+
}, 10);
|
|
424
434
|
}
|
|
425
435
|
get delta() {
|
|
426
436
|
return this._delta;
|
|
427
437
|
}
|
|
438
|
+
set deltaDirection(v) {
|
|
439
|
+
this._deltaDirection = v;
|
|
440
|
+
this._scrollDirection = this.calcScrollDirection(v);
|
|
441
|
+
}
|
|
442
|
+
get deltaDirection() {
|
|
443
|
+
return this._deltaDirection;
|
|
444
|
+
}
|
|
445
|
+
get scrollDirection() {
|
|
446
|
+
return this._scrollDirection;
|
|
447
|
+
}
|
|
428
448
|
get version() {
|
|
429
449
|
return this._version;
|
|
430
450
|
}
|
|
451
|
+
clearScrollDirectionCache() {
|
|
452
|
+
this._clearScrollDirectionDebounce.execute();
|
|
453
|
+
}
|
|
454
|
+
calcScrollDirection(v) {
|
|
455
|
+
while (this._scrollDirectionCache.length >= MAX_SCROLL_DIRECTION_POOL) {
|
|
456
|
+
this._scrollDirectionCache.shift();
|
|
457
|
+
}
|
|
458
|
+
this._scrollDirectionCache.push(v);
|
|
459
|
+
const dict = { [-1]: 0, [0]: 0, [1]: 0 };
|
|
460
|
+
for (let i = 0, l = this._scrollDirectionCache.length; i < l; i++) {
|
|
461
|
+
const dir = this._scrollDirectionCache[i];
|
|
462
|
+
dict[dir] += 1;
|
|
463
|
+
}
|
|
464
|
+
if (dict[-1] > dict[1]) {
|
|
465
|
+
return -1;
|
|
466
|
+
}
|
|
467
|
+
else if (dict[1] > dict[-1]) {
|
|
468
|
+
return 1;
|
|
469
|
+
}
|
|
470
|
+
return -1;
|
|
471
|
+
}
|
|
431
472
|
bumpVersion() {
|
|
432
473
|
this._version = this._version === Number.MAX_SAFE_INTEGER ? 0 : this._version + 1;
|
|
433
474
|
}
|
|
@@ -461,7 +502,7 @@ class CacheMap extends EventEmitter {
|
|
|
461
502
|
const TRACK_BOX_CHANGE_EVENT_NAME = 'change';
|
|
462
503
|
/**
|
|
463
504
|
* An object that performs tracking, calculations and caching.
|
|
464
|
-
* @link https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
505
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/14.x/projects/ng-virtual-list/src/lib/utils/trackBox.ts
|
|
465
506
|
* @author Evgenii Grebennikov
|
|
466
507
|
* @email djonnyx@gmail.com
|
|
467
508
|
*/
|
|
@@ -499,25 +540,46 @@ class TrackBox extends CacheMap {
|
|
|
499
540
|
this._debounceChanges.execute(this._version);
|
|
500
541
|
}
|
|
501
542
|
getItemPosition(id, stickyMap, options) {
|
|
502
|
-
const opt = Object.assign({ fromItemId: id, stickyMap
|
|
543
|
+
const opt = Object.assign({ fromItemId: id, stickyMap }, options);
|
|
503
544
|
const { scrollSize } = this.recalculateMetrics(opt);
|
|
504
545
|
return scrollSize;
|
|
505
546
|
}
|
|
506
547
|
updateCollection(items, stickyMap, options) {
|
|
507
|
-
const opt = Object.assign({ stickyMap
|
|
548
|
+
const opt = Object.assign({ stickyMap }, options);
|
|
508
549
|
this.cacheElements();
|
|
509
550
|
const metrics = this.recalculateMetrics(Object.assign(Object.assign({}, opt), { collection: items }));
|
|
510
551
|
const displayItems = this.generateDisplayCollection(items, stickyMap, metrics);
|
|
511
552
|
return { displayItems, totalSize: metrics.totalSize, delta: metrics.delta };
|
|
512
553
|
}
|
|
554
|
+
getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical) {
|
|
555
|
+
const sizeProperty = isVertical ? HEIGHT_PROP_NAME : WIDTH_PROP_NAME;
|
|
556
|
+
let offset = 0, num = 0;
|
|
557
|
+
for (let j = collection.length - 1; j >= i; j--) {
|
|
558
|
+
const item = collection[j];
|
|
559
|
+
let itemSize = 0;
|
|
560
|
+
if (map.has(item.id)) {
|
|
561
|
+
const bounds = map.get(item.id);
|
|
562
|
+
itemSize = bounds ? bounds[sizeProperty] : typicalItemSize;
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
itemSize = typicalItemSize;
|
|
566
|
+
}
|
|
567
|
+
offset += itemSize;
|
|
568
|
+
num++;
|
|
569
|
+
if (offset > size) {
|
|
570
|
+
return { num: 0, offset };
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return { num, offset };
|
|
574
|
+
}
|
|
513
575
|
/**
|
|
514
576
|
* Calculates list metrics
|
|
515
577
|
*/
|
|
516
578
|
recalculateMetrics(options) {
|
|
517
|
-
const {
|
|
518
|
-
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,
|
|
579
|
+
const { fromItemId, bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, stickyMap } = options;
|
|
580
|
+
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)
|
|
519
581
|
|| (typeof fromItemId === 'string' && fromItemId > '-1');
|
|
520
|
-
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,
|
|
582
|
+
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;
|
|
521
583
|
if (dynamicSize) {
|
|
522
584
|
let y = 0, stickyCollectionItem = undefined, stickyComponentSize = 0;
|
|
523
585
|
for (let i = 0, l = collection.length; i < l; i++) {
|
|
@@ -530,21 +592,31 @@ class TrackBox extends CacheMap {
|
|
|
530
592
|
else {
|
|
531
593
|
componentSize = typicalItemSize;
|
|
532
594
|
}
|
|
595
|
+
totalSize += componentSize;
|
|
533
596
|
if (isFromId) {
|
|
534
597
|
if (itemById === undefined) {
|
|
535
|
-
leftItemsWeights.push(componentSize);
|
|
536
|
-
leftHiddenItemsWeight += componentSize;
|
|
537
|
-
itemsFromStartToScrollEnd = ii;
|
|
538
598
|
if (stickyMap && stickyMap[collectionItem.id] > 0) {
|
|
539
599
|
stickyComponentSize = componentSize;
|
|
540
600
|
stickyCollectionItem = collectionItem;
|
|
541
601
|
}
|
|
542
602
|
if (collectionItem.id === fromItemId) {
|
|
543
|
-
itemById = collectionItem;
|
|
544
|
-
itemByIdPos = y;
|
|
545
603
|
if (stickyCollectionItem && stickyMap && stickyMap[stickyCollectionItem.id] > 0) {
|
|
546
|
-
|
|
604
|
+
const { num } = this.getElementNumToEnd(i, collection, map, typicalItemSize, size, isVertical);
|
|
605
|
+
if (num > 0) {
|
|
606
|
+
y -= size - componentSize;
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
y -= stickyComponentSize;
|
|
610
|
+
leftHiddenItemsWeight -= stickyComponentSize;
|
|
611
|
+
}
|
|
547
612
|
}
|
|
613
|
+
itemById = collectionItem;
|
|
614
|
+
itemByIdPos = y;
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
leftItemsWeights.push(componentSize);
|
|
618
|
+
leftHiddenItemsWeight += componentSize;
|
|
619
|
+
itemsFromStartToScrollEnd = ii;
|
|
548
620
|
}
|
|
549
621
|
}
|
|
550
622
|
}
|
|
@@ -572,7 +644,6 @@ class TrackBox extends CacheMap {
|
|
|
572
644
|
}
|
|
573
645
|
y += componentSize;
|
|
574
646
|
}
|
|
575
|
-
fullHeight = y;
|
|
576
647
|
if (itemsFromStartToScrollEnd === -1) {
|
|
577
648
|
itemsFromStartToScrollEnd = 0;
|
|
578
649
|
}
|
|
@@ -598,12 +669,12 @@ class TrackBox extends CacheMap {
|
|
|
598
669
|
rightItemsWeight = rightItemLength * typicalItemSize,
|
|
599
670
|
leftHiddenItemsWeight = itemsFromStartToScrollEnd * typicalItemSize,
|
|
600
671
|
totalItemsToDisplayEndWeight = itemsFromStartToDisplayEnd * typicalItemSize;
|
|
601
|
-
actualScrollSize = scrollSize
|
|
602
|
-
|
|
672
|
+
actualScrollSize = scrollSize;
|
|
673
|
+
totalSize = totalLength * typicalItemSize;
|
|
603
674
|
}
|
|
604
675
|
startIndex = Math.min(itemsFromStartToScrollEnd - leftItemLength, totalLength > 0 ? totalLength - 1 : 0);
|
|
605
|
-
const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta =
|
|
606
|
-
if (scrollDirection === -1) {
|
|
676
|
+
const itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight, itemsOnDisplayLength = itemsFromStartToDisplayEnd - itemsFromStartToScrollEnd, startPosition = leftHiddenItemsWeight - leftItemsWeight, renderItems = itemsOnDisplayLength + leftItemLength + rightItemLength, delta = totalSize - this._previouseFullHeigh;
|
|
677
|
+
if (this.scrollDirection === -1) {
|
|
607
678
|
this._delta += delta;
|
|
608
679
|
}
|
|
609
680
|
const metrics = {
|
|
@@ -636,13 +707,17 @@ class TrackBox extends CacheMap {
|
|
|
636
707
|
totalSize,
|
|
637
708
|
typicalItemSize,
|
|
638
709
|
};
|
|
639
|
-
|
|
640
|
-
this._previouseFullHeigh = fullHeight;
|
|
641
|
-
}
|
|
710
|
+
this._previouseFullHeigh = totalSize;
|
|
642
711
|
return metrics;
|
|
643
712
|
}
|
|
644
|
-
|
|
713
|
+
clearDeltaDirection() {
|
|
714
|
+
this.clearScrollDirectionCache();
|
|
715
|
+
}
|
|
716
|
+
clearDelta(clearDirectionDetector = false) {
|
|
645
717
|
this._delta = 0;
|
|
718
|
+
if (clearDirectionDetector) {
|
|
719
|
+
this.clearScrollDirectionCache();
|
|
720
|
+
}
|
|
646
721
|
}
|
|
647
722
|
generateDisplayCollection(items, stickyMap, metrics) {
|
|
648
723
|
var _a, _b;
|
|
@@ -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,23 +957,17 @@ 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
|
var _a, _b, _c;
|
|
891
962
|
this._$bounds.next((_c = (_b = (_a = this._container) === null || _a === void 0 ? void 0 : _a.nativeElement) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect()) !== null && _c !== void 0 ? _c : null);
|
|
892
963
|
};
|
|
893
|
-
this._scrollDirection = 0;
|
|
894
964
|
this._onScrollHandler = (e) => {
|
|
895
965
|
var _a;
|
|
896
|
-
this._$scrolledItemId.next(undefined);
|
|
897
966
|
const container = (_a = this._container) === null || _a === void 0 ? void 0 : _a.nativeElement;
|
|
898
967
|
if (container) {
|
|
899
968
|
const dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.scrollTop : container.scrollLeft), previouseScrollSize = this._$scrollSize.getValue();
|
|
900
969
|
let actualScrollSize = scrollSize;
|
|
901
|
-
this.
|
|
970
|
+
this._trackBox.deltaDirection = previouseScrollSize > scrollSize ? -1 : 1;
|
|
902
971
|
if (dynamicSize && delta !== 0) {
|
|
903
972
|
actualScrollSize = scrollSize + delta;
|
|
904
973
|
const params = {
|
|
@@ -912,13 +981,15 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
912
981
|
}
|
|
913
982
|
}
|
|
914
983
|
this._$scrollSize.next(actualScrollSize);
|
|
915
|
-
this.onScroll.emit(actualScrollSize);
|
|
984
|
+
this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
|
|
916
985
|
}
|
|
917
986
|
};
|
|
918
987
|
this._scrollImmediatelyHandler = undefined;
|
|
919
|
-
this._onScrollEndHandler = (e
|
|
988
|
+
this._onScrollEndHandler = (e) => {
|
|
920
989
|
const container = this._container;
|
|
921
990
|
if (container) {
|
|
991
|
+
this._trackBox.clearDelta();
|
|
992
|
+
this._trackBox.clearDeltaDirection();
|
|
922
993
|
const itemSize = this.itemSize, snapToItem = this.snapToItem, dynamicSize = this.dynamicSize, delta = this._trackBox.delta, scrollSize = (this._isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft);
|
|
923
994
|
let actualScrollSize = scrollSize;
|
|
924
995
|
if (dynamicSize && delta !== 0) {
|
|
@@ -928,7 +999,6 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
928
999
|
[this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: actualScrollSize,
|
|
929
1000
|
behavior: BEHAVIOR_INSTANT
|
|
930
1001
|
};
|
|
931
|
-
this._trackBox.clearDelta();
|
|
932
1002
|
this._$scrollSize.next(actualScrollSize);
|
|
933
1003
|
container.nativeElement.scroll(params);
|
|
934
1004
|
return;
|
|
@@ -946,9 +1016,7 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
946
1016
|
}
|
|
947
1017
|
}
|
|
948
1018
|
this._$scrollSize.next(actualScrollSize);
|
|
949
|
-
|
|
950
|
-
this.onScrollEnd.emit(actualScrollSize);
|
|
951
|
-
}
|
|
1019
|
+
this.onScroll.emit({ scrollSize: actualScrollSize, direction: this._trackBox.scrollDirection });
|
|
952
1020
|
}
|
|
953
1021
|
};
|
|
954
1022
|
this._$initialized = new BehaviorSubject(false);
|
|
@@ -966,7 +1034,7 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
966
1034
|
this._$initialized = new BehaviorSubject(false);
|
|
967
1035
|
this.$initialized = this._$initialized.asObservable();
|
|
968
1036
|
this._trackBox.displayComponents = this._displayComponents;
|
|
969
|
-
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
|
|
1037
|
+
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;
|
|
970
1038
|
$isVertical.pipe(takeUntil(this._$unsubscribe), tap(v => {
|
|
971
1039
|
this._isVertical = v;
|
|
972
1040
|
const el = this._elementRef.nativeElement;
|
|
@@ -975,31 +1043,19 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
975
1043
|
$dynamicSize.pipe(takeUntil(this._$unsubscribe), tap(dynamicSize => {
|
|
976
1044
|
this.listenCacheChangesIfNeed(dynamicSize);
|
|
977
1045
|
})).subscribe();
|
|
978
|
-
combineLatest([this.$initialized, $
|
|
1046
|
+
combineLatest([this.$initialized, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
|
|
979
1047
|
$itemsOffset, $snap, $isVertical, $dynamicSize, $cacheVersion,
|
|
980
|
-
]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), debounceTime(0), filter(([initialized]) => !!initialized), switchMap(([,
|
|
1048
|
+
]).pipe(takeUntil(this._$unsubscribe), distinctUntilChanged(), debounceTime(0), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, cacheVersion,]) => {
|
|
981
1049
|
const { width, height } = bounds;
|
|
982
1050
|
let actualScrollSize = scrollSize;
|
|
983
1051
|
const opts = {
|
|
984
1052
|
bounds: { width, height }, collection: items, dynamicSize, isVertical, itemSize,
|
|
985
|
-
itemsOffset, scrollSize: scrollSize, snap,
|
|
1053
|
+
itemsOffset, scrollSize: scrollSize, snap,
|
|
986
1054
|
};
|
|
987
|
-
|
|
988
|
-
const scrollSize = this._trackBox.getItemPosition(scrolledItemId, stickyMap, Object.assign(Object.assign({}, opts), { scrollSize: actualScrollSize }));
|
|
989
|
-
actualScrollSize = scrollSize;
|
|
990
|
-
this._$scrollSize.next(actualScrollSize);
|
|
991
|
-
}
|
|
992
|
-
const scrollDirection = this._scrollDirection, { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, Object.assign(Object.assign({}, opts), { scrollSize: actualScrollSize, scrollDirection }));
|
|
1055
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, Object.assign(Object.assign({}, opts), { scrollSize: actualScrollSize }));
|
|
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]) => {
|
|
@@ -1214,6 +1270,13 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1214
1270
|
* Behavior accepts the values "auto", "instant" and "smooth".
|
|
1215
1271
|
*/
|
|
1216
1272
|
scrollTo(id, behavior = BEHAVIOR_AUTO) {
|
|
1273
|
+
this.scrollToExecutor(id, behavior);
|
|
1274
|
+
}
|
|
1275
|
+
clearScrollToRepeatExecutionTimeout() {
|
|
1276
|
+
clearTimeout(this._scrollToRepeatExecutionTimeout);
|
|
1277
|
+
}
|
|
1278
|
+
scrollToExecutor(id, behavior, iteration = 0) {
|
|
1279
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1217
1280
|
const items = this.items;
|
|
1218
1281
|
if (!items || !items.length) {
|
|
1219
1282
|
return;
|
|
@@ -1225,24 +1288,33 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1225
1288
|
container.nativeElement.removeEventListener(SCROLL, this._onScrollHandler);
|
|
1226
1289
|
container.nativeElement.removeEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1227
1290
|
}
|
|
1228
|
-
const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, opts = {
|
|
1291
|
+
const { width, height } = this._$bounds.getValue() || { width: 0, height: 0 }, stickyMap = this.stickyMap, items = this.items, isVertical = this._isVertical, opts = {
|
|
1229
1292
|
bounds: { width, height }, collection: items, dynamicSize, isVertical: this._isVertical, itemSize,
|
|
1230
|
-
itemsOffset: this.itemsOffset, scrollSize:
|
|
1293
|
+
itemsOffset: this.itemsOffset, scrollSize: isVertical ? container.nativeElement.scrollTop : container.nativeElement.scrollLeft,
|
|
1231
1294
|
snap: this.snap, fromItemId: id,
|
|
1232
|
-
}, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [
|
|
1233
|
-
this._$scrolledItemId.next(id);
|
|
1295
|
+
}, scrollSize = this._trackBox.getItemPosition(id, stickyMap, opts), params = { [isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: scrollSize, behavior };
|
|
1234
1296
|
this._$scrollSize.next(scrollSize);
|
|
1235
1297
|
if (container) {
|
|
1236
1298
|
const handler = () => {
|
|
1237
1299
|
if (container) {
|
|
1238
1300
|
container.nativeElement.removeEventListener(SCROLL_END, handler);
|
|
1239
|
-
|
|
1240
|
-
this.
|
|
1301
|
+
const { displayItems, totalSize } = this._trackBox.updateCollection(items, stickyMap, Object.assign(Object.assign({}, opts), { scrollSize, fromItemId: id }));
|
|
1302
|
+
this.resetBoundsSize(isVertical, totalSize);
|
|
1303
|
+
this.createDisplayComponentsIfNeed(displayItems);
|
|
1304
|
+
this.tracking();
|
|
1305
|
+
const _scrollSize = this._trackBox.getItemPosition(id, stickyMap, Object.assign(Object.assign({}, opts), { scrollSize, fromItemId: id }));
|
|
1306
|
+
if (scrollSize < _scrollSize && iteration < MAX_SCROLL_TO_ITERATIONS) {
|
|
1307
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1308
|
+
this._scrollToRepeatExecutionTimeout = setTimeout(() => {
|
|
1309
|
+
this.scrollToExecutor(id, BEHAVIOR_INSTANT, iteration + 1);
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
else {
|
|
1313
|
+
this._$scrollSize.next(scrollSize);
|
|
1314
|
+
this.onScroll.emit({ scrollSize, direction: this._trackBox.scrollDirection });
|
|
1241
1315
|
container.nativeElement.addEventListener(SCROLL, this._onScrollHandler);
|
|
1242
1316
|
container.nativeElement.addEventListener(SCROLL_END, this._onScrollEndHandler);
|
|
1243
|
-
}
|
|
1244
|
-
this.listenCacheChangesIfNeed(dynamicSize);
|
|
1245
|
-
this.onScroll.emit(scrollSize);
|
|
1317
|
+
}
|
|
1246
1318
|
}
|
|
1247
1319
|
};
|
|
1248
1320
|
container.nativeElement.addEventListener(SCROLL_END, handler);
|
|
@@ -1272,9 +1344,7 @@ class NgVirtualListComponent extends DisposableComponent {
|
|
|
1272
1344
|
}
|
|
1273
1345
|
ngOnDestroy() {
|
|
1274
1346
|
super.ngOnDestroy();
|
|
1275
|
-
|
|
1276
|
-
clearTimeout(this._scrollToTimeout);
|
|
1277
|
-
}
|
|
1347
|
+
this.clearScrollToRepeatExecutionTimeout();
|
|
1278
1348
|
if (this._trackBox) {
|
|
1279
1349
|
this._trackBox.dispose();
|
|
1280
1350
|
}
|