ng-virtual-list 19.7.21 → 19.7.23

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 CHANGED
@@ -115,7 +115,7 @@ items = Array.from({ length: 100000 }, (_, i) => ({ id: i, name: `Item #${i}` })
115
115
 
116
116
  Template:
117
117
  ```html
118
- <ng-virtual-list class="list" direction="horizontal" [items]="horizontalItems" [bufferSize]="50"
118
+ <ng-virtual-list class="list" direction="horizontal" [items]="horizontalItems" [bufferSize]="5" [maxBufferSize]="20"
119
119
  [itemRenderer]="horizontalItemRenderer" [itemSize]="64" [methodForSelecting]="'select'"
120
120
  [selectedIds]="2" (onSelect)="onSelect($event)" (onItemClick)="onItemClick($event)"></ng-virtual-list>
121
121
 
@@ -166,8 +166,8 @@ export class AppComponent {
166
166
  Template:
167
167
  ```html
168
168
  <ng-virtual-list class="list" direction="horizontal" [items]="horizontalGroupItems" [itemRenderer]="horizontalGroupItemRenderer"
169
- [bufferSize]="50" [itemConfigMap]="horizontalGroupItemConfigMap" [itemSize]="54" [snap]="true" [methodForSelecting]="'multi-select'"
170
- [selectedIds]="[3,2]" (onSelect)="onSelect($event)" (onItemClick)="onItemClick($event)"></ng-virtual-list>
169
+ [bufferSize]="5" [maxBufferSize]="20" [itemConfigMap]="horizontalGroupItemConfigMap" [itemSize]="54" [snap]="true"
170
+ methodForSelecting="multi-select" [selectedIds]="[3,2]" (onSelect)="onSelect($event)" (onItemClick)="onItemClick($event)"></ng-virtual-list>
171
171
 
172
172
  <ng-template #horizontalGroupItemRenderer let-data="data" let-config="config">
173
173
  @if (data) {
@@ -239,7 +239,7 @@ export class AppComponent {
239
239
 
240
240
  Template:
241
241
  ```html
242
- <ng-virtual-list class="list simple" [items]="items" [bufferSize]="50" [itemRenderer]="itemRenderer"
242
+ <ng-virtual-list class="list simple" [items]="items" [bufferSize]="5" [maxBufferSize]="20" [itemRenderer]="itemRenderer"
243
243
  [itemSize]="40"></ng-virtual-list>
244
244
 
245
245
  <ng-template #itemRenderer let-data="data">
@@ -280,7 +280,7 @@ export class AppComponent {
280
280
 
281
281
  Template:
282
282
  ```html
283
- <ng-virtual-list class="list simple" [items]="groupItems" [bufferSize]="50" [itemRenderer]="groupItemRenderer"
283
+ <ng-virtual-list class="list simple" [items]="groupItems" [bufferSize]="5" [maxBufferSize]="20" [itemRenderer]="groupItemRenderer"
284
284
  [itemConfigMap]="groupItemConfigMap" [itemSize]="40" [snap]="false"></ng-virtual-list>
285
285
 
286
286
  <ng-template #groupItemRenderer let-data="data">
@@ -307,7 +307,7 @@ Template:
307
307
 
308
308
  Template (with snapping):
309
309
  ```html
310
- <ng-virtual-list class="list simple" [items]="groupItems" [bufferSize]="50" [itemRenderer]="groupItemRenderer"
310
+ <ng-virtual-list class="list simple" [items]="groupItems" [bufferSize]="5" [maxBufferSize]="20" [itemRenderer]="groupItemRenderer"
311
311
  [itemConfigMap]="groupItemConfigMap" [itemSize]="40" [snap]="true"></ng-virtual-list>
312
312
 
313
313
  <ng-template #groupItemRenderer let-data="data">
@@ -372,7 +372,7 @@ Template
372
372
  <button class="scroll-to__button" (click)="onButtonScrollToIdClickHandler($event)">Scroll</button>
373
373
  </div>
374
374
 
375
- <ng-virtual-list #virtualList class="list" [items]="items" [itemRenderer]="itemRenderer" [bufferSize]="50"
375
+ <ng-virtual-list #virtualList class="list" [items]="items" [itemRenderer]="itemRenderer" [bufferSize]="5" [maxBufferSize]="20"
376
376
  [itemSize]="40"></ng-virtual-list>
377
377
 
378
378
  <ng-template #itemRenderer let-data="data">
@@ -426,7 +426,7 @@ Virtual list with height-adjustable elements.
426
426
 
427
427
  Template
428
428
  ```html
429
- <ng-virtual-list #dynamicList class="list" [items]="groupDynamicItems" [itemRenderer]="groupItemRenderer" [bufferSize]="10"
429
+ <ng-virtual-list #dynamicList class="list" [items]="groupDynamicItems" [itemRenderer]="groupItemRenderer" [bufferSize]="5" [maxBufferSize]="20"
430
430
  [itemConfigMap]="groupDynamicItemConfigMap" [dynamicSize]="true" [snap]="true"></ng-virtual-list>
431
431
 
432
432
  <ng-template #groupItemRenderer let-data="data">
@@ -565,7 +565,7 @@ List items are encapsulated in shadowDOM, so to override default styles you need
565
565
  Selecting even elements:
566
566
 
567
567
  ```html
568
- <ng-virtual-list class="list" direction="horizontal" [items]="horizontalItems" [bufferSize]="5"
568
+ <ng-virtual-list class="list" direction="horizontal" [items]="horizontalItems" [bufferSize]="5" [maxBufferSize]="20"
569
569
  [itemRenderer]="horizontalItemRenderer" [itemSize]="54"></ng-virtual-list>
570
570
 
571
571
  <ng-template #horizontalItemRenderer let-data="data" let-config="config">
@@ -589,7 +589,7 @@ Selecting even elements:
589
589
 
590
590
  ## 📚 API
591
591
 
592
- [NgVirtualListComponent](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/ng-virtual-list.component.ts)
592
+ ### [NgVirtualListComponent](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/ng-virtual-list.component.ts)
593
593
 
594
594
  Inputs
595
595
 
@@ -630,9 +630,6 @@ Outputs
630
630
  | onScrollReachStart | void | Fires when the scroll reaches the start. |
631
631
  | onScrollReachEnd | void | Fires when the scroll reaches the end. |
632
632
 
633
-
634
- <br/>
635
-
636
633
  Methods
637
634
 
638
635
  | Method | Type | Description |
@@ -640,6 +637,25 @@ Methods
640
637
  | scrollTo | (id: [Id](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/types/id.ts), behavior: ScrollBehavior = 'auto') => number | The method scrolls the list to the element with the given id and returns the value of the scrolled area. Behavior accepts the values ​​"auto", "instant" and "smooth". |
641
638
  | scrollToEnd | (behavior?: ScrollBehavior) => void | Scrolls the scroll area to the desired element with the specified ID. |
642
639
  | getItemBounds | (id: [Id](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/types/id.ts), behavior?: ScrollBehavior) => void | Returns the bounds of an element with a given id |
640
+ | focus | [Id](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/types/id.ts), align: [FocusAlignment](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/types/focus-alignment.ts) = [FocusAlignments.NONE](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/enums/focus-alignments.ts) | Focus an list item by a given id. |
641
+
642
+ <br/>
643
+
644
+ ### Template API
645
+
646
+ ```html
647
+ <ng-template #itemRenderer let-data="data" let-config="config" let-measures="measures">
648
+ <!-- content -->
649
+ </ng-template>
650
+ ```
651
+
652
+ Properties
653
+
654
+ | Property | Type | Description |
655
+ |--|--|--|
656
+ | data | {\[id: [Id](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/types/id.ts) \], [otherProps: string]: any;} | Collection item data. |
657
+ | config | [IDisplayObjectConfig](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/models/display-object-config.model.ts) | Display object configuration. A set of `select`, `collapse`, and `focus` methods are also provided. |
658
+ | measures | [IDisplayObjectMeasures](https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/models/display-object-measures.model.ts) \| undefined | Display object metrics. |
643
659
 
644
660
  <br/>
645
661
 
@@ -2,9 +2,9 @@ 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, fromEvent, combineLatest, map, filter, distinctUntilChanged, switchMap, of } from 'rxjs';
6
- import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
7
5
  import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
6
+ import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
7
+ import { Subject, tap, fromEvent, combineLatest, map, filter, distinctUntilChanged, switchMap, of } from 'rxjs';
8
8
 
9
9
  /**
10
10
  * Action modes for collection elements.
@@ -82,6 +82,20 @@ var SnappingMethods;
82
82
  SnappingMethods["ADVANCED"] = "advanced";
83
83
  })(SnappingMethods || (SnappingMethods = {}));
84
84
 
85
+ /**
86
+ * Focus Alignments.
87
+ * @link https://github.com/DjonnyX/ng-virtual-list/blob/19.x/projects/ng-virtual-list/src/lib/enums/focus-alignments.ts
88
+ * @author Evgenii Grebennikov
89
+ * @email djonnyx@gmail.com
90
+ */
91
+ var FocusAlignments;
92
+ (function (FocusAlignments) {
93
+ FocusAlignments["NONE"] = "none";
94
+ FocusAlignments["START"] = "start";
95
+ FocusAlignments["CENTER"] = "center";
96
+ FocusAlignments["END"] = "end";
97
+ })(FocusAlignments || (FocusAlignments = {}));
98
+
85
99
  const DEFAULT_ITEM_SIZE = 24;
86
100
  const DEFAULT_BUFFER_SIZE = 2;
87
101
  const DEFAULT_MAX_BUFFER_SIZE = 10;
@@ -327,11 +341,11 @@ class NgVirtualListService {
327
341
  }
328
342
  }
329
343
  itemToFocus;
330
- focus(element) {
344
+ focus(element, align = FocusAlignments.CENTER) {
331
345
  element.focus({ preventScroll: true });
332
346
  if (element.parentElement) {
333
347
  const pos = parseFloat(element.parentElement?.getAttribute('position') ?? '0');
334
- this.itemToFocus?.(element, pos);
348
+ this.itemToFocus?.(element, pos, align);
335
349
  }
336
350
  }
337
351
  areaFocus(id) {
@@ -410,7 +424,7 @@ const validateFunction = (value, undefinable = false, nullable = false) => {
410
424
  return (undefinable && isUndefinable(value)) || (nullable && isNullable(value)) || typeof value === 'function';
411
425
  };
412
426
 
413
- const ATTR_AREA_SELECTED = 'area-selected', TABINDEX = 'ng-vl-index', KEY_SPACE = " ", KEY_ARR_LEFT = "ArrowLeft", KEY_ARR_UP = "ArrowUp", KEY_ARR_RIGHT = "ArrowRight", KEY_ARR_DOWN = "ArrowDown", EVENT_FOCUS_IN = 'focusin', EVENT_FOCUS_OUT = 'focusout', EVENT_KEY_DOWN = 'keydown';
427
+ const ATTR_AREA_SELECTED = 'area-selected', TABINDEX = 'ng-vl-index', POSITION = 'position', POSITION_ZERO = '0', ID = 'item-id', KEY_SPACE = " ", KEY_ARR_LEFT = "ArrowLeft", KEY_ARR_UP = "ArrowUp", KEY_ARR_RIGHT = "ArrowRight", KEY_ARR_DOWN = "ArrowDown", EVENT_FOCUS_IN = 'focusin', EVENT_FOCUS_OUT = 'focusout', EVENT_KEY_DOWN = 'keydown';
414
428
  const getElementByIndex = (index) => {
415
429
  return `[${TABINDEX}="${index}"]`;
416
430
  };
@@ -430,7 +444,7 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
430
444
  _isCollapsed = false;
431
445
  config = signal({});
432
446
  measures = signal(undefined);
433
- focus = signal(false);
447
+ focused = signal(false);
434
448
  part = signal(PART_DEFAULT_ITEM);
435
449
  regular = false;
436
450
  data = signal(undefined);
@@ -494,21 +508,28 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
494
508
  }
495
509
  this._service.collapse(data, collapsed);
496
510
  };
511
+ _focusHandler = () =>
512
+ /**
513
+ * Focus a list item
514
+ */
515
+ (align = FocusAlignments.CENTER) => {
516
+ this.focus(align);
517
+ };
497
518
  constructor() {
498
519
  super();
499
520
  this._id = this._service.generateComponentId();
500
521
  this._elementRef.nativeElement.setAttribute('id', String(this._id));
501
- const $data = toObservable(this.data), $focus = toObservable(this.focus);
502
- $focus.pipe(takeUntilDestroyed(), tap(v => {
522
+ const $data = toObservable(this.data), $focused = toObservable(this.focused);
523
+ $focused.pipe(takeUntilDestroyed(), tap(v => {
503
524
  this._service.areaFocus(v ? this._id : this._service.focusedId === this._id ? null : this._service.focusedId);
504
525
  })).subscribe();
505
526
  fromEvent(this.element, EVENT_FOCUS_IN).pipe(takeUntilDestroyed(), tap(e => {
506
- this.focus.set(true);
527
+ this.focused.set(true);
507
528
  this.updateConfig(this._data);
508
529
  this.updatePartStr(this._data, this._isSelected, this._isCollapsed);
509
530
  })).subscribe(),
510
531
  fromEvent(this.element, EVENT_FOCUS_OUT).pipe(takeUntilDestroyed(), tap(e => {
511
- this.focus.set(false);
532
+ this.focused.set(false);
512
533
  this.updateConfig(this._data);
513
534
  this.updatePartStr(this._data, this._isSelected, this._isCollapsed);
514
535
  })).subscribe(),
@@ -611,23 +632,34 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
611
632
  }
612
633
  }
613
634
  }
635
+ focus(align = FocusAlignments.CENTER) {
636
+ if (this._service.listElement) {
637
+ const tabIndex = this._data?.config?.tabIndex ?? 0;
638
+ let index = tabIndex;
639
+ const el = this._service.listElement.querySelector(getElementByIndex(index));
640
+ if (el) {
641
+ this._service.focus(el, align);
642
+ }
643
+ }
644
+ }
614
645
  updateMeasures(v) {
615
646
  this.measures.set(v?.measures ? { ...v.measures } : undefined);
616
647
  }
617
648
  updateConfig(v) {
618
649
  this.config.set({
619
- ...v?.config || {}, selected: this._isSelected, collapsed: this._isCollapsed, focus: this.focus(),
620
- collapse: this._collapseHandler(v), select: this._selectHandler(v)
650
+ ...v?.config || {}, selected: this._isSelected, collapsed: this._isCollapsed, focused: this.focused(),
651
+ collapse: this._collapseHandler(v), select: this._selectHandler(v), focus: this._focusHandler(),
621
652
  });
622
653
  }
623
654
  update() {
624
655
  const data = this._data, regular = this.regular, length = this._regularLength;
625
656
  if (data) {
657
+ this._elementRef.nativeElement.setAttribute(ID, `${data.id}`);
626
658
  const styles = this._elementRef.nativeElement.style;
627
659
  styles.zIndex = data.config.zIndex;
628
660
  if (data.config.snapped) {
629
- this._elementRef.nativeElement.setAttribute('position', data.config.sticky === 1 ? '0' : `${data.config.isVertical ? data.measures.y : data.measures.x}`);
630
- styles.transform = data.config.sticky === 1 ? ZEROS_TRANSLATE_3D : `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.x}${PX}, ${data.config.isVertical ? data.measures.y : 0}${PX} , 0)`;
661
+ this._elementRef.nativeElement.setAttribute(POSITION, data.config.sticky === 1 ? POSITION_ZERO : `${data.config.isVertical ? data.measures.y : data.measures.x}`);
662
+ styles.transform = data.config.sticky === 1 ? ZEROS_TRANSLATE_3D : `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.x}${PX}, ${data.config.isVertical ? data.measures.y : 0}${PX}, ${POSITION_ZERO})`;
631
663
  ;
632
664
  if (!data.config.isSnappingMethodAdvanced) {
633
665
  styles.position = POSITION_STICKY;
@@ -636,17 +668,20 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
636
668
  else {
637
669
  styles.position = POSITION_ABSOLUTE;
638
670
  if (regular) {
639
- this._elementRef.nativeElement.setAttribute('position', '0');
640
- styles.transform = `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.delta}${PX}, ${data.config.isVertical ? data.measures.delta : 0}${PX} , 0)`;
671
+ this._elementRef.nativeElement.setAttribute(POSITION, POSITION_ZERO);
672
+ styles.transform = `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.delta}${PX}, ${data.config.isVertical ? data.measures.delta : 0}${PX}, ${POSITION_ZERO})`;
641
673
  }
642
674
  else {
643
- this._elementRef.nativeElement.setAttribute('position', `${data.config.isVertical ? data.measures.y : data.measures.x}`);
644
- styles.transform = `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.x}${PX}, ${data.config.isVertical ? data.measures.y : 0}${PX} , 0)`;
675
+ this._elementRef.nativeElement.setAttribute(POSITION, `${data.config.isVertical ? data.measures.y : data.measures.x}`);
676
+ styles.transform = `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.x}${PX}, ${data.config.isVertical ? data.measures.y : 0}${PX}, ${POSITION_ZERO})`;
645
677
  }
646
678
  }
647
679
  styles.height = data.config.isVertical ? data.config.dynamic ? SIZE_AUTO : `${data.measures.height}${PX}` : regular ? length : SIZE_100_PERSENT;
648
680
  styles.width = data.config.isVertical ? regular ? length : SIZE_100_PERSENT : data.config.dynamic ? SIZE_AUTO : `${data.measures.width}${PX}`;
649
681
  }
682
+ else {
683
+ this._elementRef.nativeElement.removeAttribute(ID);
684
+ }
650
685
  }
651
686
  updatePartStr(v, isSelected, isCollapsed) {
652
687
  let odd = false;
@@ -667,7 +702,7 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
667
702
  if (v ? v.config.new : false) {
668
703
  part += PART_ITEM_NEW;
669
704
  }
670
- if (this.focus()) {
705
+ if (this.focused()) {
671
706
  part += PART_ITEM_FOCUSED;
672
707
  }
673
708
  this.part.set(part);
@@ -714,14 +749,14 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
714
749
  this._service.itemClick(this._data);
715
750
  }
716
751
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
717
- 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 _part = part();\r\n@let _measures = measures();\r\n@let renderer = itemRenderer();\r\n\r\n@if (item) {\r\n <div #listItem [part]=\"_part\" [attr.ng-vl-index]=\"_config.tabIndex || -1\" tabindex=\"0\" 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 });
752
+ 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 _part = part();\r\n@let _measures = measures();\r\n@let renderer = itemRenderer();\r\n\r\n@if (item) {\r\n <div #listItem [part]=\"_part\" [attr.ng-vl-index]=\"_config.tabIndex || -1\" tabindex=\"0\" class=\"ngvl-item__container\"\r\n [ngClass]=\"{'snapped': item.config.snapped, 'snapped-out': item.config.snappedOut, 'focus': focused()}\" (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 });
718
753
  }
719
754
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgVirtualListItemComponent, decorators: [{
720
755
  type: Component,
721
756
  args: [{ selector: 'ng-virtual-list-item', imports: [CommonModule], host: {
722
757
  'class': 'ngvl__item',
723
758
  'role': 'listitem',
724
- }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@let item = data();\r\n@let _config = config();\r\n@let _part = part();\r\n@let _measures = measures();\r\n@let renderer = itemRenderer();\r\n\r\n@if (item) {\r\n <div #listItem [part]=\"_part\" [attr.ng-vl-index]=\"_config.tabIndex || -1\" tabindex=\"0\" 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"] }]
759
+ }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@let item = data();\r\n@let _config = config();\r\n@let _part = part();\r\n@let _measures = measures();\r\n@let renderer = itemRenderer();\r\n\r\n@if (item) {\r\n <div #listItem [part]=\"_part\" [attr.ng-vl-index]=\"_config.tabIndex || -1\" tabindex=\"0\" class=\"ngvl-item__container\"\r\n [ngClass]=\"{'snapped': item.config.snapped, 'snapped-out': item.config.snappedOut, 'focus': focused()}\" (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"] }]
725
760
  }], ctorParameters: () => [] });
726
761
 
727
762
  /**
@@ -2161,7 +2196,7 @@ const isCollectionMode = (src, expected) => {
2161
2196
  return NORMAL_ALIASES.includes(src);
2162
2197
  };
2163
2198
 
2164
- const ROLE_LIST = 'list', ROLE_LIST_BOX = 'listbox';
2199
+ const ROLE_LIST = 'list', ROLE_LIST_BOX = 'listbox', ITEM_ID = 'item-id', ITEM_CONTAINER = 'ngvl-item__container';
2165
2200
  const validateScrollIteration = (value) => {
2166
2201
  return Number.isNaN(value) || (value < 0) ? 0 : value > MAX_SCROLL_TO_ITERATIONS ? MAX_SCROLL_TO_ITERATIONS : value;
2167
2202
  }, validateId = (id) => {
@@ -2179,6 +2214,11 @@ const validateScrollIteration = (value) => {
2179
2214
  if (!valid) {
2180
2215
  throw Error('The "iteration" parameter must be of type `number`.');
2181
2216
  }
2217
+ }, validateFocusAlignment = (align) => {
2218
+ const valid = validateString(align) && (align === 'none' || align === 'start' || align === 'center' || align === 'end');
2219
+ if (!valid) {
2220
+ throw Error('The "align" parameter must have the value `none`, `start`, `center` or `end`.');
2221
+ }
2182
2222
  };
2183
2223
  /**
2184
2224
  * Virtual list component.
@@ -2643,12 +2683,33 @@ class NgVirtualListComponent {
2643
2683
  this._scrollSize.set(actualScrollSize);
2644
2684
  }
2645
2685
  };
2646
- itemToFocus = (element, position) => {
2686
+ itemToFocus = (element, position, align = FocusAlignments.CENTER) => {
2647
2687
  const container = this._container()?.nativeElement;
2648
2688
  if (container) {
2649
- const { width, height } = this._bounds(), { width: elementWidth, height: elementHeight } = element.getBoundingClientRect(), isVertical = this._isVertical, pos = isVertical ? position - (height - elementHeight) * .5 : position - (width - elementWidth) * .5;
2650
- const params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: pos, behavior: 'instant' };
2651
- container.scrollTo(params);
2689
+ const { width, height } = this._bounds(), { width: elementWidth, height: elementHeight } = element.getBoundingClientRect(), isVertical = this._isVertical;
2690
+ let pos = Number.NaN;
2691
+ switch (align) {
2692
+ case FocusAlignments.START: {
2693
+ pos = isVertical ? position : position;
2694
+ break;
2695
+ }
2696
+ case FocusAlignments.CENTER: {
2697
+ pos = isVertical ? position - (height - elementHeight) * .5 : position - (width - elementWidth) * .5;
2698
+ break;
2699
+ }
2700
+ case FocusAlignments.END: {
2701
+ pos = isVertical ? position - (height - elementHeight) : position - (width - elementWidth);
2702
+ break;
2703
+ }
2704
+ case FocusAlignments.NONE:
2705
+ default: {
2706
+ break;
2707
+ }
2708
+ }
2709
+ if (!Number.isNaN(pos)) {
2710
+ const params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: pos, behavior: 'instant' };
2711
+ container.scrollTo(params);
2712
+ }
2652
2713
  }
2653
2714
  };
2654
2715
  _elementRef = inject((ElementRef));
@@ -2974,6 +3035,20 @@ class NgVirtualListComponent {
2974
3035
  validateId(id);
2975
3036
  return this._trackBox.getItemBounds(id);
2976
3037
  }
3038
+ /**
3039
+ * Focus an list item by a given id.
3040
+ */
3041
+ focus(id, align = FocusAlignments.NONE) {
3042
+ validateId(id);
3043
+ validateFocusAlignment(align);
3044
+ const el = this._list()?.nativeElement.querySelector(`[${ITEM_ID}="${id}"]`);
3045
+ if (el) {
3046
+ const focusedEl = el.querySelector(`.${ITEM_CONTAINER}`);
3047
+ if (focusedEl) {
3048
+ this._service.focus(focusedEl, align);
3049
+ }
3050
+ }
3051
+ }
2977
3052
  /**
2978
3053
  * The method scrolls the list to the element with the given id and returns the value of the scrolled area.
2979
3054
  * Behavior accepts the values ​​"auto", "instant" and "smooth".
@@ -3158,5 +3233,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
3158
3233
  * Generated bundle index. Do not edit.
3159
3234
  */
3160
3235
 
3161
- export { CollectionModes, Directions, MethodsForSelecting, NgVirtualListComponent, NgVirtualListItemComponent, ScrollEvent, SnappingMethods, debounce, toggleClassName };
3236
+ export { CollectionModes, Directions, FocusAlignments, MethodsForSelecting, NgVirtualListComponent, NgVirtualListItemComponent, ScrollEvent, SnappingMethods, debounce, toggleClassName };
3162
3237
  //# sourceMappingURL=ng-virtual-list.mjs.map