@ahmedhfarag/ngx-virtual-scroller 4.0.3
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/LICENSE +21 -0
- package/README.md +808 -0
- package/fesm2022/ahmedfharag-ngx-virtual-scroller.mjs +1071 -0
- package/fesm2022/ahmedfharag-ngx-virtual-scroller.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/package.json +45 -0
- package/virtual-scroller.d.ts +172 -0
@@ -0,0 +1,1071 @@
|
|
1
|
+
import * as i0 from '@angular/core';
|
2
|
+
import { EventEmitter, PLATFORM_ID, ElementRef, ContentChild, ViewChild, Output, Input, Inject, Optional, Component } from '@angular/core';
|
3
|
+
import { isPlatformServer, CommonModule } from '@angular/common';
|
4
|
+
import * as tween from '@tweenjs/tween.js';
|
5
|
+
|
6
|
+
function VIRTUAL_SCROLLER_DEFAULT_OPTIONS_FACTORY() {
|
7
|
+
return {
|
8
|
+
checkResizeInterval: 1000,
|
9
|
+
modifyOverflowStyleOfParentScroll: true,
|
10
|
+
resizeBypassRefreshThreshold: 5,
|
11
|
+
scrollAnimationTime: 750,
|
12
|
+
scrollDebounceTime: 0,
|
13
|
+
scrollThrottlingTime: 0,
|
14
|
+
stripedTable: false
|
15
|
+
};
|
16
|
+
}
|
17
|
+
class VirtualScrollerComponent {
|
18
|
+
element;
|
19
|
+
renderer;
|
20
|
+
zone;
|
21
|
+
changeDetectorRef;
|
22
|
+
viewPortItems;
|
23
|
+
window = window;
|
24
|
+
get viewPortInfo() {
|
25
|
+
let pageInfo = this.previousViewPort || {};
|
26
|
+
return {
|
27
|
+
startIndex: pageInfo.startIndex || 0,
|
28
|
+
endIndex: pageInfo.endIndex || 0,
|
29
|
+
scrollStartPosition: pageInfo.scrollStartPosition || 0,
|
30
|
+
scrollEndPosition: pageInfo.scrollEndPosition || 0,
|
31
|
+
maxScrollPosition: pageInfo.maxScrollPosition || 0,
|
32
|
+
startIndexWithBuffer: pageInfo.startIndexWithBuffer || 0,
|
33
|
+
endIndexWithBuffer: pageInfo.endIndexWithBuffer || 0
|
34
|
+
};
|
35
|
+
}
|
36
|
+
executeRefreshOutsideAngularZone = false;
|
37
|
+
_enableUnequalChildrenSizes = false;
|
38
|
+
get enableUnequalChildrenSizes() {
|
39
|
+
return this._enableUnequalChildrenSizes;
|
40
|
+
}
|
41
|
+
set enableUnequalChildrenSizes(value) {
|
42
|
+
if (this._enableUnequalChildrenSizes === value) {
|
43
|
+
return;
|
44
|
+
}
|
45
|
+
this._enableUnequalChildrenSizes = value;
|
46
|
+
this.minMeasuredChildWidth = undefined;
|
47
|
+
this.minMeasuredChildHeight = undefined;
|
48
|
+
}
|
49
|
+
RTL = false;
|
50
|
+
useMarginInsteadOfTranslate = false;
|
51
|
+
modifyOverflowStyleOfParentScroll;
|
52
|
+
stripedTable;
|
53
|
+
scrollbarWidth;
|
54
|
+
scrollbarHeight;
|
55
|
+
childWidth;
|
56
|
+
childHeight;
|
57
|
+
ssrChildWidth;
|
58
|
+
ssrChildHeight;
|
59
|
+
ssrViewportWidth = 1920;
|
60
|
+
ssrViewportHeight = 1080;
|
61
|
+
_bufferAmount;
|
62
|
+
get bufferAmount() {
|
63
|
+
if (typeof (this._bufferAmount) === 'number' && this._bufferAmount >= 0) {
|
64
|
+
return this._bufferAmount;
|
65
|
+
}
|
66
|
+
else {
|
67
|
+
return this.enableUnequalChildrenSizes ? 5 : 0;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
set bufferAmount(value) {
|
71
|
+
this._bufferAmount = value;
|
72
|
+
}
|
73
|
+
scrollAnimationTime;
|
74
|
+
resizeBypassRefreshThreshold;
|
75
|
+
_scrollThrottlingTime;
|
76
|
+
get scrollThrottlingTime() {
|
77
|
+
return this._scrollThrottlingTime;
|
78
|
+
}
|
79
|
+
set scrollThrottlingTime(value) {
|
80
|
+
this._scrollThrottlingTime = value;
|
81
|
+
this.updateOnScrollFunction();
|
82
|
+
}
|
83
|
+
_scrollDebounceTime;
|
84
|
+
get scrollDebounceTime() {
|
85
|
+
return this._scrollDebounceTime;
|
86
|
+
}
|
87
|
+
set scrollDebounceTime(value) {
|
88
|
+
this._scrollDebounceTime = value;
|
89
|
+
this.updateOnScrollFunction();
|
90
|
+
}
|
91
|
+
onScroll;
|
92
|
+
updateOnScrollFunction() {
|
93
|
+
if (this.scrollDebounceTime) {
|
94
|
+
this.onScroll = this.debounce(() => {
|
95
|
+
this.refresh_internal(false);
|
96
|
+
}, this.scrollDebounceTime);
|
97
|
+
}
|
98
|
+
else if (this.scrollThrottlingTime) {
|
99
|
+
this.onScroll = this.throttleTrailing(() => {
|
100
|
+
this.refresh_internal(false);
|
101
|
+
}, this.scrollThrottlingTime);
|
102
|
+
}
|
103
|
+
else {
|
104
|
+
this.onScroll = () => {
|
105
|
+
this.refresh_internal(false);
|
106
|
+
};
|
107
|
+
}
|
108
|
+
}
|
109
|
+
checkScrollElementResizedTimer;
|
110
|
+
_checkResizeInterval;
|
111
|
+
get checkResizeInterval() {
|
112
|
+
return this._checkResizeInterval;
|
113
|
+
}
|
114
|
+
set checkResizeInterval(value) {
|
115
|
+
if (this._checkResizeInterval === value) {
|
116
|
+
return;
|
117
|
+
}
|
118
|
+
this._checkResizeInterval = value;
|
119
|
+
this.addScrollEventHandlers();
|
120
|
+
}
|
121
|
+
_items = [];
|
122
|
+
get items() {
|
123
|
+
return this._items;
|
124
|
+
}
|
125
|
+
set items(value) {
|
126
|
+
if (value === this._items) {
|
127
|
+
return;
|
128
|
+
}
|
129
|
+
this._items = value || [];
|
130
|
+
this.refresh_internal(true);
|
131
|
+
}
|
132
|
+
compareItems = (item1, item2) => item1 === item2;
|
133
|
+
_horizontal;
|
134
|
+
get horizontal() {
|
135
|
+
return this._horizontal;
|
136
|
+
}
|
137
|
+
set horizontal(value) {
|
138
|
+
this._horizontal = value;
|
139
|
+
this.updateDirection();
|
140
|
+
}
|
141
|
+
revertParentOverscroll() {
|
142
|
+
const scrollElement = this.getScrollElement();
|
143
|
+
if (scrollElement && this.oldParentScrollOverflow) {
|
144
|
+
scrollElement.style['overflow-y'] = this.oldParentScrollOverflow.y;
|
145
|
+
scrollElement.style['overflow-x'] = this.oldParentScrollOverflow.x;
|
146
|
+
}
|
147
|
+
this.oldParentScrollOverflow = undefined;
|
148
|
+
}
|
149
|
+
oldParentScrollOverflow;
|
150
|
+
_parentScroll;
|
151
|
+
get parentScroll() {
|
152
|
+
return this._parentScroll;
|
153
|
+
}
|
154
|
+
set parentScroll(value) {
|
155
|
+
if (this._parentScroll === value) {
|
156
|
+
return;
|
157
|
+
}
|
158
|
+
this.revertParentOverscroll();
|
159
|
+
this._parentScroll = value;
|
160
|
+
this.addScrollEventHandlers();
|
161
|
+
const scrollElement = this.getScrollElement();
|
162
|
+
if (this.modifyOverflowStyleOfParentScroll && scrollElement !== this.element.nativeElement) {
|
163
|
+
this.oldParentScrollOverflow = { x: scrollElement.style['overflow-x'], y: scrollElement.style['overflow-y'] };
|
164
|
+
scrollElement.style['overflow-y'] = this.horizontal ? 'visible' : 'auto';
|
165
|
+
scrollElement.style['overflow-x'] = this.horizontal ? 'auto' : 'visible';
|
166
|
+
}
|
167
|
+
}
|
168
|
+
vsUpdate = new EventEmitter();
|
169
|
+
vsChange = new EventEmitter();
|
170
|
+
vsStart = new EventEmitter();
|
171
|
+
vsEnd = new EventEmitter();
|
172
|
+
contentElementRef;
|
173
|
+
invisiblePaddingElementRef;
|
174
|
+
headerElementRef;
|
175
|
+
containerElementRef;
|
176
|
+
ngOnInit() {
|
177
|
+
this.addScrollEventHandlers();
|
178
|
+
}
|
179
|
+
ngOnDestroy() {
|
180
|
+
this.removeScrollEventHandlers();
|
181
|
+
this.revertParentOverscroll();
|
182
|
+
}
|
183
|
+
ngOnChanges(changes) {
|
184
|
+
let indexLengthChanged = this.cachedItemsLength !== this.items.length;
|
185
|
+
this.cachedItemsLength = this.items.length;
|
186
|
+
const firstRun = !changes.items || !changes.items.previousValue || changes.items.previousValue.length === 0;
|
187
|
+
this.refresh_internal(indexLengthChanged || firstRun);
|
188
|
+
}
|
189
|
+
ngDoCheck() {
|
190
|
+
if (this.cachedItemsLength !== this.items.length) {
|
191
|
+
this.cachedItemsLength = this.items.length;
|
192
|
+
this.refresh_internal(true);
|
193
|
+
return;
|
194
|
+
}
|
195
|
+
if (this.previousViewPort && this.viewPortItems && this.viewPortItems.length > 0) {
|
196
|
+
let itemsArrayChanged = false;
|
197
|
+
for (let i = 0; i < this.viewPortItems.length; ++i) {
|
198
|
+
if (!this.compareItems(this.items[this.previousViewPort.startIndexWithBuffer + i], this.viewPortItems[i])) {
|
199
|
+
itemsArrayChanged = true;
|
200
|
+
break;
|
201
|
+
}
|
202
|
+
}
|
203
|
+
if (itemsArrayChanged) {
|
204
|
+
this.refresh_internal(true);
|
205
|
+
}
|
206
|
+
}
|
207
|
+
}
|
208
|
+
refresh() {
|
209
|
+
this.refresh_internal(true);
|
210
|
+
}
|
211
|
+
invalidateAllCachedMeasurements() {
|
212
|
+
this.wrapGroupDimensions = {
|
213
|
+
maxChildSizePerWrapGroup: [],
|
214
|
+
numberOfKnownWrapGroupChildSizes: 0,
|
215
|
+
sumOfKnownWrapGroupChildWidths: 0,
|
216
|
+
sumOfKnownWrapGroupChildHeights: 0
|
217
|
+
};
|
218
|
+
this.minMeasuredChildWidth = undefined;
|
219
|
+
this.minMeasuredChildHeight = undefined;
|
220
|
+
this.refresh_internal(false);
|
221
|
+
}
|
222
|
+
invalidateCachedMeasurementForItem(item) {
|
223
|
+
if (this.enableUnequalChildrenSizes) {
|
224
|
+
let index = this.items && this.items.indexOf(item);
|
225
|
+
if (index >= 0) {
|
226
|
+
this.invalidateCachedMeasurementAtIndex(index);
|
227
|
+
}
|
228
|
+
}
|
229
|
+
else {
|
230
|
+
this.minMeasuredChildWidth = undefined;
|
231
|
+
this.minMeasuredChildHeight = undefined;
|
232
|
+
}
|
233
|
+
this.refresh_internal(false);
|
234
|
+
}
|
235
|
+
invalidateCachedMeasurementAtIndex(index) {
|
236
|
+
if (this.enableUnequalChildrenSizes) {
|
237
|
+
let cachedMeasurement = this.wrapGroupDimensions.maxChildSizePerWrapGroup[index];
|
238
|
+
if (cachedMeasurement) {
|
239
|
+
this.wrapGroupDimensions.maxChildSizePerWrapGroup[index] = undefined;
|
240
|
+
--this.wrapGroupDimensions.numberOfKnownWrapGroupChildSizes;
|
241
|
+
this.wrapGroupDimensions.sumOfKnownWrapGroupChildWidths -= cachedMeasurement.childWidth || 0;
|
242
|
+
this.wrapGroupDimensions.sumOfKnownWrapGroupChildHeights -= cachedMeasurement.childHeight || 0;
|
243
|
+
}
|
244
|
+
}
|
245
|
+
else {
|
246
|
+
this.minMeasuredChildWidth = undefined;
|
247
|
+
this.minMeasuredChildHeight = undefined;
|
248
|
+
}
|
249
|
+
this.refresh_internal(false);
|
250
|
+
}
|
251
|
+
scrollInto(item, alignToBeginning = true, additionalOffset = 0, animationMilliseconds = undefined, animationCompletedCallback = undefined) {
|
252
|
+
let index = this.items.indexOf(item);
|
253
|
+
if (index === -1) {
|
254
|
+
return;
|
255
|
+
}
|
256
|
+
this.scrollToIndex(index, alignToBeginning, additionalOffset, animationMilliseconds, animationCompletedCallback);
|
257
|
+
}
|
258
|
+
scrollToIndex(index, alignToBeginning = true, additionalOffset = 0, animationMilliseconds = undefined, animationCompletedCallback = undefined) {
|
259
|
+
let maxRetries = 5;
|
260
|
+
let retryIfNeeded = () => {
|
261
|
+
--maxRetries;
|
262
|
+
if (maxRetries <= 0) {
|
263
|
+
if (animationCompletedCallback) {
|
264
|
+
animationCompletedCallback();
|
265
|
+
}
|
266
|
+
return;
|
267
|
+
}
|
268
|
+
let dimensions = this.calculateDimensions();
|
269
|
+
let desiredStartIndex = Math.min(Math.max(index, 0), dimensions.itemCount - 1);
|
270
|
+
if (this.previousViewPort.startIndex === desiredStartIndex) {
|
271
|
+
if (animationCompletedCallback) {
|
272
|
+
animationCompletedCallback();
|
273
|
+
}
|
274
|
+
return;
|
275
|
+
}
|
276
|
+
this.scrollToIndex_internal(index, alignToBeginning, additionalOffset, 0, retryIfNeeded);
|
277
|
+
};
|
278
|
+
this.scrollToIndex_internal(index, alignToBeginning, additionalOffset, animationMilliseconds, retryIfNeeded);
|
279
|
+
}
|
280
|
+
scrollToIndex_internal(index, alignToBeginning = true, additionalOffset = 0, animationMilliseconds = undefined, animationCompletedCallback = undefined) {
|
281
|
+
animationMilliseconds = animationMilliseconds === undefined ? this.scrollAnimationTime : animationMilliseconds;
|
282
|
+
let dimensions = this.calculateDimensions();
|
283
|
+
let scroll = this.calculatePadding(index, dimensions) + additionalOffset;
|
284
|
+
if (!alignToBeginning) {
|
285
|
+
scroll -= dimensions.wrapGroupsPerPage * dimensions[this._childScrollDim];
|
286
|
+
}
|
287
|
+
this.scrollToPosition(scroll, animationMilliseconds, animationCompletedCallback);
|
288
|
+
}
|
289
|
+
scrollToPosition(scrollPosition, animationMilliseconds = undefined, animationCompletedCallback = undefined) {
|
290
|
+
scrollPosition += this.getElementsOffset();
|
291
|
+
animationMilliseconds = animationMilliseconds === undefined ? this.scrollAnimationTime : animationMilliseconds;
|
292
|
+
let scrollElement = this.getScrollElement();
|
293
|
+
let animationRequest;
|
294
|
+
if (this.currentTween) {
|
295
|
+
this.currentTween.stop();
|
296
|
+
this.currentTween = undefined;
|
297
|
+
}
|
298
|
+
if (!animationMilliseconds) {
|
299
|
+
this.renderer.setProperty(scrollElement, this._scrollType, scrollPosition);
|
300
|
+
this.refresh_internal(false, animationCompletedCallback);
|
301
|
+
return;
|
302
|
+
}
|
303
|
+
const tweenConfigObj = { scrollPosition: scrollElement[this._scrollType] };
|
304
|
+
let newTween = new tween.Tween(tweenConfigObj)
|
305
|
+
.to({ scrollPosition }, animationMilliseconds)
|
306
|
+
.easing(tween.Easing.Quadratic.Out)
|
307
|
+
.onUpdate((data) => {
|
308
|
+
if (isNaN(data.scrollPosition)) {
|
309
|
+
return;
|
310
|
+
}
|
311
|
+
this.renderer.setProperty(scrollElement, this._scrollType, data.scrollPosition);
|
312
|
+
this.refresh_internal(false);
|
313
|
+
})
|
314
|
+
.onStop(() => {
|
315
|
+
cancelAnimationFrame(animationRequest);
|
316
|
+
})
|
317
|
+
.start();
|
318
|
+
const animate = (time) => {
|
319
|
+
if (!newTween["isPlaying"]()) {
|
320
|
+
return;
|
321
|
+
}
|
322
|
+
newTween.update(time);
|
323
|
+
if (tweenConfigObj.scrollPosition === scrollPosition) {
|
324
|
+
this.refresh_internal(false, animationCompletedCallback);
|
325
|
+
return;
|
326
|
+
}
|
327
|
+
this.zone.runOutsideAngular(() => {
|
328
|
+
animationRequest = requestAnimationFrame(animate);
|
329
|
+
});
|
330
|
+
};
|
331
|
+
animate();
|
332
|
+
this.currentTween = newTween;
|
333
|
+
}
|
334
|
+
isAngularUniversalSSR;
|
335
|
+
constructor(element, renderer, zone, changeDetectorRef, platformId, options) {
|
336
|
+
this.element = element;
|
337
|
+
this.renderer = renderer;
|
338
|
+
this.zone = zone;
|
339
|
+
this.changeDetectorRef = changeDetectorRef;
|
340
|
+
this.isAngularUniversalSSR = isPlatformServer(platformId);
|
341
|
+
this.checkResizeInterval = options.checkResizeInterval;
|
342
|
+
this.modifyOverflowStyleOfParentScroll = options.modifyOverflowStyleOfParentScroll;
|
343
|
+
this.resizeBypassRefreshThreshold = options.resizeBypassRefreshThreshold;
|
344
|
+
this.scrollAnimationTime = options.scrollAnimationTime;
|
345
|
+
this.scrollDebounceTime = options.scrollDebounceTime;
|
346
|
+
this.scrollThrottlingTime = options.scrollThrottlingTime;
|
347
|
+
this.scrollbarHeight = options.scrollbarHeight;
|
348
|
+
this.scrollbarWidth = options.scrollbarWidth;
|
349
|
+
this.stripedTable = options.stripedTable;
|
350
|
+
this.horizontal = false;
|
351
|
+
this.resetWrapGroupDimensions();
|
352
|
+
}
|
353
|
+
getElementSize(element) {
|
354
|
+
const result = element.getBoundingClientRect();
|
355
|
+
const styles = getComputedStyle(element);
|
356
|
+
const marginTop = parseInt(styles['margin-top'], 10) || 0;
|
357
|
+
const marginBottom = parseInt(styles['margin-bottom'], 10) || 0;
|
358
|
+
const marginLeft = parseInt(styles['margin-left'], 10) || 0;
|
359
|
+
const marginRight = parseInt(styles['margin-right'], 10) || 0;
|
360
|
+
return {
|
361
|
+
x: result.left,
|
362
|
+
y: result.top,
|
363
|
+
top: result.top + marginTop,
|
364
|
+
bottom: result.bottom + marginBottom,
|
365
|
+
left: result.left + marginLeft,
|
366
|
+
right: result.right + marginRight,
|
367
|
+
width: result.width + marginLeft + marginRight,
|
368
|
+
height: result.height + marginTop + marginBottom,
|
369
|
+
toJSON: () => null
|
370
|
+
};
|
371
|
+
}
|
372
|
+
previousScrollBoundingRect;
|
373
|
+
checkScrollElementResized() {
|
374
|
+
let boundingRect = this.getElementSize(this.getScrollElement());
|
375
|
+
let sizeChanged;
|
376
|
+
if (!this.previousScrollBoundingRect) {
|
377
|
+
sizeChanged = true;
|
378
|
+
}
|
379
|
+
else {
|
380
|
+
let widthChange = Math.abs(boundingRect.width - this.previousScrollBoundingRect.width);
|
381
|
+
let heightChange = Math.abs(boundingRect.height - this.previousScrollBoundingRect.height);
|
382
|
+
sizeChanged = widthChange > this.resizeBypassRefreshThreshold || heightChange > this.resizeBypassRefreshThreshold;
|
383
|
+
}
|
384
|
+
if (sizeChanged) {
|
385
|
+
this.previousScrollBoundingRect = boundingRect;
|
386
|
+
if (boundingRect.width > 0 && boundingRect.height > 0) {
|
387
|
+
this.refresh_internal(false);
|
388
|
+
}
|
389
|
+
}
|
390
|
+
}
|
391
|
+
_invisiblePaddingProperty;
|
392
|
+
_offsetType;
|
393
|
+
_scrollType;
|
394
|
+
_pageOffsetType;
|
395
|
+
_childScrollDim;
|
396
|
+
_translateDir;
|
397
|
+
_marginDir;
|
398
|
+
updateDirection() {
|
399
|
+
if (this.horizontal) {
|
400
|
+
this._childScrollDim = 'childWidth';
|
401
|
+
this._invisiblePaddingProperty = 'scaleX';
|
402
|
+
this._marginDir = 'margin-left';
|
403
|
+
this._offsetType = 'offsetLeft';
|
404
|
+
this._pageOffsetType = 'pageXOffset';
|
405
|
+
this._scrollType = 'scrollLeft';
|
406
|
+
this._translateDir = 'translateX';
|
407
|
+
}
|
408
|
+
else {
|
409
|
+
this._childScrollDim = 'childHeight';
|
410
|
+
this._invisiblePaddingProperty = 'scaleY';
|
411
|
+
this._marginDir = 'margin-top';
|
412
|
+
this._offsetType = 'offsetTop';
|
413
|
+
this._pageOffsetType = 'pageYOffset';
|
414
|
+
this._scrollType = 'scrollTop';
|
415
|
+
this._translateDir = 'translateY';
|
416
|
+
}
|
417
|
+
}
|
418
|
+
debounce(func, wait) {
|
419
|
+
const throttled = this.throttleTrailing(func, wait);
|
420
|
+
const result = function () {
|
421
|
+
throttled['cancel']();
|
422
|
+
throttled.apply(this, arguments);
|
423
|
+
};
|
424
|
+
result['cancel'] = function () {
|
425
|
+
throttled['cancel']();
|
426
|
+
};
|
427
|
+
return result;
|
428
|
+
}
|
429
|
+
throttleTrailing(func, wait) {
|
430
|
+
let timeout = undefined;
|
431
|
+
let _arguments = arguments;
|
432
|
+
const result = function () {
|
433
|
+
const _this = this;
|
434
|
+
_arguments = arguments;
|
435
|
+
if (timeout) {
|
436
|
+
return;
|
437
|
+
}
|
438
|
+
if (wait <= 0) {
|
439
|
+
func.apply(_this, _arguments);
|
440
|
+
}
|
441
|
+
else {
|
442
|
+
timeout = setTimeout(function () {
|
443
|
+
timeout = undefined;
|
444
|
+
func.apply(_this, _arguments);
|
445
|
+
}, wait);
|
446
|
+
}
|
447
|
+
};
|
448
|
+
result['cancel'] = function () {
|
449
|
+
if (timeout) {
|
450
|
+
clearTimeout(timeout);
|
451
|
+
timeout = undefined;
|
452
|
+
}
|
453
|
+
};
|
454
|
+
return result;
|
455
|
+
}
|
456
|
+
calculatedScrollbarWidth = 0;
|
457
|
+
calculatedScrollbarHeight = 0;
|
458
|
+
padding = 0;
|
459
|
+
previousViewPort = {};
|
460
|
+
currentTween;
|
461
|
+
cachedItemsLength;
|
462
|
+
disposeScrollHandler;
|
463
|
+
disposeResizeHandler;
|
464
|
+
refresh_internal(itemsArrayModified, refreshCompletedCallback = undefined, maxRunTimes = 2) {
|
465
|
+
//note: maxRunTimes is to force it to keep recalculating if the previous iteration caused a re-render (different sliced items in viewport or scrollPosition changed).
|
466
|
+
//The default of 2x max will probably be accurate enough without causing too large a performance bottleneck
|
467
|
+
//The code would typically quit out on the 2nd iteration anyways. The main time it'd think more than 2 runs would be necessary would be for vastly different sized child items or if this is the 1st time the items array was initialized.
|
468
|
+
//Without maxRunTimes, If the user is actively scrolling this code would become an infinite loop until they stopped scrolling. This would be okay, except each scroll event would start an additional infinte loop. We want to short-circuit it to prevent this.
|
469
|
+
if (itemsArrayModified && this.previousViewPort && this.previousViewPort.scrollStartPosition > 0) {
|
470
|
+
//if items were prepended, scroll forward to keep same items visible
|
471
|
+
let oldViewPort = this.previousViewPort;
|
472
|
+
let oldViewPortItems = this.viewPortItems;
|
473
|
+
let oldRefreshCompletedCallback = refreshCompletedCallback;
|
474
|
+
refreshCompletedCallback = () => {
|
475
|
+
let scrollLengthDelta = this.previousViewPort.scrollLength - oldViewPort.scrollLength;
|
476
|
+
if (scrollLengthDelta > 0 && this.viewPortItems) {
|
477
|
+
let oldStartItem = oldViewPortItems[0];
|
478
|
+
let oldStartItemIndex = this.items.findIndex(x => this.compareItems(oldStartItem, x));
|
479
|
+
if (oldStartItemIndex > this.previousViewPort.startIndexWithBuffer) {
|
480
|
+
let itemOrderChanged = false;
|
481
|
+
for (let i = 1; i < this.viewPortItems.length; ++i) {
|
482
|
+
if (!this.compareItems(this.items[oldStartItemIndex + i], oldViewPortItems[i])) {
|
483
|
+
itemOrderChanged = true;
|
484
|
+
break;
|
485
|
+
}
|
486
|
+
}
|
487
|
+
if (!itemOrderChanged) {
|
488
|
+
this.scrollToPosition(this.previousViewPort.scrollStartPosition + scrollLengthDelta, 0, oldRefreshCompletedCallback);
|
489
|
+
return;
|
490
|
+
}
|
491
|
+
}
|
492
|
+
}
|
493
|
+
if (oldRefreshCompletedCallback) {
|
494
|
+
oldRefreshCompletedCallback();
|
495
|
+
}
|
496
|
+
};
|
497
|
+
}
|
498
|
+
this.zone.runOutsideAngular(() => {
|
499
|
+
requestAnimationFrame(() => {
|
500
|
+
if (itemsArrayModified) {
|
501
|
+
this.resetWrapGroupDimensions();
|
502
|
+
}
|
503
|
+
let viewport = this.calculateViewport();
|
504
|
+
let startChanged = itemsArrayModified || viewport.startIndex !== this.previousViewPort.startIndex;
|
505
|
+
let endChanged = itemsArrayModified || viewport.endIndex !== this.previousViewPort.endIndex;
|
506
|
+
let scrollLengthChanged = viewport.scrollLength !== this.previousViewPort.scrollLength;
|
507
|
+
let paddingChanged = viewport.padding !== this.previousViewPort.padding;
|
508
|
+
let scrollPositionChanged = viewport.scrollStartPosition !== this.previousViewPort.scrollStartPosition || viewport.scrollEndPosition !== this.previousViewPort.scrollEndPosition || viewport.maxScrollPosition !== this.previousViewPort.maxScrollPosition;
|
509
|
+
this.previousViewPort = viewport;
|
510
|
+
if (scrollLengthChanged) {
|
511
|
+
this.renderer.setStyle(this.invisiblePaddingElementRef.nativeElement, 'transform', `${this._invisiblePaddingProperty}(${viewport.scrollLength})`);
|
512
|
+
this.renderer.setStyle(this.invisiblePaddingElementRef.nativeElement, 'webkitTransform', `${this._invisiblePaddingProperty}(${viewport.scrollLength})`);
|
513
|
+
}
|
514
|
+
if (paddingChanged) {
|
515
|
+
if (this.useMarginInsteadOfTranslate) {
|
516
|
+
this.renderer.setStyle(this.contentElementRef.nativeElement, this._marginDir, `${viewport.padding}px`);
|
517
|
+
}
|
518
|
+
else {
|
519
|
+
this.renderer.setStyle(this.contentElementRef.nativeElement, 'transform', `${this._translateDir}(${viewport.padding}px)`);
|
520
|
+
this.renderer.setStyle(this.contentElementRef.nativeElement, 'webkitTransform', `${this._translateDir}(${viewport.padding}px)`);
|
521
|
+
}
|
522
|
+
}
|
523
|
+
if (this.headerElementRef) {
|
524
|
+
let scrollPosition = this.getScrollElement()[this._scrollType];
|
525
|
+
let containerOffset = this.getElementsOffset();
|
526
|
+
let offset = Math.max(scrollPosition - viewport.padding - containerOffset + this.headerElementRef.nativeElement.clientHeight, 0);
|
527
|
+
this.renderer.setStyle(this.headerElementRef.nativeElement, 'transform', `${this._translateDir}(${offset}px)`);
|
528
|
+
this.renderer.setStyle(this.headerElementRef.nativeElement, 'webkitTransform', `${this._translateDir}(${offset}px)`);
|
529
|
+
}
|
530
|
+
const changeEventArg = (startChanged || endChanged) ? {
|
531
|
+
startIndex: viewport.startIndex,
|
532
|
+
endIndex: viewport.endIndex,
|
533
|
+
scrollStartPosition: viewport.scrollStartPosition,
|
534
|
+
scrollEndPosition: viewport.scrollEndPosition,
|
535
|
+
startIndexWithBuffer: viewport.startIndexWithBuffer,
|
536
|
+
endIndexWithBuffer: viewport.endIndexWithBuffer,
|
537
|
+
maxScrollPosition: viewport.maxScrollPosition
|
538
|
+
} : undefined;
|
539
|
+
if (startChanged || endChanged || scrollPositionChanged) {
|
540
|
+
const handleChanged = () => {
|
541
|
+
// update the scroll list to trigger re-render of components in viewport
|
542
|
+
this.viewPortItems = viewport.startIndexWithBuffer >= 0 && viewport.endIndexWithBuffer >= 0 ? this.items.slice(viewport.startIndexWithBuffer, viewport.endIndexWithBuffer + 1) : [];
|
543
|
+
this.vsUpdate.emit(this.viewPortItems);
|
544
|
+
if (startChanged) {
|
545
|
+
this.vsStart.emit(changeEventArg);
|
546
|
+
}
|
547
|
+
if (endChanged) {
|
548
|
+
this.vsEnd.emit(changeEventArg);
|
549
|
+
}
|
550
|
+
if (startChanged || endChanged) {
|
551
|
+
this.changeDetectorRef.markForCheck();
|
552
|
+
this.vsChange.emit(changeEventArg);
|
553
|
+
}
|
554
|
+
if (maxRunTimes > 0) {
|
555
|
+
this.refresh_internal(false, refreshCompletedCallback, maxRunTimes - 1);
|
556
|
+
return;
|
557
|
+
}
|
558
|
+
if (refreshCompletedCallback) {
|
559
|
+
refreshCompletedCallback();
|
560
|
+
}
|
561
|
+
};
|
562
|
+
if (this.executeRefreshOutsideAngularZone) {
|
563
|
+
handleChanged();
|
564
|
+
}
|
565
|
+
else {
|
566
|
+
this.zone.run(handleChanged);
|
567
|
+
}
|
568
|
+
}
|
569
|
+
else {
|
570
|
+
if (maxRunTimes > 0 && (scrollLengthChanged || paddingChanged)) {
|
571
|
+
this.refresh_internal(false, refreshCompletedCallback, maxRunTimes - 1);
|
572
|
+
return;
|
573
|
+
}
|
574
|
+
if (refreshCompletedCallback) {
|
575
|
+
refreshCompletedCallback();
|
576
|
+
}
|
577
|
+
}
|
578
|
+
});
|
579
|
+
});
|
580
|
+
}
|
581
|
+
getScrollElement() {
|
582
|
+
return this.parentScroll instanceof Window ? document.scrollingElement || document.documentElement || document.body : this.parentScroll || this.element.nativeElement;
|
583
|
+
}
|
584
|
+
addScrollEventHandlers() {
|
585
|
+
if (this.isAngularUniversalSSR) {
|
586
|
+
return;
|
587
|
+
}
|
588
|
+
let scrollElement = this.getScrollElement();
|
589
|
+
this.removeScrollEventHandlers();
|
590
|
+
this.zone.runOutsideAngular(() => {
|
591
|
+
if (this.parentScroll instanceof Window) {
|
592
|
+
this.disposeScrollHandler = this.renderer.listen('window', 'scroll', this.onScroll);
|
593
|
+
this.disposeResizeHandler = this.renderer.listen('window', 'resize', this.onScroll);
|
594
|
+
}
|
595
|
+
else {
|
596
|
+
this.disposeScrollHandler = this.renderer.listen(scrollElement, 'scroll', this.onScroll);
|
597
|
+
if (this._checkResizeInterval > 0) {
|
598
|
+
this.checkScrollElementResizedTimer = setInterval(() => { this.checkScrollElementResized(); }, this._checkResizeInterval);
|
599
|
+
}
|
600
|
+
}
|
601
|
+
});
|
602
|
+
}
|
603
|
+
removeScrollEventHandlers() {
|
604
|
+
if (this.checkScrollElementResizedTimer) {
|
605
|
+
clearInterval(this.checkScrollElementResizedTimer);
|
606
|
+
}
|
607
|
+
if (this.disposeScrollHandler) {
|
608
|
+
this.disposeScrollHandler();
|
609
|
+
this.disposeScrollHandler = undefined;
|
610
|
+
}
|
611
|
+
if (this.disposeResizeHandler) {
|
612
|
+
this.disposeResizeHandler();
|
613
|
+
this.disposeResizeHandler = undefined;
|
614
|
+
}
|
615
|
+
}
|
616
|
+
getElementsOffset() {
|
617
|
+
if (this.isAngularUniversalSSR) {
|
618
|
+
return 0;
|
619
|
+
}
|
620
|
+
let offset = 0;
|
621
|
+
if (this.containerElementRef && this.containerElementRef.nativeElement) {
|
622
|
+
offset += this.containerElementRef.nativeElement[this._offsetType];
|
623
|
+
}
|
624
|
+
if (this.parentScroll) {
|
625
|
+
let scrollElement = this.getScrollElement();
|
626
|
+
let elementClientRect = this.getElementSize(this.element.nativeElement);
|
627
|
+
let scrollClientRect = this.getElementSize(scrollElement);
|
628
|
+
if (this.horizontal) {
|
629
|
+
offset += elementClientRect.left - scrollClientRect.left;
|
630
|
+
}
|
631
|
+
else {
|
632
|
+
offset += elementClientRect.top - scrollClientRect.top;
|
633
|
+
}
|
634
|
+
if (!(this.parentScroll instanceof Window)) {
|
635
|
+
offset += scrollElement[this._scrollType];
|
636
|
+
}
|
637
|
+
}
|
638
|
+
return offset;
|
639
|
+
}
|
640
|
+
countItemsPerWrapGroup() {
|
641
|
+
if (this.isAngularUniversalSSR) {
|
642
|
+
return Math.round(this.horizontal ? this.ssrViewportHeight / this.ssrChildHeight : this.ssrViewportWidth / this.ssrChildWidth);
|
643
|
+
}
|
644
|
+
let propertyName = this.horizontal ? 'offsetLeft' : 'offsetTop';
|
645
|
+
let children = ((this.containerElementRef && this.containerElementRef.nativeElement) || this.contentElementRef.nativeElement).children;
|
646
|
+
let childrenLength = children ? children.length : 0;
|
647
|
+
if (childrenLength === 0) {
|
648
|
+
return 1;
|
649
|
+
}
|
650
|
+
let firstOffset = children[0][propertyName];
|
651
|
+
let result = 1;
|
652
|
+
while (result < childrenLength && firstOffset === children[result][propertyName]) {
|
653
|
+
++result;
|
654
|
+
}
|
655
|
+
return result;
|
656
|
+
}
|
657
|
+
getScrollStartPosition() {
|
658
|
+
let windowScrollValue = undefined;
|
659
|
+
if (this.parentScroll instanceof Window) {
|
660
|
+
windowScrollValue = window[this._pageOffsetType];
|
661
|
+
}
|
662
|
+
return windowScrollValue || this.getScrollElement()[this._scrollType] || 0;
|
663
|
+
}
|
664
|
+
minMeasuredChildWidth;
|
665
|
+
minMeasuredChildHeight;
|
666
|
+
wrapGroupDimensions;
|
667
|
+
resetWrapGroupDimensions() {
|
668
|
+
const oldWrapGroupDimensions = this.wrapGroupDimensions;
|
669
|
+
this.invalidateAllCachedMeasurements();
|
670
|
+
if (!this.enableUnequalChildrenSizes || !oldWrapGroupDimensions || oldWrapGroupDimensions.numberOfKnownWrapGroupChildSizes === 0) {
|
671
|
+
return;
|
672
|
+
}
|
673
|
+
const itemsPerWrapGroup = this.countItemsPerWrapGroup();
|
674
|
+
for (let wrapGroupIndex = 0; wrapGroupIndex < oldWrapGroupDimensions.maxChildSizePerWrapGroup.length; ++wrapGroupIndex) {
|
675
|
+
const oldWrapGroupDimension = oldWrapGroupDimensions.maxChildSizePerWrapGroup[wrapGroupIndex];
|
676
|
+
if (!oldWrapGroupDimension || !oldWrapGroupDimension.items || !oldWrapGroupDimension.items.length) {
|
677
|
+
continue;
|
678
|
+
}
|
679
|
+
if (oldWrapGroupDimension.items.length !== itemsPerWrapGroup) {
|
680
|
+
return;
|
681
|
+
}
|
682
|
+
let itemsChanged = false;
|
683
|
+
let arrayStartIndex = itemsPerWrapGroup * wrapGroupIndex;
|
684
|
+
for (let i = 0; i < itemsPerWrapGroup; ++i) {
|
685
|
+
if (!this.compareItems(oldWrapGroupDimension.items[i], this.items[arrayStartIndex + i])) {
|
686
|
+
itemsChanged = true;
|
687
|
+
break;
|
688
|
+
}
|
689
|
+
}
|
690
|
+
if (!itemsChanged) {
|
691
|
+
++this.wrapGroupDimensions.numberOfKnownWrapGroupChildSizes;
|
692
|
+
this.wrapGroupDimensions.sumOfKnownWrapGroupChildWidths += oldWrapGroupDimension.childWidth || 0;
|
693
|
+
this.wrapGroupDimensions.sumOfKnownWrapGroupChildHeights += oldWrapGroupDimension.childHeight || 0;
|
694
|
+
this.wrapGroupDimensions.maxChildSizePerWrapGroup[wrapGroupIndex] = oldWrapGroupDimension;
|
695
|
+
}
|
696
|
+
}
|
697
|
+
}
|
698
|
+
calculateDimensions() {
|
699
|
+
let scrollElement = this.getScrollElement();
|
700
|
+
const maxCalculatedScrollBarSize = 25; // Note: Formula to auto-calculate doesn't work for ParentScroll, so we default to this if not set by consuming application
|
701
|
+
this.calculatedScrollbarHeight = Math.max(Math.min(scrollElement.offsetHeight - scrollElement.clientHeight, maxCalculatedScrollBarSize), this.calculatedScrollbarHeight);
|
702
|
+
this.calculatedScrollbarWidth = Math.max(Math.min(scrollElement.offsetWidth - scrollElement.clientWidth, maxCalculatedScrollBarSize), this.calculatedScrollbarWidth);
|
703
|
+
let viewportWidth = scrollElement.offsetWidth - (this.scrollbarWidth || this.calculatedScrollbarWidth || (this.horizontal ? 0 : maxCalculatedScrollBarSize));
|
704
|
+
let viewportHeight = scrollElement.offsetHeight - (this.scrollbarHeight || this.calculatedScrollbarHeight || (this.horizontal ? maxCalculatedScrollBarSize : 0));
|
705
|
+
let content = (this.containerElementRef && this.containerElementRef.nativeElement) || this.contentElementRef.nativeElement;
|
706
|
+
let itemsPerWrapGroup = this.countItemsPerWrapGroup();
|
707
|
+
let wrapGroupsPerPage;
|
708
|
+
let defaultChildWidth;
|
709
|
+
let defaultChildHeight;
|
710
|
+
if (this.isAngularUniversalSSR) {
|
711
|
+
viewportWidth = this.ssrViewportWidth;
|
712
|
+
viewportHeight = this.ssrViewportHeight;
|
713
|
+
defaultChildWidth = this.ssrChildWidth;
|
714
|
+
defaultChildHeight = this.ssrChildHeight;
|
715
|
+
let itemsPerRow = Math.max(Math.ceil(viewportWidth / defaultChildWidth), 1);
|
716
|
+
let itemsPerCol = Math.max(Math.ceil(viewportHeight / defaultChildHeight), 1);
|
717
|
+
wrapGroupsPerPage = this.horizontal ? itemsPerRow : itemsPerCol;
|
718
|
+
}
|
719
|
+
else if (!this.enableUnequalChildrenSizes) {
|
720
|
+
if (content.children.length > 0) {
|
721
|
+
if (!this.childWidth || !this.childHeight) {
|
722
|
+
if (!this.minMeasuredChildWidth && viewportWidth > 0) {
|
723
|
+
this.minMeasuredChildWidth = viewportWidth;
|
724
|
+
}
|
725
|
+
if (!this.minMeasuredChildHeight && viewportHeight > 0) {
|
726
|
+
this.minMeasuredChildHeight = viewportHeight;
|
727
|
+
}
|
728
|
+
}
|
729
|
+
let child = content.children[0];
|
730
|
+
let clientRect = this.getElementSize(child);
|
731
|
+
this.minMeasuredChildWidth = Math.min(this.minMeasuredChildWidth, clientRect.width);
|
732
|
+
this.minMeasuredChildHeight = Math.min(this.minMeasuredChildHeight, clientRect.height);
|
733
|
+
}
|
734
|
+
defaultChildWidth = this.childWidth || this.minMeasuredChildWidth || viewportWidth;
|
735
|
+
defaultChildHeight = this.childHeight || this.minMeasuredChildHeight || viewportHeight;
|
736
|
+
let itemsPerRow = Math.max(Math.ceil(viewportWidth / defaultChildWidth), 1);
|
737
|
+
let itemsPerCol = Math.max(Math.ceil(viewportHeight / defaultChildHeight), 1);
|
738
|
+
wrapGroupsPerPage = this.horizontal ? itemsPerRow : itemsPerCol;
|
739
|
+
}
|
740
|
+
else {
|
741
|
+
let scrollOffset = scrollElement[this._scrollType] - (this.previousViewPort ? this.previousViewPort.padding : 0);
|
742
|
+
let arrayStartIndex = this.previousViewPort.startIndexWithBuffer || 0;
|
743
|
+
let wrapGroupIndex = Math.ceil(arrayStartIndex / itemsPerWrapGroup);
|
744
|
+
let maxWidthForWrapGroup = 0;
|
745
|
+
let maxHeightForWrapGroup = 0;
|
746
|
+
let sumOfVisibleMaxWidths = 0;
|
747
|
+
let sumOfVisibleMaxHeights = 0;
|
748
|
+
wrapGroupsPerPage = 0;
|
749
|
+
for (let i = 0; i < content.children.length; ++i) {
|
750
|
+
++arrayStartIndex;
|
751
|
+
let child = content.children[i];
|
752
|
+
let clientRect = this.getElementSize(child);
|
753
|
+
maxWidthForWrapGroup = Math.max(maxWidthForWrapGroup, clientRect.width);
|
754
|
+
maxHeightForWrapGroup = Math.max(maxHeightForWrapGroup, clientRect.height);
|
755
|
+
if (arrayStartIndex % itemsPerWrapGroup === 0) {
|
756
|
+
let oldValue = this.wrapGroupDimensions.maxChildSizePerWrapGroup[wrapGroupIndex];
|
757
|
+
if (oldValue) {
|
758
|
+
--this.wrapGroupDimensions.numberOfKnownWrapGroupChildSizes;
|
759
|
+
this.wrapGroupDimensions.sumOfKnownWrapGroupChildWidths -= oldValue.childWidth || 0;
|
760
|
+
this.wrapGroupDimensions.sumOfKnownWrapGroupChildHeights -= oldValue.childHeight || 0;
|
761
|
+
}
|
762
|
+
++this.wrapGroupDimensions.numberOfKnownWrapGroupChildSizes;
|
763
|
+
const items = this.items.slice(arrayStartIndex - itemsPerWrapGroup, arrayStartIndex);
|
764
|
+
this.wrapGroupDimensions.maxChildSizePerWrapGroup[wrapGroupIndex] = {
|
765
|
+
childWidth: maxWidthForWrapGroup,
|
766
|
+
childHeight: maxHeightForWrapGroup,
|
767
|
+
items: items
|
768
|
+
};
|
769
|
+
this.wrapGroupDimensions.sumOfKnownWrapGroupChildWidths += maxWidthForWrapGroup;
|
770
|
+
this.wrapGroupDimensions.sumOfKnownWrapGroupChildHeights += maxHeightForWrapGroup;
|
771
|
+
if (this.horizontal) {
|
772
|
+
let maxVisibleWidthForWrapGroup = Math.min(maxWidthForWrapGroup, Math.max(viewportWidth - sumOfVisibleMaxWidths, 0));
|
773
|
+
if (scrollOffset > 0) {
|
774
|
+
let scrollOffsetToRemove = Math.min(scrollOffset, maxVisibleWidthForWrapGroup);
|
775
|
+
maxVisibleWidthForWrapGroup -= scrollOffsetToRemove;
|
776
|
+
scrollOffset -= scrollOffsetToRemove;
|
777
|
+
}
|
778
|
+
sumOfVisibleMaxWidths += maxVisibleWidthForWrapGroup;
|
779
|
+
if (maxVisibleWidthForWrapGroup > 0 && viewportWidth >= sumOfVisibleMaxWidths) {
|
780
|
+
++wrapGroupsPerPage;
|
781
|
+
}
|
782
|
+
}
|
783
|
+
else {
|
784
|
+
let maxVisibleHeightForWrapGroup = Math.min(maxHeightForWrapGroup, Math.max(viewportHeight - sumOfVisibleMaxHeights, 0));
|
785
|
+
if (scrollOffset > 0) {
|
786
|
+
let scrollOffsetToRemove = Math.min(scrollOffset, maxVisibleHeightForWrapGroup);
|
787
|
+
maxVisibleHeightForWrapGroup -= scrollOffsetToRemove;
|
788
|
+
scrollOffset -= scrollOffsetToRemove;
|
789
|
+
}
|
790
|
+
sumOfVisibleMaxHeights += maxVisibleHeightForWrapGroup;
|
791
|
+
if (maxVisibleHeightForWrapGroup > 0 && viewportHeight >= sumOfVisibleMaxHeights) {
|
792
|
+
++wrapGroupsPerPage;
|
793
|
+
}
|
794
|
+
}
|
795
|
+
++wrapGroupIndex;
|
796
|
+
maxWidthForWrapGroup = 0;
|
797
|
+
maxHeightForWrapGroup = 0;
|
798
|
+
}
|
799
|
+
}
|
800
|
+
let averageChildWidth = this.wrapGroupDimensions.sumOfKnownWrapGroupChildWidths / this.wrapGroupDimensions.numberOfKnownWrapGroupChildSizes;
|
801
|
+
let averageChildHeight = this.wrapGroupDimensions.sumOfKnownWrapGroupChildHeights / this.wrapGroupDimensions.numberOfKnownWrapGroupChildSizes;
|
802
|
+
defaultChildWidth = this.childWidth || averageChildWidth || viewportWidth;
|
803
|
+
defaultChildHeight = this.childHeight || averageChildHeight || viewportHeight;
|
804
|
+
if (this.horizontal) {
|
805
|
+
if (viewportWidth > sumOfVisibleMaxWidths) {
|
806
|
+
wrapGroupsPerPage += Math.ceil((viewportWidth - sumOfVisibleMaxWidths) / defaultChildWidth);
|
807
|
+
}
|
808
|
+
}
|
809
|
+
else {
|
810
|
+
if (viewportHeight > sumOfVisibleMaxHeights) {
|
811
|
+
wrapGroupsPerPage += Math.ceil((viewportHeight - sumOfVisibleMaxHeights) / defaultChildHeight);
|
812
|
+
}
|
813
|
+
}
|
814
|
+
}
|
815
|
+
let itemCount = this.items.length;
|
816
|
+
let itemsPerPage = itemsPerWrapGroup * wrapGroupsPerPage;
|
817
|
+
let pageCount_fractional = itemCount / itemsPerPage;
|
818
|
+
let numberOfWrapGroups = Math.ceil(itemCount / itemsPerWrapGroup);
|
819
|
+
let scrollLength = 0;
|
820
|
+
let defaultScrollLengthPerWrapGroup = this.horizontal ? defaultChildWidth : defaultChildHeight;
|
821
|
+
if (this.enableUnequalChildrenSizes) {
|
822
|
+
let numUnknownChildSizes = 0;
|
823
|
+
for (let i = 0; i < numberOfWrapGroups; ++i) {
|
824
|
+
let childSize = this.wrapGroupDimensions.maxChildSizePerWrapGroup[i] && this.wrapGroupDimensions.maxChildSizePerWrapGroup[i][this._childScrollDim];
|
825
|
+
if (childSize) {
|
826
|
+
scrollLength += childSize;
|
827
|
+
}
|
828
|
+
else {
|
829
|
+
++numUnknownChildSizes;
|
830
|
+
}
|
831
|
+
}
|
832
|
+
scrollLength += Math.round(numUnknownChildSizes * defaultScrollLengthPerWrapGroup);
|
833
|
+
}
|
834
|
+
else {
|
835
|
+
scrollLength = numberOfWrapGroups * defaultScrollLengthPerWrapGroup;
|
836
|
+
}
|
837
|
+
if (this.headerElementRef) {
|
838
|
+
scrollLength += this.headerElementRef.nativeElement.clientHeight;
|
839
|
+
}
|
840
|
+
let viewportLength = this.horizontal ? viewportWidth : viewportHeight;
|
841
|
+
let maxScrollPosition = Math.max(scrollLength - viewportLength, 0);
|
842
|
+
return {
|
843
|
+
childHeight: defaultChildHeight,
|
844
|
+
childWidth: defaultChildWidth,
|
845
|
+
itemCount: itemCount,
|
846
|
+
itemsPerPage: itemsPerPage,
|
847
|
+
itemsPerWrapGroup: itemsPerWrapGroup,
|
848
|
+
maxScrollPosition: maxScrollPosition,
|
849
|
+
pageCount_fractional: pageCount_fractional,
|
850
|
+
scrollLength: scrollLength,
|
851
|
+
viewportLength: viewportLength,
|
852
|
+
wrapGroupsPerPage: wrapGroupsPerPage,
|
853
|
+
};
|
854
|
+
}
|
855
|
+
cachedPageSize = 0;
|
856
|
+
previousScrollNumberElements = 0;
|
857
|
+
calculatePadding(arrayStartIndexWithBuffer, dimensions) {
|
858
|
+
if (dimensions.itemCount === 0) {
|
859
|
+
return 0;
|
860
|
+
}
|
861
|
+
let defaultScrollLengthPerWrapGroup = dimensions[this._childScrollDim];
|
862
|
+
let startingWrapGroupIndex = Math.floor(arrayStartIndexWithBuffer / dimensions.itemsPerWrapGroup) || 0;
|
863
|
+
if (!this.enableUnequalChildrenSizes) {
|
864
|
+
return defaultScrollLengthPerWrapGroup * startingWrapGroupIndex;
|
865
|
+
}
|
866
|
+
let numUnknownChildSizes = 0;
|
867
|
+
let result = 0;
|
868
|
+
for (let i = 0; i < startingWrapGroupIndex; ++i) {
|
869
|
+
let childSize = this.wrapGroupDimensions.maxChildSizePerWrapGroup[i] && this.wrapGroupDimensions.maxChildSizePerWrapGroup[i][this._childScrollDim];
|
870
|
+
if (childSize) {
|
871
|
+
result += childSize;
|
872
|
+
}
|
873
|
+
else {
|
874
|
+
++numUnknownChildSizes;
|
875
|
+
}
|
876
|
+
}
|
877
|
+
result += Math.round(numUnknownChildSizes * defaultScrollLengthPerWrapGroup);
|
878
|
+
return result;
|
879
|
+
}
|
880
|
+
calculatePageInfo(scrollPosition, dimensions) {
|
881
|
+
let scrollPercentage = 0;
|
882
|
+
if (this.enableUnequalChildrenSizes) {
|
883
|
+
const numberOfWrapGroups = Math.ceil(dimensions.itemCount / dimensions.itemsPerWrapGroup);
|
884
|
+
let totalScrolledLength = 0;
|
885
|
+
let defaultScrollLengthPerWrapGroup = dimensions[this._childScrollDim];
|
886
|
+
for (let i = 0; i < numberOfWrapGroups; ++i) {
|
887
|
+
let childSize = this.wrapGroupDimensions.maxChildSizePerWrapGroup[i] && this.wrapGroupDimensions.maxChildSizePerWrapGroup[i][this._childScrollDim];
|
888
|
+
if (childSize) {
|
889
|
+
totalScrolledLength += childSize;
|
890
|
+
}
|
891
|
+
else {
|
892
|
+
totalScrolledLength += defaultScrollLengthPerWrapGroup;
|
893
|
+
}
|
894
|
+
if (scrollPosition < totalScrolledLength) {
|
895
|
+
scrollPercentage = i / numberOfWrapGroups;
|
896
|
+
break;
|
897
|
+
}
|
898
|
+
}
|
899
|
+
}
|
900
|
+
else {
|
901
|
+
scrollPercentage = scrollPosition / dimensions.scrollLength;
|
902
|
+
}
|
903
|
+
let startingArrayIndex_fractional = Math.min(Math.max(scrollPercentage * dimensions.pageCount_fractional, 0), dimensions.pageCount_fractional) * dimensions.itemsPerPage;
|
904
|
+
let maxStart = dimensions.itemCount - dimensions.itemsPerPage - 1;
|
905
|
+
let arrayStartIndex = Math.min(Math.floor(startingArrayIndex_fractional), maxStart);
|
906
|
+
arrayStartIndex -= arrayStartIndex % dimensions.itemsPerWrapGroup; // round down to start of wrapGroup
|
907
|
+
if (this.stripedTable) {
|
908
|
+
let bufferBoundary = 2 * dimensions.itemsPerWrapGroup;
|
909
|
+
if (arrayStartIndex % bufferBoundary !== 0) {
|
910
|
+
arrayStartIndex = Math.max(arrayStartIndex - arrayStartIndex % bufferBoundary, 0);
|
911
|
+
}
|
912
|
+
}
|
913
|
+
let arrayEndIndex = Math.ceil(startingArrayIndex_fractional) + dimensions.itemsPerPage - 1;
|
914
|
+
let endIndexWithinWrapGroup = (arrayEndIndex + 1) % dimensions.itemsPerWrapGroup;
|
915
|
+
if (endIndexWithinWrapGroup > 0) {
|
916
|
+
arrayEndIndex += dimensions.itemsPerWrapGroup - endIndexWithinWrapGroup; // round up to end of wrapGroup
|
917
|
+
}
|
918
|
+
if (isNaN(arrayStartIndex)) {
|
919
|
+
arrayStartIndex = 0;
|
920
|
+
}
|
921
|
+
if (isNaN(arrayEndIndex)) {
|
922
|
+
arrayEndIndex = 0;
|
923
|
+
}
|
924
|
+
arrayStartIndex = Math.min(Math.max(arrayStartIndex, 0), dimensions.itemCount - 1);
|
925
|
+
arrayEndIndex = Math.min(Math.max(arrayEndIndex, 0), dimensions.itemCount - 1);
|
926
|
+
let bufferSize = this.bufferAmount * dimensions.itemsPerWrapGroup;
|
927
|
+
let startIndexWithBuffer = Math.min(Math.max(arrayStartIndex - bufferSize, 0), dimensions.itemCount - 1);
|
928
|
+
let endIndexWithBuffer = Math.min(Math.max(arrayEndIndex + bufferSize, 0), dimensions.itemCount - 1);
|
929
|
+
return {
|
930
|
+
startIndex: arrayStartIndex,
|
931
|
+
endIndex: arrayEndIndex,
|
932
|
+
startIndexWithBuffer: startIndexWithBuffer,
|
933
|
+
endIndexWithBuffer: endIndexWithBuffer,
|
934
|
+
scrollStartPosition: scrollPosition,
|
935
|
+
scrollEndPosition: scrollPosition + dimensions.viewportLength,
|
936
|
+
maxScrollPosition: dimensions.maxScrollPosition
|
937
|
+
};
|
938
|
+
}
|
939
|
+
calculateViewport() {
|
940
|
+
let dimensions = this.calculateDimensions();
|
941
|
+
let offset = this.getElementsOffset();
|
942
|
+
let scrollStartPosition = this.getScrollStartPosition();
|
943
|
+
if (scrollStartPosition > (dimensions.scrollLength + offset) && !(this.parentScroll instanceof Window)) {
|
944
|
+
scrollStartPosition = dimensions.scrollLength;
|
945
|
+
}
|
946
|
+
else {
|
947
|
+
scrollStartPosition -= offset;
|
948
|
+
}
|
949
|
+
scrollStartPosition = Math.max(0, scrollStartPosition);
|
950
|
+
let pageInfo = this.calculatePageInfo(scrollStartPosition, dimensions);
|
951
|
+
let newPadding = this.calculatePadding(pageInfo.startIndexWithBuffer, dimensions);
|
952
|
+
let newScrollLength = dimensions.scrollLength;
|
953
|
+
return {
|
954
|
+
startIndex: pageInfo.startIndex,
|
955
|
+
endIndex: pageInfo.endIndex,
|
956
|
+
startIndexWithBuffer: pageInfo.startIndexWithBuffer,
|
957
|
+
endIndexWithBuffer: pageInfo.endIndexWithBuffer,
|
958
|
+
padding: Math.round(newPadding),
|
959
|
+
scrollLength: Math.round(newScrollLength),
|
960
|
+
scrollStartPosition: pageInfo.scrollStartPosition,
|
961
|
+
scrollEndPosition: pageInfo.scrollEndPosition,
|
962
|
+
maxScrollPosition: pageInfo.maxScrollPosition
|
963
|
+
};
|
964
|
+
}
|
965
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: VirtualScrollerComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: PLATFORM_ID }, { token: 'virtual-scroller-default-options', optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
966
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.4", type: VirtualScrollerComponent, isStandalone: true, selector: "virtual-scroller,[virtualScroller]", inputs: { executeRefreshOutsideAngularZone: "executeRefreshOutsideAngularZone", enableUnequalChildrenSizes: "enableUnequalChildrenSizes", RTL: "RTL", useMarginInsteadOfTranslate: "useMarginInsteadOfTranslate", modifyOverflowStyleOfParentScroll: "modifyOverflowStyleOfParentScroll", stripedTable: "stripedTable", scrollbarWidth: "scrollbarWidth", scrollbarHeight: "scrollbarHeight", childWidth: "childWidth", childHeight: "childHeight", ssrChildWidth: "ssrChildWidth", ssrChildHeight: "ssrChildHeight", ssrViewportWidth: "ssrViewportWidth", ssrViewportHeight: "ssrViewportHeight", bufferAmount: "bufferAmount", scrollAnimationTime: "scrollAnimationTime", resizeBypassRefreshThreshold: "resizeBypassRefreshThreshold", scrollThrottlingTime: "scrollThrottlingTime", scrollDebounceTime: "scrollDebounceTime", checkResizeInterval: "checkResizeInterval", items: "items", compareItems: "compareItems", horizontal: "horizontal", parentScroll: "parentScroll" }, outputs: { vsUpdate: "vsUpdate", vsChange: "vsChange", vsStart: "vsStart", vsEnd: "vsEnd" }, host: { properties: { "class.horizontal": "horizontal", "class.vertical": "!horizontal", "class.selfScroll": "!parentScroll", "class.rtl": "RTL" } }, queries: [{ propertyName: "headerElementRef", first: true, predicate: ["header"], descendants: true, read: ElementRef }, { propertyName: "containerElementRef", first: true, predicate: ["container"], descendants: true, read: ElementRef }], viewQueries: [{ propertyName: "contentElementRef", first: true, predicate: ["content"], descendants: true, read: ElementRef, static: true }, { propertyName: "invisiblePaddingElementRef", first: true, predicate: ["invisiblePadding"], descendants: true, read: ElementRef, static: true }], exportAs: ["virtualScroller"], usesOnChanges: true, ngImport: i0, template: `
|
967
|
+
<div class="total-padding" #invisiblePadding></div>
|
968
|
+
<div class="scrollable-content" #content>
|
969
|
+
<ng-content></ng-content>
|
970
|
+
</div>
|
971
|
+
`, isInline: true, styles: [":host{position:relative;display:block;-webkit-overflow-scrolling:touch}:host.horizontal.selfScroll{overflow-y:visible;overflow-x:auto}:host.horizontal.selfScroll.rtl{transform:scaleX(-1)}:host.vertical.selfScroll{overflow-y:auto;overflow-x:visible}.scrollable-content{top:0;left:0;width:100%;height:100%;max-width:100vw;max-height:100vh;position:absolute}.scrollable-content ::ng-deep>*{box-sizing:border-box}:host.horizontal{white-space:nowrap}:host.horizontal .scrollable-content{display:flex}:host.horizontal .scrollable-content ::ng-deep>*{flex-shrink:0;flex-grow:0;white-space:initial}:host.horizontal.rtl .scrollable-content ::ng-deep>*{transform:scaleX(-1)}.total-padding{position:absolute;top:0;left:0;height:1px;width:1px;transform-origin:0 0;opacity:0}:host.horizontal .total-padding{height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
972
|
+
}
|
973
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.4", ngImport: i0, type: VirtualScrollerComponent, decorators: [{
|
974
|
+
type: Component,
|
975
|
+
args: [{ selector: 'virtual-scroller,[virtualScroller]', standalone: true, imports: [CommonModule], exportAs: 'virtualScroller', template: `
|
976
|
+
<div class="total-padding" #invisiblePadding></div>
|
977
|
+
<div class="scrollable-content" #content>
|
978
|
+
<ng-content></ng-content>
|
979
|
+
</div>
|
980
|
+
`, host: {
|
981
|
+
'[class.horizontal]': 'horizontal',
|
982
|
+
'[class.vertical]': '!horizontal',
|
983
|
+
'[class.selfScroll]': '!parentScroll',
|
984
|
+
'[class.rtl]': 'RTL'
|
985
|
+
}, styles: [":host{position:relative;display:block;-webkit-overflow-scrolling:touch}:host.horizontal.selfScroll{overflow-y:visible;overflow-x:auto}:host.horizontal.selfScroll.rtl{transform:scaleX(-1)}:host.vertical.selfScroll{overflow-y:auto;overflow-x:visible}.scrollable-content{top:0;left:0;width:100%;height:100%;max-width:100vw;max-height:100vh;position:absolute}.scrollable-content ::ng-deep>*{box-sizing:border-box}:host.horizontal{white-space:nowrap}:host.horizontal .scrollable-content{display:flex}:host.horizontal .scrollable-content ::ng-deep>*{flex-shrink:0;flex-grow:0;white-space:initial}:host.horizontal.rtl .scrollable-content ::ng-deep>*{transform:scaleX(-1)}.total-padding{position:absolute;top:0;left:0;height:1px;width:1px;transform-origin:0 0;opacity:0}:host.horizontal .total-padding{height:100%}\n"] }]
|
986
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: Object, decorators: [{
|
987
|
+
type: Inject,
|
988
|
+
args: [PLATFORM_ID]
|
989
|
+
}] }, { type: undefined, decorators: [{
|
990
|
+
type: Optional
|
991
|
+
}, {
|
992
|
+
type: Inject,
|
993
|
+
args: ['virtual-scroller-default-options']
|
994
|
+
}] }], propDecorators: { executeRefreshOutsideAngularZone: [{
|
995
|
+
type: Input
|
996
|
+
}], enableUnequalChildrenSizes: [{
|
997
|
+
type: Input
|
998
|
+
}], RTL: [{
|
999
|
+
type: Input
|
1000
|
+
}], useMarginInsteadOfTranslate: [{
|
1001
|
+
type: Input
|
1002
|
+
}], modifyOverflowStyleOfParentScroll: [{
|
1003
|
+
type: Input
|
1004
|
+
}], stripedTable: [{
|
1005
|
+
type: Input
|
1006
|
+
}], scrollbarWidth: [{
|
1007
|
+
type: Input
|
1008
|
+
}], scrollbarHeight: [{
|
1009
|
+
type: Input
|
1010
|
+
}], childWidth: [{
|
1011
|
+
type: Input
|
1012
|
+
}], childHeight: [{
|
1013
|
+
type: Input
|
1014
|
+
}], ssrChildWidth: [{
|
1015
|
+
type: Input
|
1016
|
+
}], ssrChildHeight: [{
|
1017
|
+
type: Input
|
1018
|
+
}], ssrViewportWidth: [{
|
1019
|
+
type: Input
|
1020
|
+
}], ssrViewportHeight: [{
|
1021
|
+
type: Input
|
1022
|
+
}], bufferAmount: [{
|
1023
|
+
type: Input
|
1024
|
+
}], scrollAnimationTime: [{
|
1025
|
+
type: Input
|
1026
|
+
}], resizeBypassRefreshThreshold: [{
|
1027
|
+
type: Input
|
1028
|
+
}], scrollThrottlingTime: [{
|
1029
|
+
type: Input
|
1030
|
+
}], scrollDebounceTime: [{
|
1031
|
+
type: Input
|
1032
|
+
}], checkResizeInterval: [{
|
1033
|
+
type: Input
|
1034
|
+
}], items: [{
|
1035
|
+
type: Input
|
1036
|
+
}], compareItems: [{
|
1037
|
+
type: Input
|
1038
|
+
}], horizontal: [{
|
1039
|
+
type: Input
|
1040
|
+
}], parentScroll: [{
|
1041
|
+
type: Input
|
1042
|
+
}], vsUpdate: [{
|
1043
|
+
type: Output
|
1044
|
+
}], vsChange: [{
|
1045
|
+
type: Output
|
1046
|
+
}], vsStart: [{
|
1047
|
+
type: Output
|
1048
|
+
}], vsEnd: [{
|
1049
|
+
type: Output
|
1050
|
+
}], contentElementRef: [{
|
1051
|
+
type: ViewChild,
|
1052
|
+
args: ['content', { read: ElementRef, static: true }]
|
1053
|
+
}], invisiblePaddingElementRef: [{
|
1054
|
+
type: ViewChild,
|
1055
|
+
args: ['invisiblePadding', { read: ElementRef, static: true }]
|
1056
|
+
}], headerElementRef: [{
|
1057
|
+
type: ContentChild,
|
1058
|
+
args: ['header', { read: ElementRef, static: false }]
|
1059
|
+
}], containerElementRef: [{
|
1060
|
+
type: ContentChild,
|
1061
|
+
args: ['container', { read: ElementRef, static: false }]
|
1062
|
+
}] } });
|
1063
|
+
class VirtualScrollerModule {
|
1064
|
+
}
|
1065
|
+
|
1066
|
+
/**
|
1067
|
+
* Generated bundle index. Do not edit.
|
1068
|
+
*/
|
1069
|
+
|
1070
|
+
export { VIRTUAL_SCROLLER_DEFAULT_OPTIONS_FACTORY, VirtualScrollerComponent, VirtualScrollerModule };
|
1071
|
+
//# sourceMappingURL=ahmedfharag-ngx-virtual-scroller.mjs.map
|