ng-virtual-list 19.1.44 → 19.2.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 +7 -0
- package/fesm2022/ng-virtual-list.mjs +217 -31
- package/fesm2022/ng-virtual-list.mjs.map +1 -1
- package/lib/components/ng-virtual-list-item.component.d.ts +4 -0
- package/lib/const/index.d.ts +6 -0
- package/lib/enums/index.d.ts +4 -1
- package/lib/enums/snapping-method.d.ts +10 -0
- package/lib/enums/snapping-methods.d.ts +16 -0
- package/lib/models/render-item-config.model.d.ts +4 -0
- package/lib/models/render-item.model.d.ts +6 -1
- package/lib/ng-virtual-list.component.d.ts +18 -4
- package/lib/utils/snapping-method.d.ts +3 -0
- package/lib/utils/trackBox.d.ts +4 -0
- package/lib/utils/tracker.d.ts +1 -1
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
package/README.md
CHANGED
|
@@ -427,6 +427,13 @@ List items are encapsulated in shadowDOM, so to override default styles you need
|
|
|
427
427
|
}
|
|
428
428
|
```
|
|
429
429
|
|
|
430
|
+
- Set up the snapped item (Only SnappingMethod.ADVANCED)
|
|
431
|
+
```css
|
|
432
|
+
.list::part(snapped-item) {
|
|
433
|
+
color: #71718c;
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
430
437
|
- Set up the list item
|
|
431
438
|
```css
|
|
432
439
|
.list::part(item) {
|
|
@@ -23,6 +23,24 @@ var Directions;
|
|
|
23
23
|
Directions["VERTICAL"] = "vertical";
|
|
24
24
|
})(Directions || (Directions = {}));
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Snapping method.
|
|
28
|
+
* @link https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/enums/snapping-method.ts
|
|
29
|
+
* @author Evgenii Grebennikov
|
|
30
|
+
* @email djonnyx@gmail.com
|
|
31
|
+
*/
|
|
32
|
+
var SnappingMethods;
|
|
33
|
+
(function (SnappingMethods) {
|
|
34
|
+
/**
|
|
35
|
+
* Normal group rendering.
|
|
36
|
+
*/
|
|
37
|
+
SnappingMethods["NORMAL"] = "normal";
|
|
38
|
+
/**
|
|
39
|
+
* The group is rendered on a transparent background. List items below the group are not rendered.
|
|
40
|
+
*/
|
|
41
|
+
SnappingMethods["ADVANCED"] = "advanced";
|
|
42
|
+
})(SnappingMethods || (SnappingMethods = {}));
|
|
43
|
+
|
|
26
44
|
const DEFAULT_ITEM_SIZE = 24;
|
|
27
45
|
const DEFAULT_ITEMS_OFFSET = 2;
|
|
28
46
|
const DEFAULT_LIST_SIZE = 400;
|
|
@@ -33,10 +51,15 @@ const TRACK_BY_PROPERTY_NAME = 'id';
|
|
|
33
51
|
const DEFAULT_DIRECTION = Directions.VERTICAL;
|
|
34
52
|
const DISPLAY_OBJECTS_LENGTH_MESUREMENT_ERROR = 1;
|
|
35
53
|
const MAX_SCROLL_TO_ITERATIONS = 5;
|
|
54
|
+
const DEFAULT_SNAPPING_METHOD = SnappingMethods.NORMAL;
|
|
36
55
|
// presets
|
|
37
56
|
const BEHAVIOR_AUTO = 'auto';
|
|
38
57
|
const BEHAVIOR_INSTANT = 'instant';
|
|
39
58
|
const BEHAVIOR_SMOOTH = 'smooth';
|
|
59
|
+
const DISPLAY_BLOCK = 'block';
|
|
60
|
+
const DISPLAY_NONE = 'none';
|
|
61
|
+
const OPACITY_0 = '0';
|
|
62
|
+
const OPACITY_100 = '100';
|
|
40
63
|
const VISIBILITY_VISIBLE = 'visible';
|
|
41
64
|
const VISIBILITY_HIDDEN = 'hidden';
|
|
42
65
|
const SIZE_100_PERSENT = '100%';
|
|
@@ -71,6 +94,7 @@ class NgVirtualListItemComponent {
|
|
|
71
94
|
get id() {
|
|
72
95
|
return this._id;
|
|
73
96
|
}
|
|
97
|
+
regular = false;
|
|
74
98
|
data = signal(undefined);
|
|
75
99
|
_data = undefined;
|
|
76
100
|
set item(v) {
|
|
@@ -78,22 +102,17 @@ class NgVirtualListItemComponent {
|
|
|
78
102
|
return;
|
|
79
103
|
}
|
|
80
104
|
const data = this._data = v;
|
|
81
|
-
|
|
82
|
-
const styles = this._elementRef.nativeElement.style;
|
|
83
|
-
styles.zIndex = String(data.config.sticky);
|
|
84
|
-
if (data.config.snapped) {
|
|
85
|
-
styles.transform = ZEROS_TRANSLATE_3D;
|
|
86
|
-
styles.position = POSITION_STICKY;
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
styles.position = POSITION_ABSOLUTE;
|
|
90
|
-
styles.transform = `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.x}${PX}, ${data.config.isVertical ? data.measures.y : 0}${PX} , 0)`;
|
|
91
|
-
}
|
|
92
|
-
styles.height = data.config.isVertical ? data.config.dynamic ? SIZE_AUTO : `${data.measures.height}${PX}` : SIZE_100_PERSENT;
|
|
93
|
-
styles.width = data.config.isVertical ? SIZE_100_PERSENT : data.config.dynamic ? SIZE_AUTO : `${data.measures.width}${PX}`;
|
|
94
|
-
}
|
|
105
|
+
this.update();
|
|
95
106
|
this.data.set(v);
|
|
96
107
|
}
|
|
108
|
+
_regularLength = SIZE_100_PERSENT;
|
|
109
|
+
set regularLength(v) {
|
|
110
|
+
if (this._regularLength === v) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
this._regularLength = v;
|
|
114
|
+
this.update();
|
|
115
|
+
}
|
|
97
116
|
get item() {
|
|
98
117
|
return this._data;
|
|
99
118
|
}
|
|
@@ -112,25 +131,65 @@ class NgVirtualListItemComponent {
|
|
|
112
131
|
this._id = NgVirtualListItemComponent.__nextId = NgVirtualListItemComponent.__nextId === Number.MAX_SAFE_INTEGER
|
|
113
132
|
? 0 : NgVirtualListItemComponent.__nextId + 1;
|
|
114
133
|
}
|
|
134
|
+
update() {
|
|
135
|
+
const data = this._data, regular = this.regular, length = this._regularLength;
|
|
136
|
+
if (data) {
|
|
137
|
+
const styles = this._elementRef.nativeElement.style;
|
|
138
|
+
styles.zIndex = String(data.config.sticky);
|
|
139
|
+
if (data.config.snapped) {
|
|
140
|
+
styles.transform = ZEROS_TRANSLATE_3D;
|
|
141
|
+
if (!data.config.isSnappingMethodAdvanced) {
|
|
142
|
+
styles.position = POSITION_STICKY;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
styles.position = POSITION_ABSOLUTE;
|
|
147
|
+
if (regular) {
|
|
148
|
+
styles.transform = `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.delta}${PX}, ${data.config.isVertical ? data.measures.delta : 0}${PX} , 0)`;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
styles.transform = `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.x}${PX}, ${data.config.isVertical ? data.measures.y : 0}${PX} , 0)`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
styles.height = data.config.isVertical ? data.config.dynamic ? SIZE_AUTO : `${data.measures.height}${PX}` : regular ? length : SIZE_100_PERSENT;
|
|
155
|
+
styles.width = data.config.isVertical ? regular ? length : SIZE_100_PERSENT : data.config.dynamic ? SIZE_AUTO : `${data.measures.width}${PX}`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
115
158
|
getBounds() {
|
|
116
159
|
const el = this._elementRef.nativeElement, { width, height } = el.getBoundingClientRect();
|
|
117
160
|
return { width, height };
|
|
118
161
|
}
|
|
119
162
|
show() {
|
|
120
163
|
const styles = this._elementRef.nativeElement.style;
|
|
121
|
-
if (
|
|
122
|
-
|
|
164
|
+
if (this.regular) {
|
|
165
|
+
if (styles.display === DISPLAY_BLOCK) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
styles.display = DISPLAY_BLOCK;
|
|
123
169
|
}
|
|
124
|
-
|
|
125
|
-
|
|
170
|
+
else {
|
|
171
|
+
if (styles.visibility === VISIBILITY_VISIBLE) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
styles.visibility = VISIBILITY_VISIBLE;
|
|
175
|
+
}
|
|
176
|
+
styles.zIndex = String(this._data?.config?.sticky ?? DEFAULT_ZINDEX);
|
|
126
177
|
}
|
|
127
178
|
hide() {
|
|
128
179
|
const styles = this._elementRef.nativeElement.style;
|
|
129
|
-
if (
|
|
130
|
-
|
|
180
|
+
if (this.regular) {
|
|
181
|
+
if (styles.display === DISPLAY_NONE) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
styles.display = DISPLAY_NONE;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
if (styles.visibility === VISIBILITY_HIDDEN) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
styles.visibility = VISIBILITY_HIDDEN;
|
|
131
191
|
}
|
|
132
192
|
styles.position = POSITION_ABSOLUTE;
|
|
133
|
-
styles.visibility = VISIBILITY_HIDDEN;
|
|
134
193
|
styles.transform = ZEROS_TRANSLATE_3D;
|
|
135
194
|
styles.zIndex = HIDDEN_ZINDEX;
|
|
136
195
|
}
|
|
@@ -241,11 +300,12 @@ class Tracker {
|
|
|
241
300
|
/**
|
|
242
301
|
* tracking by propName
|
|
243
302
|
*/
|
|
244
|
-
track(items, components, direction) {
|
|
303
|
+
track(items, components, snapedComponent, direction) {
|
|
245
304
|
if (!items) {
|
|
246
305
|
return;
|
|
247
306
|
}
|
|
248
307
|
const idPropName = this._trackingPropertyName, untrackedItems = [...components], isDown = direction === 0 || direction === 1;
|
|
308
|
+
let isRegularSnapped = false;
|
|
249
309
|
for (let i = isDown ? 0 : items.length - 1, l = isDown ? items.length : 0; isDown ? i < l : i >= l; isDown ? i++ : i--) {
|
|
250
310
|
const item = items[i], itemTrackingProperty = item[idPropName];
|
|
251
311
|
if (this._trackMap) {
|
|
@@ -257,6 +317,13 @@ class Tracker {
|
|
|
257
317
|
return v.instance.id === compId;
|
|
258
318
|
});
|
|
259
319
|
if (indexByUntrackedItems > -1) {
|
|
320
|
+
if (snapedComponent) {
|
|
321
|
+
if (item['config']['snapped'] || item['config']['snappedOut']) {
|
|
322
|
+
isRegularSnapped = true;
|
|
323
|
+
snapedComponent.instance.item = item;
|
|
324
|
+
snapedComponent.instance.show();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
260
327
|
comp.instance.item = item;
|
|
261
328
|
comp.instance.show();
|
|
262
329
|
untrackedItems.splice(indexByUntrackedItems, 1);
|
|
@@ -269,6 +336,13 @@ class Tracker {
|
|
|
269
336
|
if (untrackedItems.length > 0) {
|
|
270
337
|
const comp = untrackedItems.shift(), item = items[i];
|
|
271
338
|
if (comp) {
|
|
339
|
+
if (snapedComponent) {
|
|
340
|
+
if (item['config']['snapped'] || item['config']['snappedOut']) {
|
|
341
|
+
isRegularSnapped = true;
|
|
342
|
+
snapedComponent.instance.item = item;
|
|
343
|
+
snapedComponent.instance.show();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
272
346
|
comp.instance.item = item;
|
|
273
347
|
comp.instance.show();
|
|
274
348
|
if (this._trackMap) {
|
|
@@ -283,6 +357,11 @@ class Tracker {
|
|
|
283
357
|
comp.instance.hide();
|
|
284
358
|
}
|
|
285
359
|
}
|
|
360
|
+
if (!isRegularSnapped) {
|
|
361
|
+
if (snapedComponent) {
|
|
362
|
+
snapedComponent.instance.hide();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
286
365
|
}
|
|
287
366
|
untrackComponentByIdProperty(component) {
|
|
288
367
|
if (!component) {
|
|
@@ -599,6 +678,20 @@ class TrackBox extends CacheMap {
|
|
|
599
678
|
}
|
|
600
679
|
this._displayComponents = v;
|
|
601
680
|
}
|
|
681
|
+
_snapedDisplayComponent;
|
|
682
|
+
set snapedDisplayComponent(v) {
|
|
683
|
+
if (this._snapedDisplayComponent === v) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
this._snapedDisplayComponent = v;
|
|
687
|
+
}
|
|
688
|
+
_isSnappingMethodAdvanced = false;
|
|
689
|
+
set isSnappingMethodAdvanced(v) {
|
|
690
|
+
if (this._isSnappingMethodAdvanced === v) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
this._isSnappingMethodAdvanced = v;
|
|
694
|
+
}
|
|
602
695
|
/**
|
|
603
696
|
* Set the trackBy property
|
|
604
697
|
*/
|
|
@@ -1074,7 +1167,7 @@ class TrackBox extends CacheMap {
|
|
|
1074
1167
|
generateDisplayCollection(items, stickyMap, metrics) {
|
|
1075
1168
|
const { normalizedItemWidth, normalizedItemHeight, dynamicSize, itemsFromStartToScrollEnd, isVertical, renderItems: renderItemsLength, scrollSize, sizeProperty, snap, snippedPos, startPosition, totalLength, startIndex, typicalItemSize, } = metrics, displayItems = [];
|
|
1076
1169
|
if (items.length) {
|
|
1077
|
-
const actualSnippedPosition = snippedPos;
|
|
1170
|
+
const actualSnippedPosition = snippedPos, isSnappingMethodAdvanced = this.isSnappingMethodAdvanced;
|
|
1078
1171
|
let pos = startPosition, renderItems = renderItemsLength, stickyItem, nextSticky, stickyItemIndex = -1, stickyItemSize = 0;
|
|
1079
1172
|
if (snap) {
|
|
1080
1173
|
for (let i = Math.min(itemsFromStartToScrollEnd > 0 ? itemsFromStartToScrollEnd : 0, totalLength - 1); i >= 0; i--) {
|
|
@@ -1085,6 +1178,7 @@ class TrackBox extends CacheMap {
|
|
|
1085
1178
|
y: isVertical ? actualSnippedPosition : 0,
|
|
1086
1179
|
width: normalizedItemWidth,
|
|
1087
1180
|
height: normalizedItemHeight,
|
|
1181
|
+
delta: 0,
|
|
1088
1182
|
}, config = {
|
|
1089
1183
|
isVertical,
|
|
1090
1184
|
sticky,
|
|
@@ -1092,6 +1186,7 @@ class TrackBox extends CacheMap {
|
|
|
1092
1186
|
snapped: true,
|
|
1093
1187
|
snappedOut: false,
|
|
1094
1188
|
dynamic: dynamicSize,
|
|
1189
|
+
isSnappingMethodAdvanced,
|
|
1095
1190
|
};
|
|
1096
1191
|
const itemData = items[i];
|
|
1097
1192
|
stickyItem = { id, measures, data: itemData, config };
|
|
@@ -1114,6 +1209,7 @@ class TrackBox extends CacheMap {
|
|
|
1114
1209
|
y: isVertical ? pos : 0,
|
|
1115
1210
|
width: normalizedItemWidth,
|
|
1116
1211
|
height: normalizedItemHeight,
|
|
1212
|
+
delta: 0,
|
|
1117
1213
|
}, config = {
|
|
1118
1214
|
isVertical,
|
|
1119
1215
|
sticky: stickyMap[id],
|
|
@@ -1121,6 +1217,7 @@ class TrackBox extends CacheMap {
|
|
|
1121
1217
|
snapped: false,
|
|
1122
1218
|
snappedOut: false,
|
|
1123
1219
|
dynamic: dynamicSize,
|
|
1220
|
+
isSnappingMethodAdvanced,
|
|
1124
1221
|
};
|
|
1125
1222
|
const itemData = items[i];
|
|
1126
1223
|
const item = { id, measures, data: itemData, config };
|
|
@@ -1129,6 +1226,7 @@ class TrackBox extends CacheMap {
|
|
|
1129
1226
|
item.measures.y = isVertical ? snapped ? actualSnippedPosition : pos : 0;
|
|
1130
1227
|
nextSticky = item;
|
|
1131
1228
|
nextSticky.config.snapped = snapped;
|
|
1229
|
+
nextSticky.measures.delta = isVertical ? item.measures.y - scrollSize : item.measures.x - scrollSize;
|
|
1132
1230
|
}
|
|
1133
1231
|
displayItems.push(item);
|
|
1134
1232
|
}
|
|
@@ -1143,9 +1241,11 @@ class TrackBox extends CacheMap {
|
|
|
1143
1241
|
stickyItem.config.snapped = nextSticky.config.snapped = false;
|
|
1144
1242
|
stickyItem.config.snappedOut = true;
|
|
1145
1243
|
stickyItem.config.sticky = 1;
|
|
1244
|
+
stickyItem.measures.delta = isVertical ? stickyItem.measures.y - scrollSize : stickyItem.measures.x - scrollSize;
|
|
1146
1245
|
}
|
|
1147
1246
|
else {
|
|
1148
1247
|
nextSticky.config.snapped = true;
|
|
1248
|
+
nextSticky.measures.delta = isVertical ? nextSticky.measures.y - scrollSize : nextSticky.measures.x - scrollSize;
|
|
1149
1249
|
}
|
|
1150
1250
|
}
|
|
1151
1251
|
}
|
|
@@ -1158,7 +1258,7 @@ class TrackBox extends CacheMap {
|
|
|
1158
1258
|
if (!this._items || !this._displayComponents) {
|
|
1159
1259
|
return;
|
|
1160
1260
|
}
|
|
1161
|
-
this._tracker.track(this._items, this._displayComponents, this.scrollDirection);
|
|
1261
|
+
this._tracker.track(this._items, this._displayComponents, this._snapedDisplayComponent, this.scrollDirection);
|
|
1162
1262
|
}
|
|
1163
1263
|
setDisplayObjectIndexMapById(v) {
|
|
1164
1264
|
this._tracker.displayObjectIndexMapById = v;
|
|
@@ -1235,6 +1335,14 @@ class ScrollEvent {
|
|
|
1235
1335
|
}
|
|
1236
1336
|
}
|
|
1237
1337
|
|
|
1338
|
+
const ADVANCED_PATTERNS = [SnappingMethods.ADVANCED, 'advanced'], DEFAULT_PATTERN = [SnappingMethods.NORMAL, 'normal'];
|
|
1339
|
+
const isSnappingMethodAdvenced = (method) => {
|
|
1340
|
+
return ADVANCED_PATTERNS.includes(method);
|
|
1341
|
+
};
|
|
1342
|
+
const isSnappingMethodDefault = (method) => {
|
|
1343
|
+
return DEFAULT_PATTERN.includes(method);
|
|
1344
|
+
};
|
|
1345
|
+
|
|
1238
1346
|
/**
|
|
1239
1347
|
* Virtual list component.
|
|
1240
1348
|
* Maximum performance for extremely large lists.
|
|
@@ -1251,6 +1359,8 @@ class NgVirtualListComponent {
|
|
|
1251
1359
|
*/
|
|
1252
1360
|
get id() { return this._id; }
|
|
1253
1361
|
_listContainerRef;
|
|
1362
|
+
_snapContainerRef;
|
|
1363
|
+
_snappedContainer = viewChild('snapped');
|
|
1254
1364
|
_container = viewChild('container');
|
|
1255
1365
|
_list = viewChild('list');
|
|
1256
1366
|
/**
|
|
@@ -1320,11 +1430,51 @@ class NgVirtualListComponent {
|
|
|
1320
1430
|
* Number of elements outside the scope of visibility. Default value is 2.
|
|
1321
1431
|
*/
|
|
1322
1432
|
itemsOffset = input(DEFAULT_ITEMS_OFFSET);
|
|
1433
|
+
/**
|
|
1434
|
+
* Snapping method.
|
|
1435
|
+
* 'default' - Normal group rendering.
|
|
1436
|
+
* 'advanced' - The group is rendered on a transparent background. List items below the group are not rendered.
|
|
1437
|
+
*/
|
|
1438
|
+
snappingMethod = input(DEFAULT_SNAPPING_METHOD);
|
|
1439
|
+
_isSnappingMethodAdvanced = this.getIsSnappingMethodAdvanced();
|
|
1323
1440
|
_isVertical = this.getIsVertical();
|
|
1324
1441
|
_displayComponents = [];
|
|
1442
|
+
_snapedDisplayComponent;
|
|
1325
1443
|
_bounds = signal(null);
|
|
1326
1444
|
_scrollSize = signal(0);
|
|
1327
1445
|
_resizeObserver = null;
|
|
1446
|
+
_resizeSnappedComponentHandler = () => {
|
|
1447
|
+
const list = this._list(), container = this._container(), snappedComponent = this._snapedDisplayComponent?.instance;
|
|
1448
|
+
if (list && container && snappedComponent) {
|
|
1449
|
+
const isVertical = this._isVertical;
|
|
1450
|
+
snappedComponent.regularLength = `${isVertical ? list.nativeElement.offsetWidth : list.nativeElement.offsetHeight}${PX}`;
|
|
1451
|
+
const { width: sWidth, height: sHeight } = snappedComponent.getBounds() ?? { width: 0, height: 0 }, containerElement = container.nativeElement, listElement = list?.nativeElement, { width: lWidth, height: lHeight } = listElement?.getBoundingClientRect() ?? { width: 0, height: 0 }, { width, height } = this._bounds() ?? { width: 0, height: 0 };
|
|
1452
|
+
let left, right, top, bottom, scrollBarSize;
|
|
1453
|
+
if (isVertical) {
|
|
1454
|
+
const snappedY = snappedComponent.item?.measures.y ?? 0, scrollSize = container.nativeElement.scrollTop, delta = snappedY - scrollSize;
|
|
1455
|
+
delta;
|
|
1456
|
+
scrollBarSize = width - lWidth;
|
|
1457
|
+
left = 0;
|
|
1458
|
+
right = width - scrollBarSize;
|
|
1459
|
+
top = sHeight;
|
|
1460
|
+
bottom = height;
|
|
1461
|
+
containerElement.style.clipPath = `path("M 0 ${top + delta} L 0 ${height} L ${width} ${height} L ${width} 0 L ${right} 0 L ${right} ${top + delta} Z")`;
|
|
1462
|
+
}
|
|
1463
|
+
else {
|
|
1464
|
+
const snappedX = snappedComponent.item?.measures.x ?? 0, scrollSize = container.nativeElement.scrollLeft, delta = snappedX - scrollSize;
|
|
1465
|
+
scrollBarSize = height - lHeight;
|
|
1466
|
+
left = sWidth;
|
|
1467
|
+
right = width;
|
|
1468
|
+
top = 0;
|
|
1469
|
+
bottom = height - scrollBarSize;
|
|
1470
|
+
containerElement.style.clipPath = `path("M ${left + delta} 0 L ${left + delta} ${bottom} L 0 ${bottom} L 0 ${height} L ${width} ${height} L ${width} 0 Z")`;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
_resizeSnappedObserver = null;
|
|
1475
|
+
_componentsResizeObserver = new ResizeObserver(() => {
|
|
1476
|
+
this._trackBox.changes();
|
|
1477
|
+
});
|
|
1328
1478
|
_onResizeHandler = () => {
|
|
1329
1479
|
const bounds = this._container()?.nativeElement?.getBoundingClientRect();
|
|
1330
1480
|
if (bounds) {
|
|
@@ -1333,6 +1483,9 @@ class NgVirtualListComponent {
|
|
|
1333
1483
|
else {
|
|
1334
1484
|
this._bounds.set({ width: DEFAULT_LIST_SIZE, height: DEFAULT_LIST_SIZE });
|
|
1335
1485
|
}
|
|
1486
|
+
if (this._isSnappingMethodAdvanced) {
|
|
1487
|
+
this.updateRegularRenderer();
|
|
1488
|
+
}
|
|
1336
1489
|
};
|
|
1337
1490
|
_onScrollHandler = (e) => {
|
|
1338
1491
|
this.clearScrollToRepeatExecutionTimeout();
|
|
@@ -1368,12 +1521,15 @@ class NgVirtualListComponent {
|
|
|
1368
1521
|
$trackBy.pipe(takeUntilDestroyed(), tap(v => {
|
|
1369
1522
|
this._trackBox.trackingPropertyName = v;
|
|
1370
1523
|
})).subscribe();
|
|
1371
|
-
const $bounds = toObservable(this._bounds).pipe(filter(b => !!b)), $items = toObservable(this.items).pipe(map(i => !i ? [] : i)), $scrollSize = toObservable(this._scrollSize), $itemSize = toObservable(this.itemSize).pipe(map(v => v <= 0 ? DEFAULT_ITEM_SIZE : v)), $itemsOffset = toObservable(this.itemsOffset).pipe(map(v => v < 0 ? DEFAULT_ITEMS_OFFSET : v)), $stickyMap = toObservable(this.stickyMap).pipe(map(v => !v ? {} : v)), $snap = toObservable(this.snap), $isVertical = toObservable(this.direction).pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $dynamicSize = toObservable(this.dynamicSize), $enabledBufferOptimization = toObservable(this.enabledBufferOptimization), $cacheVersion = toObservable(this._cacheVersion);
|
|
1524
|
+
const $bounds = toObservable(this._bounds).pipe(filter(b => !!b)), $items = toObservable(this.items).pipe(map(i => !i ? [] : i)), $scrollSize = toObservable(this._scrollSize), $itemSize = toObservable(this.itemSize).pipe(map(v => v <= 0 ? DEFAULT_ITEM_SIZE : v)), $itemsOffset = toObservable(this.itemsOffset).pipe(map(v => v < 0 ? DEFAULT_ITEMS_OFFSET : v)), $stickyMap = toObservable(this.stickyMap).pipe(map(v => !v ? {} : v)), $snap = toObservable(this.snap), $isVertical = toObservable(this.direction).pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $dynamicSize = toObservable(this.dynamicSize), $enabledBufferOptimization = toObservable(this.enabledBufferOptimization), $snappingMethod = toObservable(this.snappingMethod).pipe(map(v => this.getIsSnappingMethodAdvanced(v || DEFAULT_SNAPPING_METHOD))), $cacheVersion = toObservable(this._cacheVersion);
|
|
1372
1525
|
$isVertical.pipe(takeUntilDestroyed(), tap(v => {
|
|
1373
1526
|
this._isVertical = v;
|
|
1374
1527
|
const el = this._elementRef.nativeElement;
|
|
1375
1528
|
toggleClassName(el, v ? CLASS_LIST_VERTICAL : CLASS_LIST_HORIZONTAL, true);
|
|
1376
1529
|
})).subscribe();
|
|
1530
|
+
$snappingMethod.pipe(takeUntilDestroyed(), tap(v => {
|
|
1531
|
+
this._isSnappingMethodAdvanced = this._trackBox.isSnappingMethodAdvanced = v;
|
|
1532
|
+
})).subscribe();
|
|
1377
1533
|
$dynamicSize.pipe(takeUntilDestroyed(), tap(dynamicSize => {
|
|
1378
1534
|
this.listenCacheChangesIfNeed(dynamicSize);
|
|
1379
1535
|
})).subscribe();
|
|
@@ -1388,6 +1544,9 @@ class NgVirtualListComponent {
|
|
|
1388
1544
|
this.resetBoundsSize(isVertical, totalSize);
|
|
1389
1545
|
this.createDisplayComponentsIfNeed(displayItems);
|
|
1390
1546
|
this.tracking();
|
|
1547
|
+
if (this._isSnappingMethodAdvanced) {
|
|
1548
|
+
this.updateRegularRenderer();
|
|
1549
|
+
}
|
|
1391
1550
|
const container = this._container();
|
|
1392
1551
|
if (container) {
|
|
1393
1552
|
const delta = this._trackBox.delta;
|
|
@@ -1423,18 +1582,29 @@ class NgVirtualListComponent {
|
|
|
1423
1582
|
}
|
|
1424
1583
|
}
|
|
1425
1584
|
}
|
|
1585
|
+
getIsSnappingMethodAdvanced(m) {
|
|
1586
|
+
const method = m || this.snappingMethod();
|
|
1587
|
+
return isSnappingMethodAdvenced(method);
|
|
1588
|
+
}
|
|
1426
1589
|
getIsVertical(d) {
|
|
1427
1590
|
const dir = d || this.direction();
|
|
1428
1591
|
return isDirection(dir, Directions.VERTICAL);
|
|
1429
1592
|
}
|
|
1430
|
-
_componentsResizeObserver = new ResizeObserver(() => {
|
|
1431
|
-
this._trackBox.changes();
|
|
1432
|
-
});
|
|
1433
1593
|
createDisplayComponentsIfNeed(displayItems) {
|
|
1434
1594
|
if (!displayItems || !this._listContainerRef) {
|
|
1435
1595
|
this._trackBox.setDisplayObjectIndexMapById({});
|
|
1436
1596
|
return;
|
|
1437
1597
|
}
|
|
1598
|
+
if (this._isSnappingMethodAdvanced && this.snap()) {
|
|
1599
|
+
if (!this._snapedDisplayComponent && this._snapContainerRef) {
|
|
1600
|
+
const comp = this._snapContainerRef.createComponent(NgVirtualListItemComponent);
|
|
1601
|
+
comp.instance.regular = true;
|
|
1602
|
+
this._snapedDisplayComponent = comp;
|
|
1603
|
+
this._trackBox.snapedDisplayComponent = this._snapedDisplayComponent;
|
|
1604
|
+
this._resizeSnappedObserver = new ResizeObserver(this._resizeSnappedComponentHandler);
|
|
1605
|
+
this._resizeSnappedObserver.observe(comp.instance.element);
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1438
1608
|
this._trackBox.items = displayItems;
|
|
1439
1609
|
const _listContainerRef = this._listContainerRef;
|
|
1440
1610
|
const maxLength = displayItems.length, components = this._displayComponents;
|
|
@@ -1447,6 +1617,9 @@ class NgVirtualListComponent {
|
|
|
1447
1617
|
}
|
|
1448
1618
|
this.resetRenderers();
|
|
1449
1619
|
}
|
|
1620
|
+
updateRegularRenderer() {
|
|
1621
|
+
this._resizeSnappedComponentHandler();
|
|
1622
|
+
}
|
|
1450
1623
|
resetRenderers(itemRenderer) {
|
|
1451
1624
|
const doMap = {}, components = this._displayComponents;
|
|
1452
1625
|
for (let i = 0, l = components.length; i < l; i++) {
|
|
@@ -1457,6 +1630,10 @@ class NgVirtualListComponent {
|
|
|
1457
1630
|
doMap[id] = i;
|
|
1458
1631
|
}
|
|
1459
1632
|
}
|
|
1633
|
+
if (this._isSnappingMethodAdvanced && this.snap() && this._snapedDisplayComponent && this._snapContainerRef) {
|
|
1634
|
+
const comp = this._snapedDisplayComponent;
|
|
1635
|
+
comp.instance.renderer = itemRenderer || this.itemRenderer();
|
|
1636
|
+
}
|
|
1460
1637
|
this._trackBox.setDisplayObjectIndexMapById(doMap);
|
|
1461
1638
|
}
|
|
1462
1639
|
/**
|
|
@@ -1598,10 +1775,16 @@ class NgVirtualListComponent {
|
|
|
1598
1775
|
if (this._componentsResizeObserver) {
|
|
1599
1776
|
this._componentsResizeObserver.disconnect();
|
|
1600
1777
|
}
|
|
1778
|
+
if (this._resizeSnappedObserver) {
|
|
1779
|
+
this._resizeSnappedObserver.disconnect();
|
|
1780
|
+
}
|
|
1601
1781
|
if (this._resizeObserver) {
|
|
1602
1782
|
this._resizeObserver.disconnect();
|
|
1603
1783
|
}
|
|
1604
1784
|
}
|
|
1785
|
+
if (this._snapedDisplayComponent) {
|
|
1786
|
+
this._snapedDisplayComponent.destroy();
|
|
1787
|
+
}
|
|
1605
1788
|
if (this._displayComponents) {
|
|
1606
1789
|
while (this._displayComponents.length > 0) {
|
|
1607
1790
|
const comp = this._displayComponents.pop();
|
|
@@ -1610,14 +1793,17 @@ class NgVirtualListComponent {
|
|
|
1610
1793
|
}
|
|
1611
1794
|
}
|
|
1612
1795
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1613
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
1796
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", 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{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 });
|
|
1614
1797
|
}
|
|
1615
1798
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListComponent, decorators: [{
|
|
1616
1799
|
type: Component,
|
|
1617
|
-
args: [{ selector: 'ng-virtual-list', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, template: "<div #container part=\"scroller\" class=\"
|
|
1800
|
+
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{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"] }]
|
|
1618
1801
|
}], ctorParameters: () => [], propDecorators: { _listContainerRef: [{
|
|
1619
1802
|
type: ViewChild,
|
|
1620
1803
|
args: ['renderersContainer', { read: ViewContainerRef }]
|
|
1804
|
+
}], _snapContainerRef: [{
|
|
1805
|
+
type: ViewChild,
|
|
1806
|
+
args: ['snapRendererContainer', { read: ViewContainerRef }]
|
|
1621
1807
|
}] } });
|
|
1622
1808
|
|
|
1623
1809
|
/*
|
|
@@ -1628,5 +1814,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
1628
1814
|
* Generated bundle index. Do not edit.
|
|
1629
1815
|
*/
|
|
1630
1816
|
|
|
1631
|
-
export { NgVirtualListComponent };
|
|
1817
|
+
export { Directions, NgVirtualListComponent, SnappingMethods };
|
|
1632
1818
|
//# sourceMappingURL=ng-virtual-list.mjs.map
|