ng-virtual-list 20.1.4 → 20.3.0
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 +11 -11
- package/fesm2022/ng-virtual-list.mjs +153 -40
- package/fesm2022/ng-virtual-list.mjs.map +1 -1
- package/index.d.ts +444 -69
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,10 +25,10 @@ npm i ng-virtual-list
|
|
|
25
25
|
|
|
26
26
|
Template:
|
|
27
27
|
```html
|
|
28
|
-
<ng-virtual-list class="list" direction="
|
|
29
|
-
[itemRenderer]="
|
|
28
|
+
<ng-virtual-list class="list" direction="horizontal" [items]="horizontalItems" [itemsOffset]="50"
|
|
29
|
+
[itemRenderer]="horizontalItemRenderer" [itemSize]="64"></ng-virtual-list>
|
|
30
30
|
|
|
31
|
-
<ng-template #
|
|
31
|
+
<ng-template #horizontalItemRenderer let-data="data">
|
|
32
32
|
@if (data) {
|
|
33
33
|
<div class="list__h-container" (click)="onItemClick(data)">
|
|
34
34
|
<span>{{data.name}}</span>
|
|
@@ -63,7 +63,7 @@ export class AppComponent {
|
|
|
63
63
|
|
|
64
64
|
Template:
|
|
65
65
|
```html
|
|
66
|
-
<ng-virtual-list class="list" direction="
|
|
66
|
+
<ng-virtual-list class="list" direction="horizontal" [items]="horizontalGroupItems" [itemRenderer]="horizontalGroupItemRenderer"
|
|
67
67
|
[itemsOffset]="50" [stickyMap]="horizontalGroupItemsStickyMap" [itemSize]="54" [snap]="true"></ng-virtual-list>
|
|
68
68
|
|
|
69
69
|
<ng-template #horizontalGroupItemRenderer let-data="data">
|
|
@@ -455,7 +455,7 @@ Inputs
|
|
|
455
455
|
| itemSize | number? = 24 | If direction = 'vertical', then the height of a typical element. If direction = 'horizontal', then the width of a typical element. Ignored if the dynamicSize property is true. |
|
|
456
456
|
| itemsOffset | number? = 2 | Number of elements outside the scope of visibility. Default value is 2. |
|
|
457
457
|
| itemRenderer | TemplateRef | Rendering element template. |
|
|
458
|
-
| stickyMap | [IVirtualListStickyMap?](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/models/sticky-map.model.ts) | Dictionary zIndex by id of the list element. If the value is not set or equal to 0, then a simple element is displayed, if the value is greater than 0, then the sticky position mode is enabled for the element. |
|
|
458
|
+
| stickyMap | [IVirtualListStickyMap?](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/models/sticky-map.model.ts) | Dictionary zIndex by id of the list element. If the value is not set or equal to 0, then a simple element is displayed, if the value is greater than 0, then the sticky position mode is enabled for the element. 1 - position start, 2 - position end. |
|
|
459
459
|
| snap | boolean? = false | Determines whether elements will snap. Default value is "false". |
|
|
460
460
|
| snappingMethod | [SnappingMethod? = 'normal'](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/enums/snapping-method.ts) | Snapping method. 'normal' - Normal group rendering. 'advanced' - The group is rendered on a transparent background. List items below the group are not rendered. |
|
|
461
461
|
| direction | [Direction? = 'vertical'](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/enums/direction.ts) | Determines the direction in which elements are placed. Default value is "vertical". |
|
|
@@ -488,12 +488,12 @@ Methods
|
|
|
488
488
|
|
|
489
489
|
| Angular version | ng-virtual-list version | git | npm |
|
|
490
490
|
|--|--|--|--|
|
|
491
|
-
| 19.x | 19.
|
|
492
|
-
| 18.x | 18.
|
|
493
|
-
| 17.x | 17.
|
|
494
|
-
| 16.x | 16.
|
|
495
|
-
| 15.x | 15.
|
|
496
|
-
| 14.x | 14.
|
|
491
|
+
| 19.x | 19.3.3 | [19.x](https://github.com/DjonnyX/ng-virtual-list/tree/19.x) | [19.3.3](https://www.npmjs.com/package/ng-virtual-list/v/19.3.3) |
|
|
492
|
+
| 18.x | 18.3.0 | [18.x](https://github.com/DjonnyX/ng-virtual-list/tree/18.x) | [18.3.0](https://www.npmjs.com/package/ng-virtual-list/v/18.3.0) |
|
|
493
|
+
| 17.x | 17.3.0 | [17.x](https://github.com/DjonnyX/ng-virtual-list/tree/17.x) | [17.3.0](https://www.npmjs.com/package/ng-virtual-list/v/17.3.0) |
|
|
494
|
+
| 16.x | 16.3.0 | [16.x](https://github.com/DjonnyX/ng-virtual-list/tree/16.x) | [16.3.0](https://www.npmjs.com/package/ng-virtual-list/v/16.3.0) |
|
|
495
|
+
| 15.x | 15.3.0 | [15.x](https://github.com/DjonnyX/ng-virtual-list/tree/15.x) | [15.3.0](https://www.npmjs.com/package/ng-virtual-list/v/15.3.0) |
|
|
496
|
+
| 14.x | 14.3.0 | [14.x](https://github.com/DjonnyX/ng-virtual-list/tree/14.x) | [14.3.0](https://www.npmjs.com/package/ng-virtual-list/v/14.3.0) |
|
|
497
497
|
|
|
498
498
|
<br/>
|
|
499
499
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as i0 from '@angular/core';
|
|
2
|
-
import { signal, inject, ElementRef, ChangeDetectionStrategy, Component, viewChild, output, input, ViewContainerRef, ViewChild, ViewEncapsulation } from '@angular/core';
|
|
3
1
|
import * as i1 from '@angular/common';
|
|
4
2
|
import { CommonModule } from '@angular/common';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { signal, inject, ElementRef, ChangeDetectionStrategy, Component, viewChild, output, input, ViewContainerRef, ViewChild, ViewEncapsulation } from '@angular/core';
|
|
5
5
|
import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
6
6
|
import { tap, filter, map, combineLatest, distinctUntilChanged, switchMap, of } from 'rxjs';
|
|
7
7
|
|
|
@@ -82,13 +82,22 @@ const SCROLL_END = 'scrollend';
|
|
|
82
82
|
const CLASS_LIST_VERTICAL = 'vertical';
|
|
83
83
|
const CLASS_LIST_HORIZONTAL = 'horizontal';
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Virtual List Item Interface
|
|
87
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/models/-basevirtual-list-item-component.ts
|
|
88
|
+
* @author Evgenii Grebennikov
|
|
89
|
+
* @email djonnyx@gmail.com
|
|
90
|
+
*/
|
|
91
|
+
class BaseVirtualListItemComponent {
|
|
92
|
+
}
|
|
93
|
+
|
|
85
94
|
/**
|
|
86
95
|
* Virtual list item component
|
|
87
96
|
* @link https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/components/ng-virtual-list-item.component.ts
|
|
88
97
|
* @author Evgenii Grebennikov
|
|
89
98
|
* @email djonnyx@gmail.com
|
|
90
99
|
*/
|
|
91
|
-
class NgVirtualListItemComponent {
|
|
100
|
+
class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
|
|
92
101
|
static __nextId = 0;
|
|
93
102
|
_id;
|
|
94
103
|
get id() {
|
|
@@ -128,6 +137,7 @@ class NgVirtualListItemComponent {
|
|
|
128
137
|
return this._elementRef.nativeElement;
|
|
129
138
|
}
|
|
130
139
|
constructor() {
|
|
140
|
+
super();
|
|
131
141
|
this._id = NgVirtualListItemComponent.__nextId = NgVirtualListItemComponent.__nextId === Number.MAX_SAFE_INTEGER
|
|
132
142
|
? 0 : NgVirtualListItemComponent.__nextId + 1;
|
|
133
143
|
}
|
|
@@ -135,9 +145,10 @@ class NgVirtualListItemComponent {
|
|
|
135
145
|
const data = this._data, regular = this.regular, length = this._regularLength;
|
|
136
146
|
if (data) {
|
|
137
147
|
const styles = this._elementRef.nativeElement.style;
|
|
138
|
-
styles.zIndex =
|
|
148
|
+
styles.zIndex = data.config.zIndex;
|
|
139
149
|
if (data.config.snapped) {
|
|
140
|
-
styles.transform = ZEROS_TRANSLATE_3D
|
|
150
|
+
styles.transform = data.config.sticky === 1 ? ZEROS_TRANSLATE_3D : `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.x}${PX}, ${data.config.isVertical ? data.measures.y : 0}${PX} , 0)`;
|
|
151
|
+
;
|
|
141
152
|
if (!data.config.isSnappingMethodAdvanced) {
|
|
142
153
|
styles.position = POSITION_STICKY;
|
|
143
154
|
}
|
|
@@ -173,7 +184,7 @@ class NgVirtualListItemComponent {
|
|
|
173
184
|
}
|
|
174
185
|
styles.visibility = VISIBILITY_VISIBLE;
|
|
175
186
|
}
|
|
176
|
-
styles.zIndex =
|
|
187
|
+
styles.zIndex = this._data?.config?.zIndex ?? DEFAULT_ZINDEX;
|
|
177
188
|
}
|
|
178
189
|
hide() {
|
|
179
190
|
const styles = this._elementRef.nativeElement.style;
|
|
@@ -194,7 +205,7 @@ class NgVirtualListItemComponent {
|
|
|
194
205
|
styles.zIndex = HIDDEN_ZINDEX;
|
|
195
206
|
}
|
|
196
207
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgVirtualListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
197
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: NgVirtualListItemComponent, isStandalone: true, selector: "ng-virtual-list-item", host: { classAttribute: "ngvl__item" }, ngImport: i0, template: "@let item = data();\r\n@let renderer = itemRenderer();\r\n\r\n@if (item) {\r\n <li #listItem part=\"item\" class=\"ngvl-item__container\" [ngClass]=\"{'snapped': item.config.snapped,\r\n 'snapped-out': item.config.snappedOut}\">\r\n @if (renderer) {\r\n <ng-container [ngTemplateOutlet]=\"renderer\"\r\n [ngTemplateOutletContext]=\"{data: item.data || {}, config: item.config}\" />\r\n }\r\n </li>\r\n}", styles: [":host{display:block;position:absolute;left:0;top:0;box-sizing:border-box;overflow:hidden;will-change:scroll-position}.ngvl-item__container{margin:0;padding:0;overflow:hidden;background-color:#fff;width:inherit;height:inherit}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
208
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: NgVirtualListItemComponent, isStandalone: true, selector: "ng-virtual-list-item", host: { classAttribute: "ngvl__item" }, usesInheritance: true, ngImport: i0, template: "@let item = data();\r\n@let renderer = itemRenderer();\r\n\r\n@if (item) {\r\n <li #listItem part=\"item\" class=\"ngvl-item__container\" [ngClass]=\"{'snapped': item.config.snapped,\r\n 'snapped-out': item.config.snappedOut}\">\r\n @if (renderer) {\r\n <ng-container [ngTemplateOutlet]=\"renderer\"\r\n [ngTemplateOutletContext]=\"{data: item.data || {}, config: item.config}\" />\r\n }\r\n </li>\r\n}", styles: [":host{display:block;position:absolute;left:0;top:0;box-sizing:border-box;overflow:hidden;will-change:scroll-position}.ngvl-item__container{margin:0;padding:0;overflow:hidden;background-color:#fff;width:inherit;height:inherit}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
198
209
|
}
|
|
199
210
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgVirtualListItemComponent, decorators: [{
|
|
200
211
|
type: Component,
|
|
@@ -254,12 +265,12 @@ const debounce = (cb, debounceTime = 0) => {
|
|
|
254
265
|
* @author Evgenii Grebennikov
|
|
255
266
|
* @email djonnyx@gmail.com
|
|
256
267
|
*/
|
|
257
|
-
const toggleClassName = (el, className,
|
|
268
|
+
const toggleClassName = (el, className, removeClassName) => {
|
|
258
269
|
if (!el.classList.contains(className)) {
|
|
259
270
|
el.classList.add(className);
|
|
260
271
|
}
|
|
261
|
-
|
|
262
|
-
el.classList.remove(
|
|
272
|
+
if (removeClassName) {
|
|
273
|
+
el.classList.remove(removeClassName);
|
|
263
274
|
}
|
|
264
275
|
};
|
|
265
276
|
|
|
@@ -325,7 +336,17 @@ class Tracker {
|
|
|
325
336
|
}
|
|
326
337
|
}
|
|
327
338
|
comp.instance.item = item;
|
|
328
|
-
|
|
339
|
+
if (snapedComponent) {
|
|
340
|
+
if (item['config']['snapped'] || item['config']['snappedOut']) {
|
|
341
|
+
comp.instance.hide();
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
comp.instance.show();
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
comp.instance.show();
|
|
349
|
+
}
|
|
329
350
|
untrackedItems.splice(indexByUntrackedItems, 1);
|
|
330
351
|
continue;
|
|
331
352
|
}
|
|
@@ -344,7 +365,17 @@ class Tracker {
|
|
|
344
365
|
}
|
|
345
366
|
}
|
|
346
367
|
comp.instance.item = item;
|
|
347
|
-
|
|
368
|
+
if (snapedComponent) {
|
|
369
|
+
if (item['config']['snapped'] || item['config']['snappedOut']) {
|
|
370
|
+
comp.instance.hide();
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
comp.instance.show();
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
comp.instance.show();
|
|
378
|
+
}
|
|
348
379
|
if (this._trackMap) {
|
|
349
380
|
this._trackMap[itemTrackingProperty] = comp.instance.id;
|
|
350
381
|
}
|
|
@@ -962,7 +993,7 @@ class TrackBox extends CacheMap {
|
|
|
962
993
|
}
|
|
963
994
|
}
|
|
964
995
|
if (deletedItemsMap.hasOwnProperty(i)) {
|
|
965
|
-
const bounds = deletedItemsMap[i], size = bounds[sizeProperty] ?? typicalItemSize;
|
|
996
|
+
const bounds = deletedItemsMap[i], size = bounds?.[sizeProperty] ?? typicalItemSize;
|
|
966
997
|
if (y < scrollSize - size) {
|
|
967
998
|
leftSizeOfDeletedItems += size;
|
|
968
999
|
}
|
|
@@ -970,7 +1001,7 @@ class TrackBox extends CacheMap {
|
|
|
970
1001
|
totalSize += componentSize;
|
|
971
1002
|
if (isFromId) {
|
|
972
1003
|
if (itemById === undefined) {
|
|
973
|
-
if (id !== fromItemId && stickyMap && stickyMap[id]
|
|
1004
|
+
if (id !== fromItemId && stickyMap && stickyMap[id] === 1) {
|
|
974
1005
|
stickyComponentSize = componentSize;
|
|
975
1006
|
stickyCollectionItem = collectionItem;
|
|
976
1007
|
}
|
|
@@ -1166,19 +1197,19 @@ class TrackBox extends CacheMap {
|
|
|
1166
1197
|
this.bumpVersion();
|
|
1167
1198
|
}
|
|
1168
1199
|
generateDisplayCollection(items, stickyMap, metrics) {
|
|
1169
|
-
const { normalizedItemWidth, normalizedItemHeight, dynamicSize, itemsFromStartToScrollEnd, isVertical, renderItems: renderItemsLength, scrollSize, sizeProperty, snap, snippedPos, startPosition, totalLength, startIndex, typicalItemSize, } = metrics, displayItems = [];
|
|
1200
|
+
const { width, height, normalizedItemWidth, normalizedItemHeight, dynamicSize, itemsOnDisplayLength, itemsFromStartToScrollEnd, isVertical, renderItems: renderItemsLength, scrollSize, sizeProperty, snap, snippedPos, startPosition, totalLength, startIndex, typicalItemSize, } = metrics, displayItems = [];
|
|
1170
1201
|
if (items.length) {
|
|
1171
|
-
const actualSnippedPosition = snippedPos, isSnappingMethodAdvanced = this.isSnappingMethodAdvanced;
|
|
1172
|
-
let pos = startPosition, renderItems = renderItemsLength, stickyItem, nextSticky, stickyItemIndex = -1, stickyItemSize = 0;
|
|
1202
|
+
const actualSnippedPosition = snippedPos, isSnappingMethodAdvanced = this.isSnappingMethodAdvanced, boundsSize = isVertical ? height : width, actualEndSnippedPosition = boundsSize;
|
|
1203
|
+
let pos = startPosition, renderItems = renderItemsLength, stickyItem, nextSticky, stickyItemIndex = -1, stickyItemSize = 0, endStickyItem, nextEndSticky, endStickyItemIndex = -1, endStickyItemSize = 0;
|
|
1173
1204
|
if (snap) {
|
|
1174
1205
|
for (let i = Math.min(itemsFromStartToScrollEnd > 0 ? itemsFromStartToScrollEnd : 0, totalLength - 1); i >= 0; i--) {
|
|
1175
1206
|
const id = items[i].id, sticky = stickyMap[id], size = dynamicSize ? this.get(id)?.[sizeProperty] || typicalItemSize : typicalItemSize;
|
|
1176
|
-
if (sticky
|
|
1207
|
+
if (sticky === 1) {
|
|
1177
1208
|
const measures = {
|
|
1178
1209
|
x: isVertical ? 0 : actualSnippedPosition,
|
|
1179
1210
|
y: isVertical ? actualSnippedPosition : 0,
|
|
1180
|
-
width: normalizedItemWidth,
|
|
1181
|
-
height: normalizedItemHeight,
|
|
1211
|
+
width: isVertical ? normalizedItemWidth : size,
|
|
1212
|
+
height: isVertical ? size : normalizedItemHeight,
|
|
1182
1213
|
delta: 0,
|
|
1183
1214
|
}, config = {
|
|
1184
1215
|
isVertical,
|
|
@@ -1188,6 +1219,7 @@ class TrackBox extends CacheMap {
|
|
|
1188
1219
|
snappedOut: false,
|
|
1189
1220
|
dynamic: dynamicSize,
|
|
1190
1221
|
isSnappingMethodAdvanced,
|
|
1222
|
+
zIndex: '1',
|
|
1191
1223
|
};
|
|
1192
1224
|
const itemData = items[i];
|
|
1193
1225
|
stickyItem = { id, measures, data: itemData, config };
|
|
@@ -1198,18 +1230,50 @@ class TrackBox extends CacheMap {
|
|
|
1198
1230
|
}
|
|
1199
1231
|
}
|
|
1200
1232
|
}
|
|
1233
|
+
if (snap) {
|
|
1234
|
+
const startIndex = itemsFromStartToScrollEnd + itemsOnDisplayLength - 1;
|
|
1235
|
+
for (let i = Math.min(startIndex, totalLength > 0 ? totalLength - 1 : 0), l = totalLength; i < l; i++) {
|
|
1236
|
+
const id = items[i].id, sticky = stickyMap[id], size = dynamicSize
|
|
1237
|
+
? this.get(id)?.[sizeProperty] || typicalItemSize
|
|
1238
|
+
: typicalItemSize;
|
|
1239
|
+
if (sticky === 2) {
|
|
1240
|
+
const w = isVertical ? normalizedItemWidth : size, h = isVertical ? size : normalizedItemHeight, measures = {
|
|
1241
|
+
x: isVertical ? 0 : actualEndSnippedPosition - w,
|
|
1242
|
+
y: isVertical ? actualEndSnippedPosition - h : 0,
|
|
1243
|
+
width: w,
|
|
1244
|
+
height: h,
|
|
1245
|
+
delta: 0,
|
|
1246
|
+
}, config = {
|
|
1247
|
+
isVertical,
|
|
1248
|
+
sticky,
|
|
1249
|
+
snap,
|
|
1250
|
+
snapped: true,
|
|
1251
|
+
snappedOut: false,
|
|
1252
|
+
dynamic: dynamicSize,
|
|
1253
|
+
isSnappingMethodAdvanced,
|
|
1254
|
+
zIndex: '1',
|
|
1255
|
+
};
|
|
1256
|
+
const itemData = items[i];
|
|
1257
|
+
endStickyItem = { id, measures, data: itemData, config };
|
|
1258
|
+
endStickyItemIndex = i;
|
|
1259
|
+
endStickyItemSize = size;
|
|
1260
|
+
displayItems.push(endStickyItem);
|
|
1261
|
+
break;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1201
1265
|
let i = startIndex;
|
|
1202
1266
|
while (renderItems > 0) {
|
|
1203
1267
|
if (i >= totalLength) {
|
|
1204
1268
|
break;
|
|
1205
1269
|
}
|
|
1206
1270
|
const id = items[i].id, size = dynamicSize ? this.get(id)?.[sizeProperty] || typicalItemSize : typicalItemSize;
|
|
1207
|
-
if (id !== stickyItem?.id) {
|
|
1208
|
-
const snapped = snap && stickyMap[id]
|
|
1209
|
-
x: isVertical ? 0 : pos,
|
|
1210
|
-
y: isVertical ? pos : 0,
|
|
1211
|
-
width: normalizedItemWidth,
|
|
1212
|
-
height: normalizedItemHeight,
|
|
1271
|
+
if (id !== stickyItem?.id && id !== endStickyItem?.id) {
|
|
1272
|
+
const snapped = snap && (stickyMap[id] === 1 && pos <= scrollSize || stickyMap[id] === 2 && pos >= scrollSize + boundsSize - size), measures = {
|
|
1273
|
+
x: isVertical ? stickyMap[id] === 1 ? 0 : boundsSize - size : pos,
|
|
1274
|
+
y: isVertical ? pos : stickyMap[id] === 2 ? boundsSize - size : 0,
|
|
1275
|
+
width: isVertical ? normalizedItemWidth : size,
|
|
1276
|
+
height: isVertical ? size : normalizedItemHeight,
|
|
1213
1277
|
delta: 0,
|
|
1214
1278
|
}, config = {
|
|
1215
1279
|
isVertical,
|
|
@@ -1219,15 +1283,28 @@ class TrackBox extends CacheMap {
|
|
|
1219
1283
|
snappedOut: false,
|
|
1220
1284
|
dynamic: dynamicSize,
|
|
1221
1285
|
isSnappingMethodAdvanced,
|
|
1286
|
+
zIndex: '0',
|
|
1222
1287
|
};
|
|
1288
|
+
if (snapped) {
|
|
1289
|
+
config.zIndex = '2';
|
|
1290
|
+
}
|
|
1223
1291
|
const itemData = items[i];
|
|
1224
1292
|
const item = { id, measures, data: itemData, config };
|
|
1225
|
-
if (!nextSticky && stickyItemIndex < i && stickyMap[id]
|
|
1293
|
+
if (!nextSticky && stickyItemIndex < i && stickyMap[id] === 1 && (pos <= scrollSize + size + stickyItemSize)) {
|
|
1226
1294
|
item.measures.x = isVertical ? 0 : snapped ? actualSnippedPosition : pos;
|
|
1227
1295
|
item.measures.y = isVertical ? snapped ? actualSnippedPosition : pos : 0;
|
|
1228
1296
|
nextSticky = item;
|
|
1229
1297
|
nextSticky.config.snapped = snapped;
|
|
1230
|
-
nextSticky.measures.delta = isVertical ? item.measures.y - scrollSize : item.measures.x - scrollSize;
|
|
1298
|
+
nextSticky.measures.delta = isVertical ? (item.measures.y - scrollSize) : (item.measures.x - scrollSize);
|
|
1299
|
+
nextSticky.config.zIndex = '3';
|
|
1300
|
+
}
|
|
1301
|
+
else if (!nextEndSticky && endStickyItemIndex > i && stickyMap[id] === 2 && (pos >= scrollSize + boundsSize - size - endStickyItemSize)) {
|
|
1302
|
+
item.measures.x = isVertical ? 0 : snapped ? actualEndSnippedPosition - size : pos;
|
|
1303
|
+
item.measures.y = isVertical ? snapped ? actualEndSnippedPosition - size : pos : 0;
|
|
1304
|
+
nextEndSticky = item;
|
|
1305
|
+
nextEndSticky.config.zIndex = '3';
|
|
1306
|
+
nextEndSticky.config.snapped = snapped;
|
|
1307
|
+
nextEndSticky.measures.delta = isVertical ? (item.measures.y - scrollSize) : (item.measures.x - scrollSize);
|
|
1231
1308
|
}
|
|
1232
1309
|
displayItems.push(item);
|
|
1233
1310
|
}
|
|
@@ -1249,6 +1326,19 @@ class TrackBox extends CacheMap {
|
|
|
1249
1326
|
nextSticky.measures.delta = isVertical ? nextSticky.measures.y - scrollSize : nextSticky.measures.x - scrollSize;
|
|
1250
1327
|
}
|
|
1251
1328
|
}
|
|
1329
|
+
if (nextEndSticky && endStickyItem && nextEndSticky.measures[axis] >= scrollSize + boundsSize - endStickyItemSize - nextEndSticky.measures[sizeProperty]) {
|
|
1330
|
+
if (nextEndSticky.measures[axis] < scrollSize + boundsSize - endStickyItemSize) {
|
|
1331
|
+
endStickyItem.measures[axis] = nextEndSticky.measures[axis] + nextEndSticky.measures[sizeProperty];
|
|
1332
|
+
endStickyItem.config.snapped = nextEndSticky.config.snapped = false;
|
|
1333
|
+
endStickyItem.config.snappedOut = true;
|
|
1334
|
+
endStickyItem.config.sticky = 2;
|
|
1335
|
+
endStickyItem.measures.delta = isVertical ? endStickyItem.measures.y - scrollSize : endStickyItem.measures.x - scrollSize;
|
|
1336
|
+
}
|
|
1337
|
+
else {
|
|
1338
|
+
nextEndSticky.config.snapped = true;
|
|
1339
|
+
nextEndSticky.measures.delta = isVertical ? nextEndSticky.measures.y - scrollSize : nextEndSticky.measures.x - scrollSize;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1252
1342
|
}
|
|
1253
1343
|
return displayItems;
|
|
1254
1344
|
}
|
|
@@ -1402,6 +1492,7 @@ class NgVirtualListComponent {
|
|
|
1402
1492
|
* Rendering element template.
|
|
1403
1493
|
*/
|
|
1404
1494
|
itemRenderer = input.required();
|
|
1495
|
+
_itemRenderer = signal(undefined);
|
|
1405
1496
|
/**
|
|
1406
1497
|
* Dictionary zIndex by id of the list element. If the value is not set or equal to 0,
|
|
1407
1498
|
* then a simple element is displayed, if the value is greater than 0, then the sticky position mode is enabled for the element.
|
|
@@ -1441,6 +1532,7 @@ class NgVirtualListComponent {
|
|
|
1441
1532
|
*/
|
|
1442
1533
|
snappingMethod = input(DEFAULT_SNAPPING_METHOD);
|
|
1443
1534
|
_isSnappingMethodAdvanced = this.getIsSnappingMethodAdvanced();
|
|
1535
|
+
get isSnappingMethodAdvanced() { return this._isSnappingMethodAdvanced; }
|
|
1444
1536
|
_isVertical = this.getIsVertical();
|
|
1445
1537
|
_displayComponents = [];
|
|
1446
1538
|
_snapedDisplayComponent;
|
|
@@ -1509,10 +1601,18 @@ class NgVirtualListComponent {
|
|
|
1509
1601
|
* The name of the property by which tracking is performed
|
|
1510
1602
|
*/
|
|
1511
1603
|
trackBy = input(TRACK_BY_PROPERTY_NAME);
|
|
1604
|
+
/**
|
|
1605
|
+
* Base class of the element component
|
|
1606
|
+
*/
|
|
1607
|
+
_itemComponentClass = NgVirtualListItemComponent;
|
|
1608
|
+
/**
|
|
1609
|
+
* Base class trackBox
|
|
1610
|
+
*/
|
|
1611
|
+
_trackBoxClass = TrackBox;
|
|
1512
1612
|
/**
|
|
1513
1613
|
* Dictionary of element sizes by their id
|
|
1514
1614
|
*/
|
|
1515
|
-
_trackBox = new
|
|
1615
|
+
_trackBox = new this._trackBoxClass(this.trackBy());
|
|
1516
1616
|
_onTrackBoxChangeHandler = (v) => {
|
|
1517
1617
|
this._cacheVersion.set(v);
|
|
1518
1618
|
};
|
|
@@ -1532,7 +1632,7 @@ class NgVirtualListComponent {
|
|
|
1532
1632
|
$isVertical.pipe(takeUntilDestroyed(), tap(v => {
|
|
1533
1633
|
this._isVertical = v;
|
|
1534
1634
|
const el = this._elementRef.nativeElement;
|
|
1535
|
-
toggleClassName(el, v ? CLASS_LIST_VERTICAL : CLASS_LIST_HORIZONTAL,
|
|
1635
|
+
toggleClassName(el, v ? CLASS_LIST_VERTICAL : CLASS_LIST_HORIZONTAL, v ? CLASS_LIST_HORIZONTAL : CLASS_LIST_VERTICAL);
|
|
1536
1636
|
})).subscribe();
|
|
1537
1637
|
$snappingMethod.pipe(takeUntilDestroyed(), tap(v => {
|
|
1538
1638
|
this._isSnappingMethodAdvanced = this._trackBox.isSnappingMethodAdvanced = v;
|
|
@@ -1569,12 +1669,19 @@ class NgVirtualListComponent {
|
|
|
1569
1669
|
}
|
|
1570
1670
|
return of(displayItems);
|
|
1571
1671
|
})).subscribe();
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1672
|
+
this.setupRenderer();
|
|
1673
|
+
}
|
|
1674
|
+
setupRenderer() {
|
|
1675
|
+
const $itemRenderer = toObservable(this.itemRenderer);
|
|
1676
|
+
$itemRenderer.pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(v => !!v), tap(v => {
|
|
1677
|
+
this._itemRenderer.set(v);
|
|
1678
|
+
})).subscribe();
|
|
1575
1679
|
}
|
|
1576
1680
|
/** @internal */
|
|
1577
1681
|
ngOnInit() {
|
|
1682
|
+
this.onInit();
|
|
1683
|
+
}
|
|
1684
|
+
onInit() {
|
|
1578
1685
|
this._initialized.set(true);
|
|
1579
1686
|
}
|
|
1580
1687
|
listenCacheChangesIfNeed(value) {
|
|
@@ -1604,7 +1711,7 @@ class NgVirtualListComponent {
|
|
|
1604
1711
|
}
|
|
1605
1712
|
if (this._isSnappingMethodAdvanced && this.snap()) {
|
|
1606
1713
|
if (!this._snapedDisplayComponent && this._snapContainerRef) {
|
|
1607
|
-
const comp = this._snapContainerRef.createComponent(
|
|
1714
|
+
const comp = this._snapContainerRef.createComponent(this._itemComponentClass);
|
|
1608
1715
|
comp.instance.regular = true;
|
|
1609
1716
|
this._snapedDisplayComponent = comp;
|
|
1610
1717
|
this._trackBox.snapedDisplayComponent = this._snapedDisplayComponent;
|
|
@@ -1617,7 +1724,7 @@ class NgVirtualListComponent {
|
|
|
1617
1724
|
const maxLength = displayItems.length, components = this._displayComponents;
|
|
1618
1725
|
while (components.length < maxLength) {
|
|
1619
1726
|
if (_listContainerRef) {
|
|
1620
|
-
const comp = _listContainerRef.createComponent(
|
|
1727
|
+
const comp = _listContainerRef.createComponent(this._itemComponentClass);
|
|
1621
1728
|
components.push(comp);
|
|
1622
1729
|
this._componentsResizeObserver.observe(comp.instance.element);
|
|
1623
1730
|
}
|
|
@@ -1633,13 +1740,13 @@ class NgVirtualListComponent {
|
|
|
1633
1740
|
const item = components[i];
|
|
1634
1741
|
if (item) {
|
|
1635
1742
|
const id = item.instance.id;
|
|
1636
|
-
item.instance.renderer = itemRenderer || this.
|
|
1743
|
+
item.instance.renderer = itemRenderer || this._itemRenderer();
|
|
1637
1744
|
doMap[id] = i;
|
|
1638
1745
|
}
|
|
1639
1746
|
}
|
|
1640
1747
|
if (this._isSnappingMethodAdvanced && this.snap() && this._snapedDisplayComponent && this._snapContainerRef) {
|
|
1641
1748
|
const comp = this._snapedDisplayComponent;
|
|
1642
|
-
comp.instance.renderer = itemRenderer || this.
|
|
1749
|
+
comp.instance.renderer = itemRenderer || this._itemRenderer();
|
|
1643
1750
|
}
|
|
1644
1751
|
this._trackBox.setDisplayObjectIndexMapById(doMap);
|
|
1645
1752
|
}
|
|
@@ -1757,6 +1864,9 @@ class NgVirtualListComponent {
|
|
|
1757
1864
|
};
|
|
1758
1865
|
/** @internal */
|
|
1759
1866
|
ngAfterViewInit() {
|
|
1867
|
+
this.afterViewInit();
|
|
1868
|
+
}
|
|
1869
|
+
afterViewInit() {
|
|
1760
1870
|
const containerEl = this._container();
|
|
1761
1871
|
if (containerEl) {
|
|
1762
1872
|
// for direction calculation
|
|
@@ -1770,6 +1880,9 @@ class NgVirtualListComponent {
|
|
|
1770
1880
|
}
|
|
1771
1881
|
/** @internal */
|
|
1772
1882
|
ngOnDestroy() {
|
|
1883
|
+
this.dispose();
|
|
1884
|
+
}
|
|
1885
|
+
dispose() {
|
|
1773
1886
|
this.clearScrollToRepeatExecutionTimeout();
|
|
1774
1887
|
if (this._trackBox) {
|
|
1775
1888
|
this._trackBox.dispose();
|
|
@@ -1800,11 +1913,11 @@ class NgVirtualListComponent {
|
|
|
1800
1913
|
}
|
|
1801
1914
|
}
|
|
1802
1915
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgVirtualListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1803
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: NgVirtualListComponent, isStandalone: true, selector: "ng-virtual-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, snap: { classPropertyName: "snap", publicName: "snap", isSignal: true, isRequired: false, transformFunction: null }, enabledBufferOptimization: { classPropertyName: "enabledBufferOptimization", publicName: "enabledBufferOptimization", isSignal: true, isRequired: false, transformFunction: null }, itemRenderer: { classPropertyName: "itemRenderer", publicName: "itemRenderer", isSignal: true, isRequired: true, transformFunction: null }, stickyMap: { classPropertyName: "stickyMap", publicName: "stickyMap", isSignal: true, isRequired: false, transformFunction: null }, itemSize: { classPropertyName: "itemSize", publicName: "itemSize", isSignal: true, isRequired: false, transformFunction: null }, dynamicSize: { classPropertyName: "dynamicSize", publicName: "dynamicSize", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, itemsOffset: { classPropertyName: "itemsOffset", publicName: "itemsOffset", isSignal: true, isRequired: false, transformFunction: null }, snappingMethod: { classPropertyName: "snappingMethod", publicName: "snappingMethod", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd" }, viewQueries: [{ propertyName: "_snappedContainer", first: true, predicate: ["snapped"], descendants: true, isSignal: true }, { propertyName: "_container", first: true, predicate: ["container"], descendants: true, isSignal: true }, { propertyName: "_list", first: true, predicate: ["list"], descendants: true, isSignal: true }, { propertyName: "_listContainerRef", first: true, predicate: ["renderersContainer"], descendants: true, read: ViewContainerRef }, { propertyName: "_snapContainerRef", first: true, predicate: ["snapRendererContainer"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "@if (snap()) {\r\n<div #snapped part=\"snapped-item\" class=\"ngvl__list-snapper\">\r\n <ng-container #snapRendererContainer></ng-container>\r\n</div>\r\n}\r\n<div #container part=\"scroller\" class=\"ngvl__scroller\">\r\n <ul #list part=\"list\" class=\"ngvl__list\">\r\n <ng-container #renderersContainer></ng-container>\r\n </ul>\r\n</div>", styles: [":host{position:relative;display:block;width:400px;overflow:hidden}:host(.horizontal){height:48px}:host(.vertical){height:320px}.ngvl__scroller{overflow:auto;width:100%;height:100%}.ngvl__list-snapper{pointer-events:none;position:absolute;list-style:none;left:0;top:0;z-index:1}.ngvl__list{position:relative;list-style:none;padding:0;margin:0;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
1916
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: NgVirtualListComponent, isStandalone: true, selector: "ng-virtual-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, snap: { classPropertyName: "snap", publicName: "snap", isSignal: true, isRequired: false, transformFunction: null }, enabledBufferOptimization: { classPropertyName: "enabledBufferOptimization", publicName: "enabledBufferOptimization", isSignal: true, isRequired: false, transformFunction: null }, itemRenderer: { classPropertyName: "itemRenderer", publicName: "itemRenderer", isSignal: true, isRequired: true, transformFunction: null }, stickyMap: { classPropertyName: "stickyMap", publicName: "stickyMap", isSignal: true, isRequired: false, transformFunction: null }, itemSize: { classPropertyName: "itemSize", publicName: "itemSize", isSignal: true, isRequired: false, transformFunction: null }, dynamicSize: { classPropertyName: "dynamicSize", publicName: "dynamicSize", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, itemsOffset: { classPropertyName: "itemsOffset", publicName: "itemsOffset", isSignal: true, isRequired: false, transformFunction: null }, snappingMethod: { classPropertyName: "snappingMethod", publicName: "snappingMethod", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd" }, viewQueries: [{ propertyName: "_snappedContainer", first: true, predicate: ["snapped"], descendants: true, isSignal: true }, { propertyName: "_container", first: true, predicate: ["container"], descendants: true, isSignal: true }, { propertyName: "_list", first: true, predicate: ["list"], descendants: true, isSignal: true }, { propertyName: "_listContainerRef", first: true, predicate: ["renderersContainer"], descendants: true, read: ViewContainerRef }, { propertyName: "_snapContainerRef", first: true, predicate: ["snapRendererContainer"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "@if (snap()) {\r\n<div #snapped part=\"snapped-item\" class=\"ngvl__list-snapper\">\r\n <ng-container #snapRendererContainer></ng-container>\r\n</div>\r\n}\r\n<div #container part=\"scroller\" class=\"ngvl__scroller\">\r\n <ul #list part=\"list\" class=\"ngvl__list\">\r\n <ng-container #renderersContainer></ng-container>\r\n </ul>\r\n</div>", styles: [":host{position:relative;display:block;width:400px;overflow:hidden}:host(.horizontal){height:48px}:host(.horizontal) .ngvl__list{display:inline-flex}:host(.horizontal) .ngvl__scroller{overflow:auto hidden}:host(.vertical) .ngvl__scroller{overflow:hidden auto}:host(.vertical){height:320px}.ngvl__scroller{overflow:auto;width:100%;height:100%}.ngvl__list-snapper{pointer-events:none;position:absolute;list-style:none;left:0;top:0;z-index:1}.ngvl__list{position:relative;list-style:none;padding:0;margin:0;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
1804
1917
|
}
|
|
1805
1918
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgVirtualListComponent, decorators: [{
|
|
1806
1919
|
type: Component,
|
|
1807
|
-
args: [{ selector: 'ng-virtual-list', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, template: "@if (snap()) {\r\n<div #snapped part=\"snapped-item\" class=\"ngvl__list-snapper\">\r\n <ng-container #snapRendererContainer></ng-container>\r\n</div>\r\n}\r\n<div #container part=\"scroller\" class=\"ngvl__scroller\">\r\n <ul #list part=\"list\" class=\"ngvl__list\">\r\n <ng-container #renderersContainer></ng-container>\r\n </ul>\r\n</div>", styles: [":host{position:relative;display:block;width:400px;overflow:hidden}:host(.horizontal){height:48px}:host(.vertical){height:320px}.ngvl__scroller{overflow:auto;width:100%;height:100%}.ngvl__list-snapper{pointer-events:none;position:absolute;list-style:none;left:0;top:0;z-index:1}.ngvl__list{position:relative;list-style:none;padding:0;margin:0;width:100%;height:100%}\n"] }]
|
|
1920
|
+
args: [{ selector: 'ng-virtual-list', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, template: "@if (snap()) {\r\n<div #snapped part=\"snapped-item\" class=\"ngvl__list-snapper\">\r\n <ng-container #snapRendererContainer></ng-container>\r\n</div>\r\n}\r\n<div #container part=\"scroller\" class=\"ngvl__scroller\">\r\n <ul #list part=\"list\" class=\"ngvl__list\">\r\n <ng-container #renderersContainer></ng-container>\r\n </ul>\r\n</div>", styles: [":host{position:relative;display:block;width:400px;overflow:hidden}:host(.horizontal){height:48px}:host(.horizontal) .ngvl__list{display:inline-flex}:host(.horizontal) .ngvl__scroller{overflow:auto hidden}:host(.vertical) .ngvl__scroller{overflow:hidden auto}:host(.vertical){height:320px}.ngvl__scroller{overflow:auto;width:100%;height:100%}.ngvl__list-snapper{pointer-events:none;position:absolute;list-style:none;left:0;top:0;z-index:1}.ngvl__list{position:relative;list-style:none;padding:0;margin:0;width:100%;height:100%}\n"] }]
|
|
1808
1921
|
}], ctorParameters: () => [], propDecorators: { _listContainerRef: [{
|
|
1809
1922
|
type: ViewChild,
|
|
1810
1923
|
args: ['renderersContainer', { read: ViewContainerRef }]
|
|
@@ -1821,5 +1934,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
|
|
|
1821
1934
|
* Generated bundle index. Do not edit.
|
|
1822
1935
|
*/
|
|
1823
1936
|
|
|
1824
|
-
export { Directions, NgVirtualListComponent, SnappingMethods };
|
|
1937
|
+
export { BaseVirtualListItemComponent, Directions, NgVirtualListComponent, NgVirtualListItemComponent, SnappingMethods };
|
|
1825
1938
|
//# sourceMappingURL=ng-virtual-list.mjs.map
|