ng-virtual-list 20.7.13 → 20.7.14

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
@@ -589,7 +589,7 @@ Selecting even elements:
589
589
 
590
590
  ## 📚 API
591
591
 
592
- [NgVirtualListComponent](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/ng-virtual-list.component.ts)
592
+ ### [NgVirtualListComponent](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/ng-virtual-list.component.ts)
593
593
 
594
594
  Inputs
595
595
 
@@ -640,6 +640,25 @@ Methods
640
640
  | scrollTo | (id: [Id](https://github.com/DjonnyX/ng-virtual-list/blob/20.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
641
  | scrollToEnd | (behavior?: ScrollBehavior) => void | Scrolls the scroll area to the desired element with the specified ID. |
642
642
  | getItemBounds | (id: [Id](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/types/id.ts), behavior?: ScrollBehavior) => void | Returns the bounds of an element with a given id |
643
+ | focus | [Id](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/types/id.ts), align: [FocusAlignment](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/types/focus-alignment.ts) = [FocusAlignments.NONE](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/enums/focus-alignments.ts) | Focus an list item by a given id. |
644
+
645
+ <br/>
646
+
647
+ ### Template API
648
+
649
+ ```html
650
+ <ng-template #itemRenderer let-data="data" let-config="config" let-measures="measures">
651
+ <!-- content -->
652
+ </ng-template>
653
+ ```
654
+
655
+ Properties
656
+
657
+ | Property | Type | Description |
658
+ |--|--|--|
659
+ | data | {\[id: [Id](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/types/id.ts) \], [otherProps: string]: any;} | Collection item data. |
660
+ | config | [IDisplayObjectConfig](https://github.com/DjonnyX/ng-virtual-list/blob/20.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. |
661
+ | measures | [IDisplayObjectMeasures](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/models/display-object-measures.model.ts) \| undefined | Display object metrics. |
643
662
 
644
663
  <br/>
645
664
 
@@ -647,12 +666,12 @@ Methods
647
666
 
648
667
  | Angular version | ng-virtual-list version | git | npm |
649
668
  |--|--|--|--|
650
- | 19.x | 19.7.22 | [19.x](https://github.com/DjonnyX/ng-virtual-list/tree/19.x) | [19.7.22](https://www.npmjs.com/package/ng-virtual-list/v/19.7.22) |
651
- | 18.x | 18.7.13 | [18.x](https://github.com/DjonnyX/ng-virtual-list/tree/18.x) | [18.7.13](https://www.npmjs.com/package/ng-virtual-list/v/18.7.13) |
652
- | 17.x | 17.7.14 | [17.x](https://github.com/DjonnyX/ng-virtual-list/tree/17.x) | [17.7.14](https://www.npmjs.com/package/ng-virtual-list/v/17.7.14) |
653
- | 16.x | 16.7.12 | [16.x](https://github.com/DjonnyX/ng-virtual-list/tree/16.x) | [16.7.12](https://www.npmjs.com/package/ng-virtual-list/v/16.7.12) |
654
- | 15.x | 15.7.12 | [15.x](https://github.com/DjonnyX/ng-virtual-list/tree/15.x) | [15.7.12](https://www.npmjs.com/package/ng-virtual-list/v/15.7.12) |
655
- | 14.x | 14.7.13 | [14.x](https://github.com/DjonnyX/ng-virtual-list/tree/14.x) | [14.7.13](https://www.npmjs.com/package/ng-virtual-list/v/14.7.13) |
669
+ | 19.x | 19.7.23 | [19.x](https://github.com/DjonnyX/ng-virtual-list/tree/19.x) | [19.7.23](https://www.npmjs.com/package/ng-virtual-list/v/19.7.23) |
670
+ | 18.x | 18.7.14 | [18.x](https://github.com/DjonnyX/ng-virtual-list/tree/18.x) | [18.7.14](https://www.npmjs.com/package/ng-virtual-list/v/18.7.14) |
671
+ | 17.x | 17.7.15 | [17.x](https://github.com/DjonnyX/ng-virtual-list/tree/17.x) | [17.7.15](https://www.npmjs.com/package/ng-virtual-list/v/17.7.15) |
672
+ | 16.x | 16.7.13 | [16.x](https://github.com/DjonnyX/ng-virtual-list/tree/16.x) | [16.7.13](https://www.npmjs.com/package/ng-virtual-list/v/16.7.13) |
673
+ | 15.x | 15.7.13 | [15.x](https://github.com/DjonnyX/ng-virtual-list/tree/15.x) | [15.7.13](https://www.npmjs.com/package/ng-virtual-list/v/15.7.13) |
674
+ | 14.x | 14.7.14 | [14.x](https://github.com/DjonnyX/ng-virtual-list/tree/14.x) | [14.7.14](https://www.npmjs.com/package/ng-virtual-list/v/14.7.14) |
656
675
 
657
676
  <br/>
658
677
 
@@ -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/20.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: "20.0.4", ngImport: i0, type: NgVirtualListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
717
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", 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: "20.0.4", 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: "20.0.4", 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: "20.0.4", ngImpor
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