ng-virtual-list 0.5.1 → 0.6.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 +4 -3
- package/fesm2022/ng-virtual-list.mjs +549 -88
- package/fesm2022/ng-virtual-list.mjs.map +1 -1
- package/lib/components/ng-virtual-list-item.component.d.ts +2 -0
- package/lib/const/index.d.ts +2 -0
- package/lib/models/render-item-config.model.d.ts +1 -0
- package/lib/ng-virtual-list.component.d.ts +21 -11
- package/lib/utils/cacheMap.d.ts +31 -0
- package/lib/utils/eventEmitter.d.ts +37 -0
- package/lib/utils/index.d.ts +3 -1
- package/lib/utils/trackBox.d.ts +78 -0
- package/lib/utils/tracker.d.ts +38 -0
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import { signal, inject, ElementRef, ChangeDetectionStrategy, Component, viewChi
|
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
5
|
import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
6
|
-
import {
|
|
6
|
+
import { BehaviorSubject, filter, map, tap, combineLatest, distinctUntilChanged, switchMap, of } from 'rxjs';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Virtual list item component
|
|
@@ -23,18 +23,8 @@ class NgVirtualListItemComponent {
|
|
|
23
23
|
if (this._data === v) {
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
|
-
this._data = v;
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
itemRenderer = signal(undefined);
|
|
30
|
-
set renderer(v) {
|
|
31
|
-
this.itemRenderer.set(v);
|
|
32
|
-
}
|
|
33
|
-
_elementRef = inject((ElementRef));
|
|
34
|
-
constructor() {
|
|
35
|
-
this._id = NgVirtualListItemComponent.__nextId = NgVirtualListItemComponent.__nextId === Number.MAX_SAFE_INTEGER
|
|
36
|
-
? 0 : NgVirtualListItemComponent.__nextId + 1;
|
|
37
|
-
toObservable(this.data).pipe(takeUntilDestroyed(), filter(data => !!data), tap(data => {
|
|
26
|
+
const data = this._data = v;
|
|
27
|
+
if (data) {
|
|
38
28
|
const styles = this._elementRef.nativeElement.style;
|
|
39
29
|
styles.zIndex = data.config.sticky;
|
|
40
30
|
if (data.config.snapped) {
|
|
@@ -45,9 +35,23 @@ class NgVirtualListItemComponent {
|
|
|
45
35
|
styles.position = 'absolute';
|
|
46
36
|
styles.transform = `translate3d(${data.config.isVertical ? 0 : data.measures.x}px, ${data.config.isVertical ? data.measures.y : 0}px , 0)`;
|
|
47
37
|
}
|
|
48
|
-
styles.height = data.config.isVertical ? `${data.measures.height}px` : '100%';
|
|
49
|
-
styles.width = data.config.isVertical ? '100%' : `${data.measures.width}px`;
|
|
50
|
-
}
|
|
38
|
+
styles.height = data.config.isVertical ? data.config.dynamic ? 'auto' : `${data.measures.height}px` : '100%';
|
|
39
|
+
styles.width = data.config.isVertical ? '100%' : data.config.dynamic ? 'auto' : `${data.measures.width}px`;
|
|
40
|
+
}
|
|
41
|
+
this.data.set(v);
|
|
42
|
+
}
|
|
43
|
+
itemRenderer = signal(undefined);
|
|
44
|
+
set renderer(v) {
|
|
45
|
+
this.itemRenderer.set(v);
|
|
46
|
+
}
|
|
47
|
+
_elementRef = inject((ElementRef));
|
|
48
|
+
constructor() {
|
|
49
|
+
this._id = NgVirtualListItemComponent.__nextId = NgVirtualListItemComponent.__nextId === Number.MAX_SAFE_INTEGER
|
|
50
|
+
? 0 : NgVirtualListItemComponent.__nextId + 1;
|
|
51
|
+
}
|
|
52
|
+
getBounds() {
|
|
53
|
+
const el = this._elementRef.nativeElement, { width, height, left, top } = el.getBoundingClientRect();
|
|
54
|
+
return { width, height, x: left, y: top };
|
|
51
55
|
}
|
|
52
56
|
showIfNeed() {
|
|
53
57
|
const styles = this._elementRef.nativeElement.style;
|
|
@@ -64,13 +68,13 @@ class NgVirtualListItemComponent {
|
|
|
64
68
|
styles.visibility = 'hidden';
|
|
65
69
|
}
|
|
66
70
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
67
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", 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\">\r\n @if (renderer) {\r\n <ng-container [ngTemplateOutlet]=\"renderer\" [ngTemplateOutletContext]=\"{data: item.data || {}}\" />\r\n }\r\n</li>\r\n}", styles: [":host{position:absolute;left:0;top:0;
|
|
71
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", 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\">\r\n @if (renderer) {\r\n <ng-container [ngTemplateOutlet]=\"renderer\" [ngTemplateOutletContext]=\"{data: item.data || {}}\" />\r\n }\r\n</li>\r\n}", styles: [":host{display:block;position:absolute;left:0;top:0;box-sizing:border-box;overflow:hidden}.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.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
68
72
|
}
|
|
69
73
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListItemComponent, decorators: [{
|
|
70
74
|
type: Component,
|
|
71
75
|
args: [{ selector: 'ng-virtual-list-item', imports: [CommonModule], host: {
|
|
72
76
|
'class': 'ngvl__item',
|
|
73
|
-
}, changeDetection: ChangeDetectionStrategy.OnPush, 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\">\r\n @if (renderer) {\r\n <ng-container [ngTemplateOutlet]=\"renderer\" [ngTemplateOutletContext]=\"{data: item.data || {}}\" />\r\n }\r\n</li>\r\n}", styles: [":host{position:absolute;left:0;top:0;
|
|
77
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, 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\">\r\n @if (renderer) {\r\n <ng-container [ngTemplateOutlet]=\"renderer\" [ngTemplateOutletContext]=\"{data: item.data || {}}\" />\r\n }\r\n</li>\r\n}", styles: [":host{display:block;position:absolute;left:0;top:0;box-sizing:border-box;overflow:hidden}.ngvl-item__container{margin:0;padding:0;overflow:hidden;background-color:#fff;width:inherit;height:inherit}\n"] }]
|
|
74
78
|
}], ctorParameters: () => [] });
|
|
75
79
|
|
|
76
80
|
var Directions;
|
|
@@ -84,6 +88,8 @@ const DEFAULT_ITEMS_OFFSET = 2;
|
|
|
84
88
|
const DEFAULT_LIST_SIZE = 400;
|
|
85
89
|
const DEFAULT_SNAP = false;
|
|
86
90
|
const DEFAULT_SNAP_TO_ITEM = false;
|
|
91
|
+
const DEFAULT_DYNAMIC_SIZE = false;
|
|
92
|
+
const TRACK_BY_PROPERTY_NAME = 'id';
|
|
87
93
|
const DEFAULT_DIRECTION = Directions.VERTICAL;
|
|
88
94
|
const DISPLAY_OBJECTS_LENGTH_MESUREMENT_ERROR = 1;
|
|
89
95
|
|
|
@@ -105,7 +111,450 @@ const toggleClassName = (el, className, remove = false) => {
|
|
|
105
111
|
};
|
|
106
112
|
|
|
107
113
|
/**
|
|
108
|
-
*
|
|
114
|
+
* Tracks display items by property
|
|
115
|
+
* @homepage https://github.com/DjonnyX/ng-virtual-list/tree/main/projects/ng-virtual-list
|
|
116
|
+
* @author Evgenii Grebennikov
|
|
117
|
+
* @email djonnyx@gmail.com
|
|
118
|
+
*/
|
|
119
|
+
class Tracker {
|
|
120
|
+
/**
|
|
121
|
+
* display objects dictionary of indexes by id
|
|
122
|
+
*/
|
|
123
|
+
_displayObjectIndexMapById = {};
|
|
124
|
+
set displayObjectIndexMapById(v) {
|
|
125
|
+
if (this._displayObjectIndexMapById === v) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this._displayObjectIndexMapById = v;
|
|
129
|
+
}
|
|
130
|
+
get displayObjectIndexMapById() {
|
|
131
|
+
return this._displayObjectIndexMapById;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Dictionary displayItems propertyNameId by items propertyNameId
|
|
135
|
+
*/
|
|
136
|
+
_trackMap = {};
|
|
137
|
+
get trackMap() {
|
|
138
|
+
return this._trackMap;
|
|
139
|
+
}
|
|
140
|
+
_trackingPropertyName;
|
|
141
|
+
constructor(trackingPropertyName) {
|
|
142
|
+
this._trackingPropertyName = trackingPropertyName;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* tracking by propName
|
|
146
|
+
*/
|
|
147
|
+
track(items, components, afterComponentSetup) {
|
|
148
|
+
if (!items) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const idPropName = this._trackingPropertyName, untrackedItems = [...components];
|
|
152
|
+
for (let i = 0, l = items.length; i < l; i++) {
|
|
153
|
+
const item = items[i], itemTrackingProperty = item[idPropName];
|
|
154
|
+
if (this._trackMap) {
|
|
155
|
+
const diId = this._trackMap[itemTrackingProperty];
|
|
156
|
+
if (this._trackMap.hasOwnProperty(itemTrackingProperty)) {
|
|
157
|
+
const lastIndex = this._displayObjectIndexMapById[diId], el = components[lastIndex];
|
|
158
|
+
this._checkComponentProperty(el?.instance);
|
|
159
|
+
const elId = el?.instance?.[itemTrackingProperty];
|
|
160
|
+
if (el && elId === diId) {
|
|
161
|
+
const indexByUntrackedItems = untrackedItems.findIndex(v => {
|
|
162
|
+
this._checkComponentProperty(v.instance);
|
|
163
|
+
return v.instance[itemTrackingProperty] === elId;
|
|
164
|
+
});
|
|
165
|
+
if (indexByUntrackedItems > -1) {
|
|
166
|
+
el.instance.item = item;
|
|
167
|
+
if (afterComponentSetup !== undefined) {
|
|
168
|
+
afterComponentSetup(el.instance, item);
|
|
169
|
+
}
|
|
170
|
+
untrackedItems.splice(indexByUntrackedItems, 1);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
delete this._trackMap[itemTrackingProperty];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (untrackedItems.length > 0) {
|
|
178
|
+
const el = untrackedItems.shift(), item = items[i];
|
|
179
|
+
if (el) {
|
|
180
|
+
el.instance.item = item;
|
|
181
|
+
if (this._trackMap) {
|
|
182
|
+
this._checkComponentProperty(el.instance);
|
|
183
|
+
this._trackMap[itemTrackingProperty] = el.instance[itemTrackingProperty];
|
|
184
|
+
}
|
|
185
|
+
if (afterComponentSetup !== undefined) {
|
|
186
|
+
afterComponentSetup(el.instance, item);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (untrackedItems.length) {
|
|
192
|
+
throw Error('Tracking by id caused an error.');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
untrackComponentByIdProperty(component) {
|
|
196
|
+
if (!component) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const propertyIdName = this._trackingPropertyName;
|
|
200
|
+
this._checkComponentProperty(component);
|
|
201
|
+
if (this._trackMap && component[propertyIdName] !== undefined) {
|
|
202
|
+
delete this._trackMap[propertyIdName];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
_checkComponentProperty(component) {
|
|
206
|
+
if (!component) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const propertyIdName = this._trackingPropertyName;
|
|
210
|
+
try {
|
|
211
|
+
component[propertyIdName];
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
throw Error(`Property ${propertyIdName} does not exist.`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
dispose() {
|
|
218
|
+
this._trackMap = null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Simple event emitter
|
|
224
|
+
* @homepage https://github.com/DjonnyX/ng-virtual-list/tree/main/projects/ng-virtual-list
|
|
225
|
+
* @author Evgenii Grebennikov
|
|
226
|
+
* @email djonnyx@gmail.com
|
|
227
|
+
*/
|
|
228
|
+
class EventEmitter {
|
|
229
|
+
_listeners = {};
|
|
230
|
+
_disposed = false;
|
|
231
|
+
constructor() { }
|
|
232
|
+
/**
|
|
233
|
+
* Emits the event
|
|
234
|
+
*/
|
|
235
|
+
dispatch(event, ...args) {
|
|
236
|
+
const ctx = this;
|
|
237
|
+
const listeners = this._listeners[event];
|
|
238
|
+
if (Array.isArray(listeners)) {
|
|
239
|
+
for (let i = 0, l = listeners.length; i < l; i++) {
|
|
240
|
+
const listener = listeners[i];
|
|
241
|
+
if (listener) {
|
|
242
|
+
listener.apply(ctx, args);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Emits the event async
|
|
249
|
+
*/
|
|
250
|
+
dispatchAsync(event, ...args) {
|
|
251
|
+
queueMicrotask(() => {
|
|
252
|
+
if (this._disposed) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
this.dispatch(event, ...args);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Returns true if the event listener is already subscribed.
|
|
260
|
+
*/
|
|
261
|
+
hasEventListener(eventName, handler) {
|
|
262
|
+
const event = eventName;
|
|
263
|
+
if (this._listeners.hasOwnProperty(event)) {
|
|
264
|
+
const listeners = this._listeners[event];
|
|
265
|
+
const index = listeners.findIndex(v => v === handler);
|
|
266
|
+
if (index > -1) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Add event listener
|
|
274
|
+
*/
|
|
275
|
+
addEventListener(eventName, handler) {
|
|
276
|
+
const event = eventName;
|
|
277
|
+
if (!this._listeners.hasOwnProperty(event)) {
|
|
278
|
+
this._listeners[event] = [];
|
|
279
|
+
}
|
|
280
|
+
this._listeners[event].push(handler);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Remove event listener
|
|
284
|
+
*/
|
|
285
|
+
removeEventListener(eventName, handler) {
|
|
286
|
+
const event = eventName;
|
|
287
|
+
if (!this._listeners.hasOwnProperty(event)) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const listeners = this._listeners[event], index = listeners.findIndex(v => v === handler);
|
|
291
|
+
if (index > -1) {
|
|
292
|
+
listeners.splice(index, 1);
|
|
293
|
+
if (listeners.length === 0) {
|
|
294
|
+
delete this._listeners[event];
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Remove all listeners
|
|
300
|
+
*/
|
|
301
|
+
removeAllListeners() {
|
|
302
|
+
const events = Object.keys(this._listeners);
|
|
303
|
+
while (events.length > 0) {
|
|
304
|
+
const event = events.pop();
|
|
305
|
+
if (event) {
|
|
306
|
+
const listeners = this._listeners[event];
|
|
307
|
+
if (Array.isArray(listeners)) {
|
|
308
|
+
while (listeners.length > 0) {
|
|
309
|
+
const listener = listeners.pop();
|
|
310
|
+
if (listener) {
|
|
311
|
+
this.removeEventListener(event, listener);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
dispose() {
|
|
319
|
+
this._disposed = true;
|
|
320
|
+
this.removeAllListeners();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Cache map.
|
|
326
|
+
* Emits a change event on each mutation.
|
|
327
|
+
* @homepage https://github.com/DjonnyX/ng-virtual-list/tree/main/projects/ng-virtual-list
|
|
328
|
+
* @author Evgenii Grebennikov
|
|
329
|
+
* @email djonnyx@gmail.com
|
|
330
|
+
*/
|
|
331
|
+
class CacheMap extends EventEmitter {
|
|
332
|
+
_map = new Map();
|
|
333
|
+
_version = 0;
|
|
334
|
+
get version() {
|
|
335
|
+
return this._version;
|
|
336
|
+
}
|
|
337
|
+
constructor() {
|
|
338
|
+
super();
|
|
339
|
+
}
|
|
340
|
+
bumpVersion() {
|
|
341
|
+
this._version = this._version === Number.MAX_SAFE_INTEGER ? 0 : this._version + 1;
|
|
342
|
+
}
|
|
343
|
+
fireChange() {
|
|
344
|
+
this.dispatch('change', this.version);
|
|
345
|
+
}
|
|
346
|
+
set(id, bounds) {
|
|
347
|
+
if (this._map.has(id) && JSON.stringify(this._map.get(id)) === JSON.stringify(bounds)) {
|
|
348
|
+
return this._map;
|
|
349
|
+
}
|
|
350
|
+
const v = this._map.set(id, bounds);
|
|
351
|
+
this.bumpVersion();
|
|
352
|
+
this.fireChange();
|
|
353
|
+
return v;
|
|
354
|
+
}
|
|
355
|
+
has(id) {
|
|
356
|
+
return this._map.has(id);
|
|
357
|
+
}
|
|
358
|
+
get(id) {
|
|
359
|
+
return this._map.get(id);
|
|
360
|
+
}
|
|
361
|
+
forEach(callbackfn, thisArg) {
|
|
362
|
+
return this._map.forEach(callbackfn, thisArg);
|
|
363
|
+
}
|
|
364
|
+
dispose() {
|
|
365
|
+
super.dispose();
|
|
366
|
+
this._map.clear();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* An object that performs tracking, calculations and caching.
|
|
372
|
+
* @homepage https://github.com/DjonnyX/ng-virtual-list/tree/main/projects/ng-virtual-list
|
|
373
|
+
* @author Evgenii Grebennikov
|
|
374
|
+
* @email djonnyx@gmail.com
|
|
375
|
+
*/
|
|
376
|
+
class TrackBox extends CacheMap {
|
|
377
|
+
_tracker;
|
|
378
|
+
_items;
|
|
379
|
+
set items(v) {
|
|
380
|
+
if (this._items === v) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
this._items = v;
|
|
384
|
+
}
|
|
385
|
+
_displayComponents;
|
|
386
|
+
set displayComponents(v) {
|
|
387
|
+
if (this._displayComponents === v) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
this._displayComponents = v;
|
|
391
|
+
}
|
|
392
|
+
constructor(trackingPropertyName) {
|
|
393
|
+
super();
|
|
394
|
+
this._tracker = new Tracker(trackingPropertyName);
|
|
395
|
+
}
|
|
396
|
+
set(id, bounds) {
|
|
397
|
+
if (this._map.has(id) && JSON.stringify(this._map.get(id)) === JSON.stringify(bounds)) {
|
|
398
|
+
return this._map;
|
|
399
|
+
}
|
|
400
|
+
const v = this._map.set(id, bounds);
|
|
401
|
+
this.bumpVersion();
|
|
402
|
+
this.fireChange();
|
|
403
|
+
return v;
|
|
404
|
+
}
|
|
405
|
+
_fireChangeTimeouts = [];
|
|
406
|
+
fireChange() {
|
|
407
|
+
this.clearchangesTimeouts();
|
|
408
|
+
this._fireChangeTimeouts.push(setTimeout(() => { this.dispatchAsync('change', this._version); }));
|
|
409
|
+
}
|
|
410
|
+
clearchangesTimeouts() {
|
|
411
|
+
while (this._fireChangeTimeouts.length > 0) {
|
|
412
|
+
const timeout = this._fireChangeTimeouts.pop();
|
|
413
|
+
clearTimeout(timeout);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
recalculateMetrics(options) {
|
|
417
|
+
const { bounds, collection, dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap, } = options;
|
|
418
|
+
const { width, height } = bounds, sizeProperty = isVertical ? 'height' : 'width', size = isVertical ? height : width, weightToDisplayEnd = scrollSize + height, totalLength = collection.length, typicalItemSize = dynamicSize ? this.getTypicalItemSize(isVertical, itemSize) || itemSize : itemSize, totalSize = dynamicSize ? this.getBoundsFromCache(collection, typicalItemSize, isVertical) : totalLength * typicalItemSize, snippedPos = Math.floor(scrollSize);
|
|
419
|
+
let itemsFromStartToScrollEnd = -1, itemsFromDisplayEndToOffsetEnd = 0, itemsFromStartToDisplayEnd = -1, leftItemLength = 0, rightItemLength = 0, leftItemsWeight = 0, rightItemsWeight = 0, startIndex;
|
|
420
|
+
if (dynamicSize) {
|
|
421
|
+
let y = 0;
|
|
422
|
+
for (let i = 0, l = collection.length; i < l; i++) {
|
|
423
|
+
const collectionItem = collection[i], map = this._map;
|
|
424
|
+
let itemSize = 0;
|
|
425
|
+
if (map.has(collectionItem.id)) {
|
|
426
|
+
const bounds = map.get(collectionItem.id);
|
|
427
|
+
itemSize = bounds ? bounds[sizeProperty] : typicalItemSize;
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
itemSize = typicalItemSize;
|
|
431
|
+
}
|
|
432
|
+
if (itemsFromStartToScrollEnd === -1 && y >= scrollSize && y <= scrollSize + itemSize) {
|
|
433
|
+
leftItemsWeight += itemSize;
|
|
434
|
+
itemsFromStartToScrollEnd = i;
|
|
435
|
+
}
|
|
436
|
+
if (itemsFromStartToDisplayEnd === -1) {
|
|
437
|
+
if (y >= weightToDisplayEnd && y <= weightToDisplayEnd + itemSize) {
|
|
438
|
+
itemsFromStartToDisplayEnd = i;
|
|
439
|
+
itemsFromDisplayEndToOffsetEnd = itemsFromStartToDisplayEnd + itemsOffset;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
if (i <= itemsFromDisplayEndToOffsetEnd) {
|
|
444
|
+
rightItemsWeight += itemSize;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
y += itemSize;
|
|
448
|
+
}
|
|
449
|
+
leftItemLength = Math.min(itemsFromStartToScrollEnd, itemsOffset);
|
|
450
|
+
rightItemLength = itemsFromStartToDisplayEnd + itemsOffset > totalLength
|
|
451
|
+
? totalLength - itemsFromStartToDisplayEnd : itemsOffset;
|
|
452
|
+
startIndex = itemsFromStartToScrollEnd - leftItemLength;
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
itemsFromStartToScrollEnd = Math.ceil(scrollSize / typicalItemSize);
|
|
456
|
+
itemsFromStartToDisplayEnd = Math.ceil((scrollSize + size) / typicalItemSize);
|
|
457
|
+
leftItemLength = Math.min(itemsFromStartToScrollEnd, itemsOffset);
|
|
458
|
+
rightItemLength = itemsFromStartToDisplayEnd + itemsOffset > totalLength
|
|
459
|
+
? totalLength - itemsFromStartToDisplayEnd : itemsOffset;
|
|
460
|
+
startIndex = itemsFromStartToScrollEnd - leftItemLength;
|
|
461
|
+
leftItemsWeight = leftItemLength * typicalItemSize;
|
|
462
|
+
rightItemsWeight = rightItemLength * typicalItemSize;
|
|
463
|
+
}
|
|
464
|
+
const leftHiddenItemsWeight = itemsFromStartToScrollEnd * typicalItemSize, totalItemsToDisplayEndWeight = itemsFromStartToDisplayEnd * typicalItemSize, itemsOnDisplay = totalItemsToDisplayEndWeight - leftHiddenItemsWeight;
|
|
465
|
+
const metrics = {
|
|
466
|
+
itemsFromStartToScrollEnd,
|
|
467
|
+
itemsFromStartToDisplayEnd,
|
|
468
|
+
itemsOnDisplay,
|
|
469
|
+
leftHiddenItemsWeight,
|
|
470
|
+
leftItemLength,
|
|
471
|
+
leftItemsWeight,
|
|
472
|
+
rightItemLength,
|
|
473
|
+
rightItemsWeight,
|
|
474
|
+
snippedPos,
|
|
475
|
+
totalItemsToDisplayEndWeight,
|
|
476
|
+
totalSize,
|
|
477
|
+
typicalItemSize,
|
|
478
|
+
};
|
|
479
|
+
return metrics;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Calculates and returns the maximum size of a repeating item
|
|
483
|
+
*/
|
|
484
|
+
getTypicalItemSize(isVertical, defaultItemSize, fromIndex = 0, to = -1) {
|
|
485
|
+
const sizeProperty = isVertical ? 'height' : 'width', sizes = {};
|
|
486
|
+
let maxRepeatingSize = defaultItemSize, count = 0;
|
|
487
|
+
this._map.forEach(bound => {
|
|
488
|
+
const size = bound[sizeProperty];
|
|
489
|
+
if (sizes.hasOwnProperty(size)) {
|
|
490
|
+
sizes[size] += 1;
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
sizes[size] = 1;
|
|
494
|
+
}
|
|
495
|
+
if (sizes[size] > count) {
|
|
496
|
+
count = sizes[size];
|
|
497
|
+
maxRepeatingSize = size;
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
return maxRepeatingSize;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* tracking by propName
|
|
504
|
+
*/
|
|
505
|
+
track(dynamicSize = false) {
|
|
506
|
+
if (!this._items || !this._displayComponents) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
this._tracker.track(this._items, this._displayComponents, dynamicSize ? (component, item) => {
|
|
510
|
+
this.cacheElementBounds(component, item);
|
|
511
|
+
} : undefined);
|
|
512
|
+
}
|
|
513
|
+
setDisplayObjectIndexMapById(v) {
|
|
514
|
+
this._tracker.displayObjectIndexMapById = v;
|
|
515
|
+
}
|
|
516
|
+
untrackComponentByIdProperty(component) {
|
|
517
|
+
this._tracker.untrackComponentByIdProperty(component);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Stores the element bounds in _sizeCacheMap
|
|
521
|
+
*/
|
|
522
|
+
cacheElementBounds(component, item) {
|
|
523
|
+
component.item = item;
|
|
524
|
+
const bounds = component.getBounds();
|
|
525
|
+
this.set(item.id, bounds);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Returns calculated bounds from cache
|
|
529
|
+
*/
|
|
530
|
+
getBoundsFromCache(items, typicalItemSize, isVertical) {
|
|
531
|
+
const sizeProperty = isVertical ? 'height' : 'width', map = this._map;
|
|
532
|
+
let size = 0;
|
|
533
|
+
for (let i = 0, l = items.length; i < l; i++) {
|
|
534
|
+
const item = items[i];
|
|
535
|
+
if (map.has(item.id)) {
|
|
536
|
+
const bounds = map.get(item.id);
|
|
537
|
+
size += bounds ? bounds[sizeProperty] : typicalItemSize;
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
size += typicalItemSize;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return size;
|
|
544
|
+
}
|
|
545
|
+
dispose() {
|
|
546
|
+
super.dispose();
|
|
547
|
+
this.clearchangesTimeouts();
|
|
548
|
+
if (this._tracker) {
|
|
549
|
+
this._tracker.dispose();
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Virtual list component.
|
|
556
|
+
* Maximum performance for extremely large lists.
|
|
557
|
+
* It is based on algorithms for virtualization of screen objects.
|
|
109
558
|
* @homepage https://github.com/DjonnyX/ng-virtual-list/tree/main/projects/ng-virtual-list
|
|
110
559
|
* @author Evgenii Grebennikov
|
|
111
560
|
* @email djonnyx@gmail.com
|
|
@@ -151,8 +600,14 @@ class NgVirtualListComponent {
|
|
|
151
600
|
stickyMap = input({});
|
|
152
601
|
/**
|
|
153
602
|
* If direction = 'vertical', then the height of a typical element. If direction = 'horizontal', then the width of a typical element.
|
|
603
|
+
* Ignored if the dynamicSize property is true.
|
|
154
604
|
*/
|
|
155
605
|
itemSize = input(DEFAULT_ITEM_SIZE);
|
|
606
|
+
/**
|
|
607
|
+
* If true then the items in the list can have different sizes and the itemSize property is ignored.
|
|
608
|
+
* If false then the items in the list have a fixed size specified by the itemSize property. The default value is false.
|
|
609
|
+
*/
|
|
610
|
+
dynamicSize = input(DEFAULT_DYNAMIC_SIZE);
|
|
156
611
|
/**
|
|
157
612
|
* Determines the direction in which elements are placed. Default value is "vertical".
|
|
158
613
|
*/
|
|
@@ -184,33 +639,66 @@ class NgVirtualListComponent {
|
|
|
184
639
|
this.onScrollEnd.emit(e);
|
|
185
640
|
};
|
|
186
641
|
_elementRef = inject((ElementRef));
|
|
187
|
-
|
|
188
|
-
|
|
642
|
+
_initialized = signal(false);
|
|
643
|
+
/**
|
|
644
|
+
* Dictionary of element sizes by their id
|
|
645
|
+
*/
|
|
646
|
+
_trackBox = new TrackBox(TRACK_BY_PROPERTY_NAME);
|
|
647
|
+
_onTrackBoxChangeHandler = (v) => {
|
|
648
|
+
this._$cacheVersion.next(v);
|
|
649
|
+
};
|
|
650
|
+
_$cacheVersion = new BehaviorSubject(-1);
|
|
651
|
+
get $cacheVersion() { return this._$cacheVersion.asObservable(); }
|
|
189
652
|
constructor() {
|
|
190
653
|
NgVirtualListComponent.__nextId = NgVirtualListComponent.__nextId + 1 === Number.MAX_SAFE_INTEGER
|
|
191
654
|
? 0 : NgVirtualListComponent.__nextId + 1;
|
|
192
655
|
this._id = NgVirtualListComponent.__nextId;
|
|
193
|
-
|
|
656
|
+
this._trackBox.displayComponents = this._displayComponents;
|
|
657
|
+
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), $cacheVersion = this.$cacheVersion, $displayItems = toObservable(this._displayItems), $initialized = toObservable(this._initialized);
|
|
658
|
+
$isVertical.pipe(takeUntilDestroyed(), tap(v => {
|
|
194
659
|
this._isVertical = v;
|
|
195
660
|
const el = this._elementRef.nativeElement;
|
|
196
661
|
toggleClassName(el, v ? 'vertical' : 'horizontal', true);
|
|
197
|
-
}));
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
662
|
+
})).subscribe();
|
|
663
|
+
$dynamicSize.pipe(takeUntilDestroyed(), tap(dynamicSize => {
|
|
664
|
+
if (dynamicSize) {
|
|
665
|
+
if (!this._trackBox.hasEventListener('change', this._onTrackBoxChangeHandler)) {
|
|
666
|
+
this._trackBox.addEventListener('change', this._onTrackBoxChangeHandler);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
if (this._trackBox.hasEventListener('change', this._onTrackBoxChangeHandler)) {
|
|
671
|
+
this._trackBox.removeEventListener('change', this._onTrackBoxChangeHandler);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
})).subscribe();
|
|
675
|
+
$displayItems.pipe(takeUntilDestroyed(), tap((displayItems) => {
|
|
676
|
+
this._trackBox.items = displayItems;
|
|
677
|
+
})).subscribe();
|
|
678
|
+
combineLatest([$initialized, $bounds, $items, $stickyMap, $scrollSize, $itemSize,
|
|
679
|
+
$itemsOffset, $snap, $isVertical, $dynamicSize, $cacheVersion,
|
|
680
|
+
]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), switchMap(([, bounds, items, stickyMap, scrollSize, itemSize, itemsOffset, snap, isVertical, dynamicSize, cacheVersion,]) => {
|
|
681
|
+
const { width, height } = bounds, { itemsFromStartToScrollEnd, itemsOnDisplay, leftHiddenItemsWeight, leftItemLength, leftItemsWeight, rightItemsWeight, snippedPos, totalSize, typicalItemSize, } = this._trackBox.recalculateMetrics({
|
|
682
|
+
bounds: { width, height }, collection: items,
|
|
683
|
+
dynamicSize, isVertical, itemSize, itemsOffset, scrollSize, snap,
|
|
684
|
+
});
|
|
685
|
+
// Нужно сперва сделать корректные расчеты для dynamicSize. Сейчас количества элементов в облостях высчитывается не корректно!
|
|
686
|
+
// Необходима кореляция startDisplayObjectY с помощью дельты от высоты предыдущей и текущей размеченной области по версии кэша.
|
|
687
|
+
// TrackBox может расчитать дельту!
|
|
201
688
|
return of({
|
|
202
|
-
items, stickyMap,
|
|
203
|
-
|
|
689
|
+
items, stickyMap, width, height, isVertical, scrollSize, itemsFromStartToScrollEnd,
|
|
690
|
+
itemsOnDisplay, leftHiddenItemsWeight, itemSize: typicalItemSize, totalSize, snap,
|
|
691
|
+
leftItemLength, leftItemsWeight, rightItemsWeight, snippedPos, dynamicSize,
|
|
204
692
|
});
|
|
205
|
-
}), tap(({ items, stickyMap,
|
|
693
|
+
}), tap(({ items, stickyMap, width, height, isVertical, scrollSize, itemsFromStartToScrollEnd, itemsOnDisplay, leftHiddenItemsWeight, leftItemLength, leftItemsWeight, rightItemsWeight, snippedPos, itemSize, totalSize, snap, dynamicSize: dynamic, }) => {
|
|
206
694
|
const displayItems = [];
|
|
207
695
|
if (items.length) {
|
|
208
|
-
const w = isVertical ? width : itemSize, h = isVertical ? itemSize : height, totalItems = items.length,
|
|
209
|
-
|
|
210
|
-
let pos = leftHiddenItemsWeight - leftItemsWeight, renderWeight = itemsOnDisplay + leftItemsWeight + rightItemsWeight, stickyItem, nextSticky, stickyItemIndex = -1;
|
|
696
|
+
const sizeProperty = isVertical ? 'height' : 'width', w = isVertical ? width : itemSize, h = isVertical ? itemSize : height, totalItems = items.length, startIndex = itemsFromStartToScrollEnd - leftItemLength;
|
|
697
|
+
let pos = leftHiddenItemsWeight - leftItemsWeight, renderWeight = itemsOnDisplay + leftItemsWeight + rightItemsWeight, stickyItem, nextSticky, stickyItemIndex = -1, stickyItemSize = 0;
|
|
211
698
|
if (snap) {
|
|
212
699
|
for (let i = itemsFromStartToScrollEnd - 1; i >= 0; i--) {
|
|
213
700
|
const id = items[i].id, sticky = stickyMap[id];
|
|
701
|
+
stickyItemSize = dynamic ? this._trackBox.get(id)?.[sizeProperty] || itemSize : itemSize;
|
|
214
702
|
if (sticky > 0) {
|
|
215
703
|
const measures = {
|
|
216
704
|
x: isVertical ? 0 : snippedPos,
|
|
@@ -222,6 +710,7 @@ class NgVirtualListComponent {
|
|
|
222
710
|
sticky,
|
|
223
711
|
snap,
|
|
224
712
|
snapped: true,
|
|
713
|
+
dynamic,
|
|
225
714
|
};
|
|
226
715
|
const itemData = items[i];
|
|
227
716
|
stickyItem = { id, measures, data: itemData, config };
|
|
@@ -236,7 +725,7 @@ class NgVirtualListComponent {
|
|
|
236
725
|
if (i >= totalItems) {
|
|
237
726
|
break;
|
|
238
727
|
}
|
|
239
|
-
const id = items[i].id;
|
|
728
|
+
const id = items[i].id, size = dynamic ? this._trackBox.get(id)?.[sizeProperty] || itemSize : itemSize;
|
|
240
729
|
if (id !== stickyItem?.id) {
|
|
241
730
|
const snaped = snap && stickyMap[id] > 0 && pos <= scrollSize, measures = {
|
|
242
731
|
x: isVertical ? 0 : pos,
|
|
@@ -248,26 +737,25 @@ class NgVirtualListComponent {
|
|
|
248
737
|
sticky: stickyMap[id],
|
|
249
738
|
snap,
|
|
250
739
|
snapped: false,
|
|
740
|
+
dynamic,
|
|
251
741
|
};
|
|
252
742
|
const itemData = items[i];
|
|
253
743
|
const item = { id, measures, data: itemData, config };
|
|
254
|
-
if (!nextSticky && stickyItemIndex < i && snap && stickyMap[id] > 0 && pos <= scrollSize +
|
|
744
|
+
if (!nextSticky && stickyItemIndex < i && snap && stickyMap[id] > 0 && pos <= scrollSize + size) {
|
|
255
745
|
item.measures.x = isVertical ? 0 : snaped ? snippedPos : pos;
|
|
256
746
|
item.measures.y = isVertical ? snaped ? snippedPos : pos : 0;
|
|
257
747
|
nextSticky = item;
|
|
258
748
|
}
|
|
259
749
|
displayItems.push(item);
|
|
260
|
-
// for dynamic item size
|
|
261
|
-
// this._sizeCacheMap.set(id, measures);
|
|
262
750
|
}
|
|
263
|
-
renderWeight -=
|
|
264
|
-
pos +=
|
|
751
|
+
renderWeight -= size;
|
|
752
|
+
pos += size;
|
|
265
753
|
i++;
|
|
266
754
|
}
|
|
267
755
|
const axis = isVertical ? 'y' : 'x';
|
|
268
|
-
if (nextSticky && stickyItem && nextSticky.measures[axis] <= scrollSize +
|
|
756
|
+
if (nextSticky && stickyItem && nextSticky.measures[axis] <= scrollSize + stickyItemSize) {
|
|
269
757
|
if (nextSticky.measures[axis] > scrollSize) {
|
|
270
|
-
stickyItem.measures[axis] = nextSticky.measures[axis] -
|
|
758
|
+
stickyItem.measures[axis] = nextSticky.measures[axis] - stickyItemSize;
|
|
271
759
|
stickyItem.config.snapped = nextSticky.config.snapped = false;
|
|
272
760
|
stickyItem.config.sticky = 1;
|
|
273
761
|
}
|
|
@@ -277,26 +765,26 @@ class NgVirtualListComponent {
|
|
|
277
765
|
}
|
|
278
766
|
}
|
|
279
767
|
this._displayItems.set(displayItems);
|
|
280
|
-
|
|
281
|
-
if (l) {
|
|
282
|
-
l.nativeElement.style[isVertical ? 'height' : 'width'] = `${totalSize}px`;
|
|
283
|
-
}
|
|
768
|
+
this.resetBoundsSize(isVertical, totalSize);
|
|
284
769
|
})).subscribe();
|
|
285
|
-
toObservable(this.itemRenderer).pipe(takeUntilDestroyed(), distinctUntilChanged(), tap(itemRenderer => {
|
|
770
|
+
combineLatest([$initialized, toObservable(this.itemRenderer)]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), tap(([, itemRenderer]) => {
|
|
286
771
|
this.resetRenderers(itemRenderer);
|
|
287
772
|
}));
|
|
288
|
-
|
|
773
|
+
combineLatest([$initialized, $displayItems]).pipe(takeUntilDestroyed(), distinctUntilChanged(), filter(([initialized]) => !!initialized), tap(([, displayItems]) => {
|
|
289
774
|
this.createDisplayComponentsIfNeed(displayItems);
|
|
290
|
-
this.tracking(
|
|
775
|
+
this.tracking();
|
|
291
776
|
})).subscribe();
|
|
292
777
|
}
|
|
778
|
+
ngOnInit() {
|
|
779
|
+
this._initialized.set(true);
|
|
780
|
+
}
|
|
293
781
|
getIsVertical(d) {
|
|
294
782
|
const dir = d || this.direction();
|
|
295
783
|
return isDirection(dir, Directions.VERTICAL);
|
|
296
784
|
}
|
|
297
785
|
createDisplayComponentsIfNeed(displayItems) {
|
|
298
786
|
if (!displayItems || !this._listContainerRef) {
|
|
299
|
-
this.
|
|
787
|
+
this._trackBox.setDisplayObjectIndexMapById({});
|
|
300
788
|
return;
|
|
301
789
|
}
|
|
302
790
|
const _listContainerRef = this._listContainerRef;
|
|
@@ -312,7 +800,7 @@ class NgVirtualListComponent {
|
|
|
312
800
|
comp?.destroy();
|
|
313
801
|
const id = comp?.instance.item?.id;
|
|
314
802
|
if (id !== undefined) {
|
|
315
|
-
|
|
803
|
+
this._trackBox.untrackComponentByIdProperty(comp?.instance);
|
|
316
804
|
}
|
|
317
805
|
}
|
|
318
806
|
this.resetRenderers();
|
|
@@ -324,48 +812,18 @@ class NgVirtualListComponent {
|
|
|
324
812
|
item.instance.renderer = itemRenderer || this.itemRenderer();
|
|
325
813
|
doMap[item.instance.id] = i;
|
|
326
814
|
}
|
|
327
|
-
this.
|
|
815
|
+
this._trackBox.setDisplayObjectIndexMapById(doMap);
|
|
328
816
|
}
|
|
329
|
-
/**
|
|
330
|
-
* Dictionary displayItems id by IRenderVirtualListItem.id
|
|
331
|
-
*/
|
|
332
|
-
_trackMap = {};
|
|
333
|
-
/**
|
|
334
|
-
* displayItems dictionary of indexes by id
|
|
335
|
-
*/
|
|
336
|
-
_doMap = {};
|
|
337
817
|
/**
|
|
338
818
|
* tracking by id
|
|
339
819
|
*/
|
|
340
|
-
tracking(
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (this._trackMap.hasOwnProperty(item.id)) {
|
|
348
|
-
const lastIndex = this._doMap[doId], el = this._displayComponents[lastIndex], elId = el?.instance.id;
|
|
349
|
-
if (el && elId === doId) {
|
|
350
|
-
const indexByUntrackedItems = untrackedItems.findIndex(v => v.instance.id === elId);
|
|
351
|
-
if (indexByUntrackedItems > -1) {
|
|
352
|
-
el.instance.item = item;
|
|
353
|
-
untrackedItems.splice(indexByUntrackedItems, 1);
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
delete this._trackMap[item.id];
|
|
358
|
-
}
|
|
359
|
-
if (untrackedItems.length > 0) {
|
|
360
|
-
const el = untrackedItems.shift(), item = displayItems[i];
|
|
361
|
-
if (el) {
|
|
362
|
-
el.instance.item = item;
|
|
363
|
-
this._trackMap[item.id] = el.instance.id;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
if (untrackedItems.length) {
|
|
368
|
-
throw Error('tracking by id caused an error');
|
|
820
|
+
tracking() {
|
|
821
|
+
this._trackBox.track(this.dynamicSize());
|
|
822
|
+
}
|
|
823
|
+
resetBoundsSize(isVertical, totalSize) {
|
|
824
|
+
const l = this._list();
|
|
825
|
+
if (l) {
|
|
826
|
+
l.nativeElement.style[isVertical ? 'height' : 'width'] = `${totalSize}px`;
|
|
369
827
|
}
|
|
370
828
|
}
|
|
371
829
|
/**
|
|
@@ -394,6 +852,9 @@ class NgVirtualListComponent {
|
|
|
394
852
|
}
|
|
395
853
|
}
|
|
396
854
|
ngOnDestroy() {
|
|
855
|
+
if (this._trackBox) {
|
|
856
|
+
this._trackBox.dispose();
|
|
857
|
+
}
|
|
397
858
|
const containerEl = this._container();
|
|
398
859
|
if (containerEl) {
|
|
399
860
|
containerEl.nativeElement.removeEventListener('scroll', this._onScrollHandler);
|
|
@@ -410,7 +871,7 @@ class NgVirtualListComponent {
|
|
|
410
871
|
}
|
|
411
872
|
}
|
|
412
873
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
413
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.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 }, snapToItem: { classPropertyName: "snapToItem", publicName: "snapToItem", 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 }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, itemsOffset: { classPropertyName: "itemsOffset", publicName: "itemsOffset", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd" }, viewQueries: [{ 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 }], ngImport: i0, template: "<div #container part=\"scroller\" class=\"ngvl__container\">\r\n <ul #list part=\"list\" class=\"ngvl__list\">\r\n <ng-container #renderersContainer></ng-container>\r\n </ul>\r\n</div>", styles: [":host{display:block;width:400px;overflow:hidden}:host(.horizontal){height:48px}:host(.vertical){height:320px}.ngvl__container{overflow:auto;width:100%;height:100%}.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 });
|
|
874
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.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 }, snapToItem: { classPropertyName: "snapToItem", publicName: "snapToItem", 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 } }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd" }, viewQueries: [{ 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 }], ngImport: i0, template: "<div #container part=\"scroller\" class=\"ngvl__container\">\r\n <ul #list part=\"list\" class=\"ngvl__list\">\r\n <ng-container #renderersContainer></ng-container>\r\n </ul>\r\n</div>", styles: [":host{display:block;width:400px;overflow:hidden}:host(.horizontal){height:48px}:host(.vertical){height:320px}.ngvl__container{overflow:auto;width:100%;height:100%}.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 });
|
|
414
875
|
}
|
|
415
876
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListComponent, decorators: [{
|
|
416
877
|
type: Component,
|