ng-virtual-list 19.7.2 → 19.7.4
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 +6 -1
- package/fesm2022/ng-virtual-list.mjs +174 -24
- package/fesm2022/ng-virtual-list.mjs.map +1 -1
- package/lib/components/ng-virtual-list-item.component.d.ts +24 -1
- package/lib/const/index.d.ts +6 -4
- package/lib/models/render-item-config.model.d.ts +4 -0
- package/lib/ng-virtual-list.component.d.ts +6 -1
- package/lib/ng-virtual-list.service.d.ts +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,6 +44,10 @@ As each item may contain images, nested components, or interactions, virtual ren
|
|
|
44
44
|
|
|
45
45
|
Single and multiple selection of elements
|
|
46
46
|
|
|
47
|
+
Navigating with the keyboard
|
|
48
|
+
|
|
49
|
+
Support for element animation
|
|
50
|
+
|
|
47
51
|
<br/>
|
|
48
52
|
|
|
49
53
|
## Installation
|
|
@@ -536,7 +540,8 @@ Inputs
|
|
|
536
540
|
| maxBufferSize | number? = 100 | Maximum number of elements outside the scope of visibility. Default value is 100. If maxBufferSize is set to be greater than bufferSize, then adaptive buffer mode is enabled. The greater the scroll size, the more elements are allocated for rendering. |
|
|
537
541
|
| itemRenderer | TemplateRef | Rendering element template. |
|
|
538
542
|
| methodForSelecting | [MethodForSelecting](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/enums/method-for-selecting.ts) | Method for selecting list items. Default value is 'none'. 'select' - List items are selected one by one. 'multi-select' - Multiple selection of list items. 'none' - List items are not selectable. |
|
|
539
|
-
| itemConfigMap | [IVirtualListItemConfigMap?](https://github.com/DjonnyX/ng-virtual-list/blob/
|
|
543
|
+
| itemConfigMap | [IVirtualListItemConfigMap?](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/models/item-config-map.model.ts) | Sets sticky position and selectable for the list item element. If sticky position is greater than 0, then sticky position is applied. If the sticky value is greater than `0`, then the sticky position mode is enabled for the element. `1` - position start, `2` - position end. Default value is `0`. Selectable determines whether an element can be selected or not. Default value is `true`. |
|
|
544
|
+
| selectByClick | boolean? = true | If `false`, the element is selected using the config.select method passed to the template; if `true`, the element is selected by clicking on it. The default value is `true`. |
|
|
540
545
|
| snap | boolean? = false | Determines whether elements will snap. Default value is "false". |
|
|
541
546
|
| snappingMethod | [SnappingMethod? = 'normal'](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/enums/snapping-method.ts) | Snapping method. 'normal' - Normal group rendering. 'advanced' - The group is rendered on a transparent background. List items below the group are not rendered. |
|
|
542
547
|
| direction | [Direction? = 'vertical'](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/enums/direction.ts) | Determines the direction in which elements are placed. Default value is "vertical". |
|
|
@@ -2,7 +2,7 @@ import * as i1 from '@angular/common';
|
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { Injectable, inject, signal, ElementRef, ChangeDetectionStrategy, Component, viewChild, output, input, ViewContainerRef, ViewChild, ViewEncapsulation } from '@angular/core';
|
|
5
|
-
import { Subject, tap, combineLatest, map, filter, distinctUntilChanged, switchMap, of } from 'rxjs';
|
|
5
|
+
import { Subject, tap, fromEvent, combineLatest, map, filter, distinctUntilChanged, switchMap, of } from 'rxjs';
|
|
6
6
|
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
|
7
7
|
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
|
|
8
8
|
|
|
@@ -69,6 +69,7 @@ const DEFAULT_BUFFER_SIZE = 2;
|
|
|
69
69
|
const DEFAULT_MAX_BUFFER_SIZE = 100;
|
|
70
70
|
const DEFAULT_LIST_SIZE = 400;
|
|
71
71
|
const DEFAULT_SNAP = false;
|
|
72
|
+
const DEFAULT_SELECT_BY_CLICK = true;
|
|
72
73
|
const DEFAULT_ENABLED_BUFFER_OPTIMIZATION = false;
|
|
73
74
|
const DEFAULT_DYNAMIC_SIZE = false;
|
|
74
75
|
const TRACK_BY_PROPERTY_NAME = 'id';
|
|
@@ -108,10 +109,11 @@ const CLASS_LIST_VERTICAL = 'vertical';
|
|
|
108
109
|
const CLASS_LIST_HORIZONTAL = 'horizontal';
|
|
109
110
|
// styles
|
|
110
111
|
const PART_DEFAULT_ITEM = 'item';
|
|
111
|
-
const PART_ITEM_ODD = ' odd';
|
|
112
|
-
const PART_ITEM_EVEN = ' even';
|
|
113
|
-
const PART_ITEM_SNAPPED = ' snapped';
|
|
114
|
-
const PART_ITEM_SELECTED = ' selected';
|
|
112
|
+
const PART_ITEM_ODD = ' item-odd';
|
|
113
|
+
const PART_ITEM_EVEN = ' item-even';
|
|
114
|
+
const PART_ITEM_SNAPPED = ' item-snapped';
|
|
115
|
+
const PART_ITEM_SELECTED = ' item-selected';
|
|
116
|
+
const PART_ITEM_FOCUSED = ' item-focused';
|
|
115
117
|
|
|
116
118
|
/**
|
|
117
119
|
* Virtual List Item Interface
|
|
@@ -155,7 +157,9 @@ class NgVirtualListService {
|
|
|
155
157
|
set methodOfSelecting(v) {
|
|
156
158
|
this._$methodOfSelecting.next(v);
|
|
157
159
|
}
|
|
160
|
+
selectByClick = DEFAULT_SELECT_BY_CLICK;
|
|
158
161
|
_trackBox;
|
|
162
|
+
listElement = null;
|
|
159
163
|
constructor() {
|
|
160
164
|
this._$methodOfSelecting.pipe(takeUntilDestroyed(), tap(v => {
|
|
161
165
|
switch (v) {
|
|
@@ -181,25 +185,61 @@ class NgVirtualListService {
|
|
|
181
185
|
})).subscribe();
|
|
182
186
|
}
|
|
183
187
|
setSelectedIds(ids) {
|
|
184
|
-
this._$selectedIds.
|
|
188
|
+
if (JSON.stringify(this._$selectedIds.getValue()) !== JSON.stringify(ids)) {
|
|
189
|
+
this._$selectedIds.next(ids);
|
|
190
|
+
}
|
|
185
191
|
}
|
|
186
192
|
itemClick(data) {
|
|
187
193
|
this._$itemClick.next(data);
|
|
194
|
+
if (this.selectByClick) {
|
|
195
|
+
this.select(data);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Selects a list item
|
|
200
|
+
* @param data
|
|
201
|
+
* @param selected - If the value is undefined, then the toggle method is executed, if false or true, then the selection/deselection is performed.
|
|
202
|
+
*/
|
|
203
|
+
select(data, selected = undefined) {
|
|
188
204
|
if (data && data.config.selectable) {
|
|
189
205
|
switch (this._$methodOfSelecting.getValue()) {
|
|
190
206
|
case MethodsForSelectingTypes.SELECT: {
|
|
191
207
|
const curr = this._$selectedIds.getValue();
|
|
192
|
-
|
|
208
|
+
if (selected === undefined) {
|
|
209
|
+
this._$selectedIds.next(curr !== data?.id ? data?.id : undefined);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
this._$selectedIds.next(selected ? data?.id : undefined);
|
|
213
|
+
}
|
|
193
214
|
break;
|
|
194
215
|
}
|
|
195
216
|
case MethodsForSelectingTypes.MULTI_SELECT: {
|
|
196
217
|
const curr = [...(this._$selectedIds.getValue() || [])], index = curr.indexOf(data.id);
|
|
197
|
-
if (
|
|
198
|
-
|
|
199
|
-
|
|
218
|
+
if (selected === undefined) {
|
|
219
|
+
if (index > -1) {
|
|
220
|
+
curr.splice(index, 1);
|
|
221
|
+
this._$selectedIds.next(curr);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
this._$selectedIds.next([...curr, data.id]);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else if (selected) {
|
|
228
|
+
if (index > -1) {
|
|
229
|
+
this._$selectedIds.next(curr);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
this._$selectedIds.next([...curr, data.id]);
|
|
233
|
+
}
|
|
200
234
|
}
|
|
201
235
|
else {
|
|
202
|
-
|
|
236
|
+
if (index > -1) {
|
|
237
|
+
curr.splice(index, 1);
|
|
238
|
+
this._$selectedIds.next(curr);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
this._$selectedIds.next(curr);
|
|
242
|
+
}
|
|
203
243
|
}
|
|
204
244
|
break;
|
|
205
245
|
}
|
|
@@ -227,7 +267,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
227
267
|
}]
|
|
228
268
|
}], ctorParameters: () => [] });
|
|
229
269
|
|
|
230
|
-
const ATTR_AREA_SELECTED = 'area-selected';
|
|
270
|
+
const ATTR_AREA_SELECTED = 'area-selected', TABINDEX = 'index', KEY_SPACE = " ", KEY_ARR_LEFT = "ArrowLeft", KEY_ARR_UP = "ArrowUp", KEY_ARR_RIGHT = "ArrowRight", KEY_ARR_DOWN = "ArrowDown";
|
|
231
271
|
/**
|
|
232
272
|
* Virtual list item component
|
|
233
273
|
* @link https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/components/ng-virtual-list-item.component.ts
|
|
@@ -242,6 +282,8 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
|
|
|
242
282
|
_service = inject(NgVirtualListService);
|
|
243
283
|
_isSelected = false;
|
|
244
284
|
config = signal({});
|
|
285
|
+
measures = signal(undefined);
|
|
286
|
+
focus = signal(false);
|
|
245
287
|
_part = PART_DEFAULT_ITEM;
|
|
246
288
|
get part() { return this._part; }
|
|
247
289
|
regular = false;
|
|
@@ -254,6 +296,7 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
|
|
|
254
296
|
this._data = v;
|
|
255
297
|
this.updatePartStr(v, this._isSelected);
|
|
256
298
|
this.updateConfig(v);
|
|
299
|
+
this.updateMeasures(v);
|
|
257
300
|
this.update();
|
|
258
301
|
this.data.set(v);
|
|
259
302
|
}
|
|
@@ -279,37 +322,115 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
|
|
|
279
322
|
get element() {
|
|
280
323
|
return this._elementRef.nativeElement;
|
|
281
324
|
}
|
|
325
|
+
_selectHandler = (data) =>
|
|
326
|
+
/**
|
|
327
|
+
* Selects a list item
|
|
328
|
+
* @param selected - If the value is undefined, then the toggle method is executed, if false or true, then the selection/deselection is performed.
|
|
329
|
+
*/
|
|
330
|
+
(selected = undefined) => {
|
|
331
|
+
this._service.select(data, selected);
|
|
332
|
+
};
|
|
282
333
|
constructor() {
|
|
283
334
|
super();
|
|
284
335
|
this._id = this._service.generateComponentId();
|
|
285
336
|
const $data = toObservable(this.data);
|
|
337
|
+
fromEvent(this.element, 'focusin').pipe(takeUntilDestroyed(), tap(e => {
|
|
338
|
+
this.focus.set(true);
|
|
339
|
+
this.updateConfig(this._data);
|
|
340
|
+
this.updatePartStr(this._data, this._isSelected);
|
|
341
|
+
})).subscribe(),
|
|
342
|
+
fromEvent(this.element, 'focusout').pipe(takeUntilDestroyed(), tap(e => {
|
|
343
|
+
this.focus.set(false);
|
|
344
|
+
this.updateConfig(this._data);
|
|
345
|
+
this.updatePartStr(this._data, this._isSelected);
|
|
346
|
+
})).subscribe(),
|
|
347
|
+
fromEvent(this.element, 'keydown').pipe(takeUntilDestroyed(), tap(e => {
|
|
348
|
+
switch (e.key) {
|
|
349
|
+
case KEY_SPACE: {
|
|
350
|
+
e.stopImmediatePropagation();
|
|
351
|
+
e.preventDefault();
|
|
352
|
+
this._service.select(this._data);
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
case KEY_ARR_LEFT:
|
|
356
|
+
if (!this.config().isVertical) {
|
|
357
|
+
e.stopImmediatePropagation();
|
|
358
|
+
e.preventDefault();
|
|
359
|
+
this.focusPrev();
|
|
360
|
+
}
|
|
361
|
+
break;
|
|
362
|
+
case KEY_ARR_UP:
|
|
363
|
+
if (this.config().isVertical) {
|
|
364
|
+
e.stopImmediatePropagation();
|
|
365
|
+
e.preventDefault();
|
|
366
|
+
this.focusPrev();
|
|
367
|
+
}
|
|
368
|
+
break;
|
|
369
|
+
case KEY_ARR_RIGHT:
|
|
370
|
+
if (!this.config().isVertical) {
|
|
371
|
+
e.stopImmediatePropagation();
|
|
372
|
+
e.preventDefault();
|
|
373
|
+
this.focusNext();
|
|
374
|
+
}
|
|
375
|
+
break;
|
|
376
|
+
case KEY_ARR_DOWN:
|
|
377
|
+
if (this.config().isVertical) {
|
|
378
|
+
e.stopImmediatePropagation();
|
|
379
|
+
e.preventDefault();
|
|
380
|
+
this.focusNext();
|
|
381
|
+
}
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
})).subscribe();
|
|
286
385
|
combineLatest([$data, this._service.$methodOfSelecting, this._service.$selectedIds]).pipe(takeUntilDestroyed(), map(([, m, ids]) => ({ method: m, ids })), tap(({ method, ids }) => {
|
|
287
386
|
switch (method) {
|
|
288
387
|
case MethodsForSelectingTypes.SELECT: {
|
|
289
388
|
const id = ids, isSelected = id === this.itemId;
|
|
290
|
-
this.
|
|
389
|
+
this.element.setAttribute(ATTR_AREA_SELECTED, String(isSelected));
|
|
291
390
|
this._isSelected = isSelected;
|
|
292
391
|
break;
|
|
293
392
|
}
|
|
294
393
|
case MethodsForSelectingTypes.MULTI_SELECT: {
|
|
295
394
|
const actualIds = ids, isSelected = this.itemId !== undefined && actualIds && actualIds.includes(this.itemId);
|
|
296
|
-
this.
|
|
395
|
+
this.element.setAttribute(ATTR_AREA_SELECTED, String(isSelected));
|
|
297
396
|
this._isSelected = isSelected;
|
|
298
397
|
break;
|
|
299
398
|
}
|
|
300
399
|
case MethodsForSelectingTypes.NONE:
|
|
301
400
|
default: {
|
|
302
|
-
this.
|
|
401
|
+
this.element.removeAttribute(ATTR_AREA_SELECTED);
|
|
303
402
|
this._isSelected = false;
|
|
304
403
|
break;
|
|
305
404
|
}
|
|
306
405
|
}
|
|
307
406
|
this.updatePartStr(this._data, this._isSelected);
|
|
308
407
|
this.updateConfig(this._data);
|
|
408
|
+
this.updateMeasures(this._data);
|
|
309
409
|
})).subscribe();
|
|
310
410
|
}
|
|
411
|
+
focusNext() {
|
|
412
|
+
const tabIndex = this.config()?.tabIndex ?? 0;
|
|
413
|
+
if (this._service.listElement && tabIndex > 0) {
|
|
414
|
+
const el = this._service.listElement.querySelector(`[${TABINDEX}="${tabIndex + 1}"]`);
|
|
415
|
+
if (el) {
|
|
416
|
+
el.focus();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
focusPrev() {
|
|
421
|
+
const tabIndex = this.config()?.tabIndex ?? 0;
|
|
422
|
+
if (this._service.listElement && tabIndex > 1) {
|
|
423
|
+
const el = this._service.listElement.querySelector(`[${TABINDEX}="${tabIndex - 1}"]`);
|
|
424
|
+
if (el) {
|
|
425
|
+
el.focus();
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
updateMeasures(v) {
|
|
430
|
+
this.measures.set(v?.measures ? { ...v.measures } : undefined);
|
|
431
|
+
}
|
|
311
432
|
updateConfig(v) {
|
|
312
|
-
this.config.set({ ...v?.config || {}, selected: this._isSelected });
|
|
433
|
+
this.config.set({ ...v?.config || {}, selected: this._isSelected, select: this._selectHandler(v), focus: this.focus() });
|
|
313
434
|
}
|
|
314
435
|
update() {
|
|
315
436
|
const data = this._data, regular = this.regular, length = this._regularLength;
|
|
@@ -349,6 +470,9 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
|
|
|
349
470
|
if (isSelected) {
|
|
350
471
|
part += PART_ITEM_SELECTED;
|
|
351
472
|
}
|
|
473
|
+
if (this.focus()) {
|
|
474
|
+
part += PART_ITEM_FOCUSED;
|
|
475
|
+
}
|
|
352
476
|
this._part = part;
|
|
353
477
|
}
|
|
354
478
|
getBounds() {
|
|
@@ -393,14 +517,14 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
|
|
|
393
517
|
this._service.itemClick(this._data);
|
|
394
518
|
}
|
|
395
519
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
396
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: NgVirtualListItemComponent, isStandalone: true, selector: "ng-virtual-list-item", host: { attributes: { "role": "listitem" }, classAttribute: "ngvl__item" }, usesInheritance: true, ngImport: i0, template: "@let item = data();\r\n@let
|
|
520
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: NgVirtualListItemComponent, isStandalone: true, selector: "ng-virtual-list-item", host: { attributes: { "role": "listitem" }, classAttribute: "ngvl__item" }, usesInheritance: true, ngImport: i0, template: "@let item = data();\r\n@let _config = config();\r\n@let _measures = measures();\r\n@let renderer = itemRenderer();\r\n\r\n@if (item) {\r\n <div #listItem [part]=\"part\" [attr.index]=\"item.config.tabIndex\" tabindex=\"1\" class=\"ngvl-item__container\"\r\n [ngClass]=\"{'snapped': item.config.snapped, 'snapped-out': item.config.snappedOut, 'focus': focus()}\" (click)=\"onClickHandler()\">\r\n @if (renderer) {\r\n <ng-container [ngTemplateOutlet]=\"renderer\"\r\n [ngTemplateOutletContext]=\"{data: item.data, measures: _measures, config: _config}\" />\r\n }\r\n </div>\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;box-sizing:border-box}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
397
521
|
}
|
|
398
522
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListItemComponent, decorators: [{
|
|
399
523
|
type: Component,
|
|
400
524
|
args: [{ selector: 'ng-virtual-list-item', imports: [CommonModule], host: {
|
|
401
525
|
'class': 'ngvl__item',
|
|
402
526
|
'role': 'listitem',
|
|
403
|
-
}, changeDetection: ChangeDetectionStrategy.OnPush, template: "@let item = data();\r\n@let
|
|
527
|
+
}, changeDetection: ChangeDetectionStrategy.OnPush, template: "@let item = data();\r\n@let _config = config();\r\n@let _measures = measures();\r\n@let renderer = itemRenderer();\r\n\r\n@if (item) {\r\n <div #listItem [part]=\"part\" [attr.index]=\"item.config.tabIndex\" tabindex=\"1\" class=\"ngvl-item__container\"\r\n [ngClass]=\"{'snapped': item.config.snapped, 'snapped-out': item.config.snappedOut, 'focus': focus()}\" (click)=\"onClickHandler()\">\r\n @if (renderer) {\r\n <ng-container [ngTemplateOutlet]=\"renderer\"\r\n [ngTemplateOutletContext]=\"{data: item.data, measures: _measures, config: _config}\" />\r\n }\r\n </div>\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;box-sizing:border-box}\n"] }]
|
|
404
528
|
}], ctorParameters: () => [] });
|
|
405
529
|
|
|
406
530
|
/**
|
|
@@ -1496,7 +1620,7 @@ class TrackBox extends CacheMap {
|
|
|
1496
1620
|
const { width, height, normalizedItemWidth, normalizedItemHeight, dynamicSize, itemsOnDisplayLength, itemsFromStartToScrollEnd, isVertical, renderItems: renderItemsLength, scrollSize, sizeProperty, snap, snippedPos, startPosition, totalLength, startIndex, typicalItemSize, } = metrics, displayItems = [];
|
|
1497
1621
|
if (items.length) {
|
|
1498
1622
|
const actualSnippedPosition = snippedPos, isSnappingMethodAdvanced = this.isSnappingMethodAdvanced, boundsSize = isVertical ? height : width, actualEndSnippedPosition = boundsSize;
|
|
1499
|
-
let pos = startPosition, renderItems = renderItemsLength, stickyItem, nextSticky, stickyItemIndex = -1, stickyItemSize = 0, endStickyItem, nextEndSticky, endStickyItemIndex = -1, endStickyItemSize = 0;
|
|
1623
|
+
let pos = startPosition, renderItems = renderItemsLength, stickyItem, nextSticky, stickyItemIndex = -1, stickyItemSize = 0, endStickyItem, nextEndSticky, endStickyItemIndex = -1, endStickyItemSize = 0, count = 1;
|
|
1500
1624
|
if (snap) {
|
|
1501
1625
|
for (let i = Math.min(itemsFromStartToScrollEnd > 0 ? itemsFromStartToScrollEnd : 0, totalLength - 1); i >= 0; i--) {
|
|
1502
1626
|
if (!items[i]) {
|
|
@@ -1521,6 +1645,7 @@ class TrackBox extends CacheMap {
|
|
|
1521
1645
|
snappedOut: false,
|
|
1522
1646
|
dynamic: dynamicSize,
|
|
1523
1647
|
isSnappingMethodAdvanced,
|
|
1648
|
+
tabIndex: count,
|
|
1524
1649
|
zIndex: '1',
|
|
1525
1650
|
};
|
|
1526
1651
|
const itemData = items[i];
|
|
@@ -1528,6 +1653,7 @@ class TrackBox extends CacheMap {
|
|
|
1528
1653
|
stickyItemIndex = i;
|
|
1529
1654
|
stickyItemSize = size;
|
|
1530
1655
|
displayItems.push(stickyItem);
|
|
1656
|
+
count++;
|
|
1531
1657
|
break;
|
|
1532
1658
|
}
|
|
1533
1659
|
}
|
|
@@ -1559,6 +1685,7 @@ class TrackBox extends CacheMap {
|
|
|
1559
1685
|
snappedOut: false,
|
|
1560
1686
|
dynamic: dynamicSize,
|
|
1561
1687
|
isSnappingMethodAdvanced,
|
|
1688
|
+
tabIndex: count,
|
|
1562
1689
|
zIndex: '1',
|
|
1563
1690
|
};
|
|
1564
1691
|
const itemData = items[i];
|
|
@@ -1566,6 +1693,7 @@ class TrackBox extends CacheMap {
|
|
|
1566
1693
|
endStickyItemIndex = i;
|
|
1567
1694
|
endStickyItemSize = size;
|
|
1568
1695
|
displayItems.push(endStickyItem);
|
|
1696
|
+
count++;
|
|
1569
1697
|
break;
|
|
1570
1698
|
}
|
|
1571
1699
|
}
|
|
@@ -1597,6 +1725,7 @@ class TrackBox extends CacheMap {
|
|
|
1597
1725
|
snappedOut: false,
|
|
1598
1726
|
dynamic: dynamicSize,
|
|
1599
1727
|
isSnappingMethodAdvanced,
|
|
1728
|
+
tabIndex: count,
|
|
1600
1729
|
zIndex: '0',
|
|
1601
1730
|
};
|
|
1602
1731
|
if (snapped) {
|
|
@@ -1621,6 +1750,7 @@ class TrackBox extends CacheMap {
|
|
|
1621
1750
|
nextEndSticky.measures.delta = isVertical ? (item.measures.y - scrollSize) : (item.measures.x - scrollSize);
|
|
1622
1751
|
}
|
|
1623
1752
|
displayItems.push(item);
|
|
1753
|
+
count++;
|
|
1624
1754
|
}
|
|
1625
1755
|
renderItems -= 1;
|
|
1626
1756
|
pos += size;
|
|
@@ -1799,6 +1929,11 @@ class NgVirtualListComponent {
|
|
|
1799
1929
|
* Sets the selected items.
|
|
1800
1930
|
*/
|
|
1801
1931
|
selectedIds = input(undefined);
|
|
1932
|
+
/**
|
|
1933
|
+
* If false, the element is selected using the config.select method passed to the template;
|
|
1934
|
+
* if true, the element is selected by clicking on it. The default value is true.
|
|
1935
|
+
*/
|
|
1936
|
+
selectByClick = input(DEFAULT_SELECT_BY_CLICK);
|
|
1802
1937
|
/**
|
|
1803
1938
|
* Determines whether elements will snap. Default value is "true".
|
|
1804
1939
|
*/
|
|
@@ -1997,7 +2132,13 @@ class NgVirtualListComponent {
|
|
|
1997
2132
|
this._initialized = signal(false);
|
|
1998
2133
|
this.$initialized = toObservable(this._initialized);
|
|
1999
2134
|
this._trackBox.displayComponents = this._displayComponents;
|
|
2000
|
-
|
|
2135
|
+
toObservable(this._list).pipe(takeUntilDestroyed(), filter(v => !!v), tap(v => {
|
|
2136
|
+
this._service.listElement = v.nativeElement;
|
|
2137
|
+
})).subscribe();
|
|
2138
|
+
const $trackBy = toObservable(this.trackBy), $selectByClick = toObservable(this.selectByClick);
|
|
2139
|
+
$selectByClick.pipe(takeUntilDestroyed(), tap(v => {
|
|
2140
|
+
this._service.selectByClick = v;
|
|
2141
|
+
})).subscribe();
|
|
2001
2142
|
$trackBy.pipe(takeUntilDestroyed(), tap(v => {
|
|
2002
2143
|
this._trackBox.trackingPropertyName = v;
|
|
2003
2144
|
})).subscribe();
|
|
@@ -2079,10 +2220,19 @@ class NgVirtualListComponent {
|
|
|
2079
2220
|
this._service.$itemClick.pipe(takeUntilDestroyed(), tap(v => {
|
|
2080
2221
|
this.onItemClick.emit(v);
|
|
2081
2222
|
})).subscribe();
|
|
2082
|
-
|
|
2083
|
-
|
|
2223
|
+
let isSelectedIdsFirstEmit = 0;
|
|
2224
|
+
this._service.$selectedIds.pipe(takeUntilDestroyed(), distinctUntilChanged(), tap(v => {
|
|
2225
|
+
if (this.isSingleSelecting || (this.isMultiSelecting && isSelectedIdsFirstEmit >= 2)) {
|
|
2226
|
+
const curr = this.selectedIds();
|
|
2227
|
+
if ((this.isSingleSelecting && JSON.stringify(v) !== JSON.stringify(curr)) || (isSelectedIdsFirstEmit === 2 && JSON.stringify(v) !== JSON.stringify(curr)) || isSelectedIdsFirstEmit > 2) {
|
|
2228
|
+
this.onSelect.emit(v);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
if (isSelectedIdsFirstEmit < 3) {
|
|
2232
|
+
isSelectedIdsFirstEmit++;
|
|
2233
|
+
}
|
|
2084
2234
|
})).subscribe();
|
|
2085
|
-
$selectedIds.pipe(takeUntilDestroyed(), tap(v => {
|
|
2235
|
+
$selectedIds.pipe(takeUntilDestroyed(), distinctUntilChanged(), tap(v => {
|
|
2086
2236
|
this._service.setSelectedIds(v);
|
|
2087
2237
|
})).subscribe();
|
|
2088
2238
|
}
|
|
@@ -2346,7 +2496,7 @@ class NgVirtualListComponent {
|
|
|
2346
2496
|
}
|
|
2347
2497
|
}
|
|
2348
2498
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2349
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: NgVirtualListComponent, isStandalone: true, selector: "ng-virtual-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, selectedIds: { classPropertyName: "selectedIds", publicName: "selectedIds", isSignal: true, isRequired: false, transformFunction: null }, snap: { classPropertyName: "snap", publicName: "snap", isSignal: true, isRequired: false, transformFunction: null }, enabledBufferOptimization: { classPropertyName: "enabledBufferOptimization", publicName: "enabledBufferOptimization", isSignal: true, isRequired: false, transformFunction: null }, itemRenderer: { classPropertyName: "itemRenderer", publicName: "itemRenderer", isSignal: true, isRequired: true, transformFunction: null }, stickyMap: { classPropertyName: "stickyMap", publicName: "stickyMap", isSignal: true, isRequired: false, transformFunction: null }, itemConfigMap: { classPropertyName: "itemConfigMap", publicName: "itemConfigMap", 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 }, bufferSize: { classPropertyName: "bufferSize", publicName: "bufferSize", isSignal: true, isRequired: false, transformFunction: null }, maxBufferSize: { classPropertyName: "maxBufferSize", publicName: "maxBufferSize", isSignal: true, isRequired: false, transformFunction: null }, snappingMethod: { classPropertyName: "snappingMethod", publicName: "snappingMethod", isSignal: true, isRequired: false, transformFunction: null }, methodForSelecting: { classPropertyName: "methodForSelecting", publicName: "methodForSelecting", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd", onViewportChange: "onViewportChange", onItemClick: "onItemClick", onSelect: "onSelect" }, providers: [NgVirtualListService], viewQueries: [{ propertyName: "_snappedContainer", first: true, predicate: ["snapped"], descendants: true, isSignal: true }, { propertyName: "_container", first: true, predicate: ["container"], descendants: true, isSignal: true }, { propertyName: "_list", first: true, predicate: ["list"], descendants: true, isSignal: true }, { propertyName: "_listContainerRef", first: true, predicate: ["renderersContainer"], descendants: true, read: ViewContainerRef }, { propertyName: "_snapContainerRef", first: true, predicate: ["snapRendererContainer"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "@if (snap()) {\r\n <div #snapped part=\"snapped-item\" class=\"ngvl__list-snapper\">\r\n <ng-container #snapRendererContainer></ng-container>\r\n </div>\r\n}\r\n<div #container part=\"scroller\" class=\"ngvl__scroller\">\r\n <div [attr.aria-orientation]=\"orientation\" #list part=\"list\" class=\"ngvl__list\">\r\n <ng-container #renderersContainer></ng-container>\r\n </div>\r\n</div>", styles: [":host{position:relative;display:block;width:400px;overflow:hidden}:host(.horizontal){height:48px}:host(.horizontal) .ngvl__list{display:inline-flex}:host(.horizontal) .ngvl__scroller{overflow:auto hidden}:host(.vertical) .ngvl__scroller{overflow:hidden auto}:host(.vertical){height:320px}.ngvl__scroller{overflow:auto;width:100%;height:100%}.ngvl__list-snapper{pointer-events:none;position:absolute;list-style:none;left:0;top:0;z-index:1}.ngvl__list{position:relative;list-style:none;padding:0;margin:0;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
2499
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: NgVirtualListComponent, isStandalone: true, selector: "ng-virtual-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, selectedIds: { classPropertyName: "selectedIds", publicName: "selectedIds", isSignal: true, isRequired: false, transformFunction: null }, selectByClick: { classPropertyName: "selectByClick", publicName: "selectByClick", isSignal: true, isRequired: false, transformFunction: null }, snap: { classPropertyName: "snap", publicName: "snap", isSignal: true, isRequired: false, transformFunction: null }, enabledBufferOptimization: { classPropertyName: "enabledBufferOptimization", publicName: "enabledBufferOptimization", isSignal: true, isRequired: false, transformFunction: null }, itemRenderer: { classPropertyName: "itemRenderer", publicName: "itemRenderer", isSignal: true, isRequired: true, transformFunction: null }, stickyMap: { classPropertyName: "stickyMap", publicName: "stickyMap", isSignal: true, isRequired: false, transformFunction: null }, itemConfigMap: { classPropertyName: "itemConfigMap", publicName: "itemConfigMap", 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 }, bufferSize: { classPropertyName: "bufferSize", publicName: "bufferSize", isSignal: true, isRequired: false, transformFunction: null }, maxBufferSize: { classPropertyName: "maxBufferSize", publicName: "maxBufferSize", isSignal: true, isRequired: false, transformFunction: null }, snappingMethod: { classPropertyName: "snappingMethod", publicName: "snappingMethod", isSignal: true, isRequired: false, transformFunction: null }, methodForSelecting: { classPropertyName: "methodForSelecting", publicName: "methodForSelecting", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd", onViewportChange: "onViewportChange", onItemClick: "onItemClick", onSelect: "onSelect" }, providers: [NgVirtualListService], viewQueries: [{ propertyName: "_snappedContainer", first: true, predicate: ["snapped"], descendants: true, isSignal: true }, { propertyName: "_container", first: true, predicate: ["container"], descendants: true, isSignal: true }, { propertyName: "_list", first: true, predicate: ["list"], descendants: true, isSignal: true }, { propertyName: "_listContainerRef", first: true, predicate: ["renderersContainer"], descendants: true, read: ViewContainerRef }, { propertyName: "_snapContainerRef", first: true, predicate: ["snapRendererContainer"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "@if (snap()) {\r\n <div #snapped part=\"snapped-item\" class=\"ngvl__list-snapper\">\r\n <ng-container #snapRendererContainer></ng-container>\r\n </div>\r\n}\r\n<div #container part=\"scroller\" class=\"ngvl__scroller\">\r\n <div [attr.aria-orientation]=\"orientation\" #list part=\"list\" class=\"ngvl__list\">\r\n <ng-container #renderersContainer></ng-container>\r\n </div>\r\n</div>", styles: [":host{position:relative;display:block;width:400px;overflow:hidden}:host(.horizontal){height:48px}:host(.horizontal) .ngvl__list{display:inline-flex}:host(.horizontal) .ngvl__scroller{overflow:auto hidden}:host(.vertical) .ngvl__scroller{overflow:hidden auto}:host(.vertical){height:320px}.ngvl__scroller{overflow:auto;width:100%;height:100%}.ngvl__list-snapper{pointer-events:none;position:absolute;list-style:none;left:0;top:0;z-index:1}.ngvl__list{position:relative;list-style:none;padding:0;margin:0;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
|
|
2350
2500
|
}
|
|
2351
2501
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListComponent, decorators: [{
|
|
2352
2502
|
type: Component,
|