ng-virtual-list 20.7.13 → 20.7.15

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
 
@@ -614,6 +614,7 @@ Inputs
614
614
  | trackBy | string? = 'id' | The name of the property by which tracking is performed. |
615
615
  | selectedIds | Array<[Id](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/types/id.ts)> \| [Id](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/types/id.ts) \| undefined | Sets the selected items. |
616
616
  | collapsedIds | Array<[Id](https://github.com/DjonnyX/ng-virtual-list/blob/20.x/projects/ng-virtual-list/src/lib/types/id.ts)> | Sets the collapsed items. |
617
+ | screenReaderMessage | string? = "Showing items $1 to $2" | Message for screen reader. The message format is: "some text `$1` some text `$2`", where `$1` is the number of the first element of the screen collection, `$2` is the number of the last element of the screen collection. |
617
618
 
618
619
  <br/>
619
620
 
@@ -640,6 +641,25 @@ Methods
640
641
  | 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
642
  | scrollToEnd | (behavior?: ScrollBehavior) => void | Scrolls the scroll area to the desired element with the specified ID. |
642
643
  | 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 |
644
+ | 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. |
645
+
646
+ <br/>
647
+
648
+ ### Template API
649
+
650
+ ```html
651
+ <ng-template #itemRenderer let-data="data" let-config="config" let-measures="measures">
652
+ <!-- content -->
653
+ </ng-template>
654
+ ```
655
+
656
+ Properties
657
+
658
+ | Property | Type | Description |
659
+ |--|--|--|
660
+ | 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. |
661
+ | 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. |
662
+ | 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
663
 
644
664
  <br/>
645
665
 
@@ -647,12 +667,12 @@ Methods
647
667
 
648
668
  | Angular version | ng-virtual-list version | git | npm |
649
669
  |--|--|--|--|
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) |
670
+ | 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) |
671
+ | 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) |
672
+ | 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) |
673
+ | 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) |
674
+ | 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) |
675
+ | 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
676
 
657
677
  <br/>
658
678
 
@@ -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, debounceTime, 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;
@@ -98,6 +112,7 @@ const DISPLAY_OBJECTS_LENGTH_MESUREMENT_ERROR = 1;
98
112
  const MAX_SCROLL_TO_ITERATIONS = 5;
99
113
  const DEFAULT_SNAPPING_METHOD = SnappingMethods.NORMAL;
100
114
  const DEFAULT_SELECT_METHOD = MethodsForSelecting.NONE;
115
+ const DEFAULT_SCREEN_READER_MESSAGE = 'Showing items $1 to $2';
101
116
  // presets
102
117
  const BEHAVIOR_AUTO = 'auto';
103
118
  const BEHAVIOR_INSTANT = 'instant';
@@ -188,7 +203,17 @@ class NgVirtualListService {
188
203
  collapseByClick = DEFAULT_COLLAPSE_BY_CLICK;
189
204
  _trackBox;
190
205
  listElement = null;
191
- collection = [];
206
+ _$displayItems = new BehaviorSubject([]);
207
+ $displayItems = this._$displayItems.asObservable();
208
+ _collection = [];
209
+ set collection(v) {
210
+ if (this._collection === v) {
211
+ return;
212
+ }
213
+ this._collection = v;
214
+ this._$displayItems.next(v);
215
+ }
216
+ get collection() { return this._collection; }
192
217
  constructor() {
193
218
  this._$methodOfSelecting.pipe(takeUntilDestroyed(), tap(v => {
194
219
  switch (v) {
@@ -327,11 +352,11 @@ class NgVirtualListService {
327
352
  }
328
353
  }
329
354
  itemToFocus;
330
- focus(element) {
355
+ focus(element, align = FocusAlignments.CENTER) {
331
356
  element.focus({ preventScroll: true });
332
357
  if (element.parentElement) {
333
358
  const pos = parseFloat(element.parentElement?.getAttribute('position') ?? '0');
334
- this.itemToFocus?.(element, pos);
359
+ this.itemToFocus?.(element, pos, align);
335
360
  }
336
361
  }
337
362
  areaFocus(id) {
@@ -410,7 +435,7 @@ const validateFunction = (value, undefinable = false, nullable = false) => {
410
435
  return (undefinable && isUndefinable(value)) || (nullable && isNullable(value)) || typeof value === 'function';
411
436
  };
412
437
 
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';
438
+ 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
439
  const getElementByIndex = (index) => {
415
440
  return `[${TABINDEX}="${index}"]`;
416
441
  };
@@ -430,7 +455,7 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
430
455
  _isCollapsed = false;
431
456
  config = signal({});
432
457
  measures = signal(undefined);
433
- focus = signal(false);
458
+ focused = signal(false);
434
459
  part = signal(PART_DEFAULT_ITEM);
435
460
  regular = false;
436
461
  data = signal(undefined);
@@ -494,21 +519,28 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
494
519
  }
495
520
  this._service.collapse(data, collapsed);
496
521
  };
522
+ _focusHandler = () =>
523
+ /**
524
+ * Focus a list item
525
+ */
526
+ (align = FocusAlignments.CENTER) => {
527
+ this.focus(align);
528
+ };
497
529
  constructor() {
498
530
  super();
499
531
  this._id = this._service.generateComponentId();
500
532
  this._elementRef.nativeElement.setAttribute('id', String(this._id));
501
- const $data = toObservable(this.data), $focus = toObservable(this.focus);
502
- $focus.pipe(takeUntilDestroyed(), tap(v => {
533
+ const $data = toObservable(this.data), $focused = toObservable(this.focused);
534
+ $focused.pipe(takeUntilDestroyed(), tap(v => {
503
535
  this._service.areaFocus(v ? this._id : this._service.focusedId === this._id ? null : this._service.focusedId);
504
536
  })).subscribe();
505
537
  fromEvent(this.element, EVENT_FOCUS_IN).pipe(takeUntilDestroyed(), tap(e => {
506
- this.focus.set(true);
538
+ this.focused.set(true);
507
539
  this.updateConfig(this._data);
508
540
  this.updatePartStr(this._data, this._isSelected, this._isCollapsed);
509
541
  })).subscribe(),
510
542
  fromEvent(this.element, EVENT_FOCUS_OUT).pipe(takeUntilDestroyed(), tap(e => {
511
- this.focus.set(false);
543
+ this.focused.set(false);
512
544
  this.updateConfig(this._data);
513
545
  this.updatePartStr(this._data, this._isSelected, this._isCollapsed);
514
546
  })).subscribe(),
@@ -611,23 +643,34 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
611
643
  }
612
644
  }
613
645
  }
646
+ focus(align = FocusAlignments.CENTER) {
647
+ if (this._service.listElement) {
648
+ const tabIndex = this._data?.config?.tabIndex ?? 0;
649
+ let index = tabIndex;
650
+ const el = this._service.listElement.querySelector(getElementByIndex(index));
651
+ if (el) {
652
+ this._service.focus(el, align);
653
+ }
654
+ }
655
+ }
614
656
  updateMeasures(v) {
615
657
  this.measures.set(v?.measures ? { ...v.measures } : undefined);
616
658
  }
617
659
  updateConfig(v) {
618
660
  this.config.set({
619
- ...v?.config || {}, selected: this._isSelected, collapsed: this._isCollapsed, focus: this.focus(),
620
- collapse: this._collapseHandler(v), select: this._selectHandler(v)
661
+ ...v?.config || {}, selected: this._isSelected, collapsed: this._isCollapsed, focused: this.focused(),
662
+ collapse: this._collapseHandler(v), select: this._selectHandler(v), focus: this._focusHandler(),
621
663
  });
622
664
  }
623
665
  update() {
624
666
  const data = this._data, regular = this.regular, length = this._regularLength;
625
667
  if (data) {
668
+ this._elementRef.nativeElement.setAttribute(ID, `${data.id}`);
626
669
  const styles = this._elementRef.nativeElement.style;
627
670
  styles.zIndex = data.config.zIndex;
628
671
  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)`;
672
+ this._elementRef.nativeElement.setAttribute(POSITION, data.config.sticky === 1 ? POSITION_ZERO : `${data.config.isVertical ? data.measures.y : data.measures.x}`);
673
+ 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
674
  ;
632
675
  if (!data.config.isSnappingMethodAdvanced) {
633
676
  styles.position = POSITION_STICKY;
@@ -636,17 +679,20 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
636
679
  else {
637
680
  styles.position = POSITION_ABSOLUTE;
638
681
  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)`;
682
+ this._elementRef.nativeElement.setAttribute(POSITION, POSITION_ZERO);
683
+ styles.transform = `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.delta}${PX}, ${data.config.isVertical ? data.measures.delta : 0}${PX}, ${POSITION_ZERO})`;
641
684
  }
642
685
  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)`;
686
+ this._elementRef.nativeElement.setAttribute(POSITION, `${data.config.isVertical ? data.measures.y : data.measures.x}`);
687
+ styles.transform = `${TRANSLATE_3D}(${data.config.isVertical ? 0 : data.measures.x}${PX}, ${data.config.isVertical ? data.measures.y : 0}${PX}, ${POSITION_ZERO})`;
645
688
  }
646
689
  }
647
690
  styles.height = data.config.isVertical ? data.config.dynamic ? SIZE_AUTO : `${data.measures.height}${PX}` : regular ? length : SIZE_100_PERSENT;
648
691
  styles.width = data.config.isVertical ? regular ? length : SIZE_100_PERSENT : data.config.dynamic ? SIZE_AUTO : `${data.measures.width}${PX}`;
649
692
  }
693
+ else {
694
+ this._elementRef.nativeElement.removeAttribute(ID);
695
+ }
650
696
  }
651
697
  updatePartStr(v, isSelected, isCollapsed) {
652
698
  let odd = false;
@@ -667,7 +713,7 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
667
713
  if (v ? v.config.new : false) {
668
714
  part += PART_ITEM_NEW;
669
715
  }
670
- if (this.focus()) {
716
+ if (this.focused()) {
671
717
  part += PART_ITEM_FOCUSED;
672
718
  }
673
719
  this.part.set(part);
@@ -714,14 +760,14 @@ class NgVirtualListItemComponent extends BaseVirtualListItemComponent {
714
760
  this._service.itemClick(this._data);
715
761
  }
716
762
  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 });
763
+ 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
764
  }
719
765
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgVirtualListItemComponent, decorators: [{
720
766
  type: Component,
721
767
  args: [{ selector: 'ng-virtual-list-item', imports: [CommonModule], host: {
722
768
  'class': 'ngvl__item',
723
769
  '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"] }]
770
+ }, 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
771
  }], ctorParameters: () => [] });
726
772
 
727
773
  /**
@@ -2161,7 +2207,7 @@ const isCollectionMode = (src, expected) => {
2161
2207
  return NORMAL_ALIASES.includes(src);
2162
2208
  };
2163
2209
 
2164
- const ROLE_LIST = 'list', ROLE_LIST_BOX = 'listbox';
2210
+ const ROLE_LIST = 'list', ROLE_LIST_BOX = 'listbox', ITEM_ID = 'item-id', ITEM_CONTAINER = 'ngvl-item__container';
2165
2211
  const validateScrollIteration = (value) => {
2166
2212
  return Number.isNaN(value) || (value < 0) ? 0 : value > MAX_SCROLL_TO_ITERATIONS ? MAX_SCROLL_TO_ITERATIONS : value;
2167
2213
  }, validateId = (id) => {
@@ -2179,6 +2225,35 @@ const validateScrollIteration = (value) => {
2179
2225
  if (!valid) {
2180
2226
  throw Error('The "iteration" parameter must be of type `number`.');
2181
2227
  }
2228
+ }, validateFocusAlignment = (align) => {
2229
+ const valid = validateString(align) && (align === 'none' || align === 'start' || align === 'center' || align === 'end');
2230
+ if (!valid) {
2231
+ throw Error('The "align" parameter must have the value `none`, `start`, `center` or `end`.');
2232
+ }
2233
+ };
2234
+ const formatScreenReaderMessage = (items, messagePattern, scrollSize, isVertical, bounds) => {
2235
+ if (!messagePattern) {
2236
+ return '';
2237
+ }
2238
+ const list = items ?? [], size = isVertical ? bounds.height : bounds.width;
2239
+ let start = Number.NaN, end = Number.NaN, prevItem;
2240
+ for (let i = 0, l = list.length; i < l; i++) {
2241
+ const item = list[i], position = isVertical ? item.measures.y : item.measures.x, itemSize = isVertical ? item.measures.height : item.measures.width;
2242
+ if (((position + itemSize) >= scrollSize) && Number.isNaN(start)) {
2243
+ start = item.index + 1;
2244
+ }
2245
+ if ((position >= (scrollSize + size)) && Number.isNaN(end) && prevItem) {
2246
+ end = prevItem.index + 1;
2247
+ }
2248
+ prevItem = item;
2249
+ }
2250
+ if (Number.isNaN(start) || Number.isNaN(end)) {
2251
+ return '';
2252
+ }
2253
+ let formatted = messagePattern ?? '';
2254
+ formatted = formatted.replace('$1', `${start}`);
2255
+ formatted = formatted.replace('$2', `${end}`);
2256
+ return formatted;
2182
2257
  };
2183
2258
  /**
2184
2259
  * Virtual list component.
@@ -2565,6 +2640,24 @@ class NgVirtualListComponent {
2565
2640
  * The name of the property by which tracking is performed
2566
2641
  */
2567
2642
  trackBy = input(TRACK_BY_PROPERTY_NAME, { ...this._trackByOptions });
2643
+ _screenReaderMessageOptions = {
2644
+ transform: (v) => {
2645
+ const valid = validateString(v);
2646
+ if (!valid) {
2647
+ console.error('The "screenReaderMessage" parameter must be of type `string`.');
2648
+ return DEFAULT_SCREEN_READER_MESSAGE;
2649
+ }
2650
+ return v;
2651
+ },
2652
+ };
2653
+ /**
2654
+ * Message for screen reader.
2655
+ * The message format is: "some text $1 some text $2",
2656
+ * where $1 is the number of the first element of the screen collection,
2657
+ * $2 is the number of the last element of the screen collection.
2658
+ */
2659
+ screenReaderMessage = input(DEFAULT_SCREEN_READER_MESSAGE, { ...this._screenReaderMessageOptions });
2660
+ screenReaderFormattedMessage = signal(this.screenReaderMessage());
2568
2661
  _isNotSelecting = this.getIsNotSelecting();
2569
2662
  get isNotSelecting() { return this._isNotSelecting; }
2570
2663
  _isSingleSelecting = this.getIsSingleSelecting();
@@ -2643,12 +2736,33 @@ class NgVirtualListComponent {
2643
2736
  this._scrollSize.set(actualScrollSize);
2644
2737
  }
2645
2738
  };
2646
- itemToFocus = (element, position) => {
2739
+ itemToFocus = (element, position, align = FocusAlignments.CENTER) => {
2647
2740
  const container = this._container()?.nativeElement;
2648
2741
  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);
2742
+ const { width, height } = this._bounds(), { width: elementWidth, height: elementHeight } = element.getBoundingClientRect(), isVertical = this._isVertical;
2743
+ let pos = Number.NaN;
2744
+ switch (align) {
2745
+ case FocusAlignments.START: {
2746
+ pos = isVertical ? position : position;
2747
+ break;
2748
+ }
2749
+ case FocusAlignments.CENTER: {
2750
+ pos = isVertical ? position - (height - elementHeight) * .5 : position - (width - elementWidth) * .5;
2751
+ break;
2752
+ }
2753
+ case FocusAlignments.END: {
2754
+ pos = isVertical ? position - (height - elementHeight) : position - (width - elementWidth);
2755
+ break;
2756
+ }
2757
+ case FocusAlignments.NONE:
2758
+ default: {
2759
+ break;
2760
+ }
2761
+ }
2762
+ if (!Number.isNaN(pos)) {
2763
+ const params = { [this._isVertical ? TOP_PROP_NAME : LEFT_PROP_NAME]: pos, behavior: 'instant' };
2764
+ container.scrollTo(params);
2765
+ }
2652
2766
  }
2653
2767
  };
2654
2768
  _elementRef = inject((ElementRef));
@@ -2721,7 +2835,10 @@ class NgVirtualListComponent {
2721
2835
  $trackBy.pipe(takeUntilDestroyed(), tap(v => {
2722
2836
  this._trackBox.trackingPropertyName = v;
2723
2837
  })).subscribe();
2724
- 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)), $bufferSize = toObservable(this.bufferSize).pipe(map(v => v < 0 ? DEFAULT_BUFFER_SIZE : v)), $maxBufferSize = toObservable(this.maxBufferSize).pipe(map(v => v < 0 ? DEFAULT_BUFFER_SIZE : v)), $itemConfigMap = toObservable(this.itemConfigMap).pipe(map(v => !v ? {} : v)), $snap = toObservable(this.snap), $isVertical = toObservable(this.direction).pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $isLazy = toObservable(this.collectionMode).pipe(map(v => this.getIsLazy(v || DEFAULT_COLLECTION_MODE))), $dynamicSize = toObservable(this.dynamicSize), $enabledBufferOptimization = toObservable(this.enabledBufferOptimization), $snappingMethod = toObservable(this.snappingMethod).pipe(map(v => this.getIsSnappingMethodAdvanced(v || DEFAULT_SNAPPING_METHOD))), $methodForSelecting = toObservable(this.methodForSelecting), $selectedIds = toObservable(this.selectedIds), $collapsedIds = toObservable(this.collapsedIds).pipe(map(v => Array.isArray(v) ? v : [])), $collapsedItemIds = toObservable(this._collapsedItemIds).pipe(map(v => Array.isArray(v) ? v : [])), $actualItems = toObservable(this._actualItems), $cacheVersion = toObservable(this._cacheVersion);
2838
+ 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)), $bufferSize = toObservable(this.bufferSize).pipe(map(v => v < 0 ? DEFAULT_BUFFER_SIZE : v)), $maxBufferSize = toObservable(this.maxBufferSize).pipe(map(v => v < 0 ? DEFAULT_BUFFER_SIZE : v)), $itemConfigMap = toObservable(this.itemConfigMap).pipe(map(v => !v ? {} : v)), $snap = toObservable(this.snap), $isVertical = toObservable(this.direction).pipe(map(v => this.getIsVertical(v || DEFAULT_DIRECTION))), $isLazy = toObservable(this.collectionMode).pipe(map(v => this.getIsLazy(v || DEFAULT_COLLECTION_MODE))), $dynamicSize = toObservable(this.dynamicSize), $enabledBufferOptimization = toObservable(this.enabledBufferOptimization), $snappingMethod = toObservable(this.snappingMethod).pipe(map(v => this.getIsSnappingMethodAdvanced(v || DEFAULT_SNAPPING_METHOD))), $methodForSelecting = toObservable(this.methodForSelecting), $selectedIds = toObservable(this.selectedIds), $collapsedIds = toObservable(this.collapsedIds).pipe(map(v => Array.isArray(v) ? v : [])), $collapsedItemIds = toObservable(this._collapsedItemIds).pipe(map(v => Array.isArray(v) ? v : [])), $actualItems = toObservable(this._actualItems), $screenReaderMessage = toObservable(this.screenReaderMessage), $displayItems = this._service.$displayItems, $cacheVersion = toObservable(this._cacheVersion);
2839
+ combineLatest([$displayItems, $screenReaderMessage, $isVertical, $scrollSize, $bounds]).pipe(takeUntilDestroyed(), distinctUntilChanged(), debounceTime(100), tap(([items, screenReaderMessage, isVertical, scrollSize, bounds]) => {
2840
+ this.screenReaderFormattedMessage.set(formatScreenReaderMessage(items, screenReaderMessage, scrollSize, isVertical, bounds));
2841
+ })).subscribe();
2725
2842
  $isLazy.pipe(takeUntilDestroyed(), tap(v => {
2726
2843
  this._trackBox.isLazy = v;
2727
2844
  })).subscribe();
@@ -2974,6 +3091,20 @@ class NgVirtualListComponent {
2974
3091
  validateId(id);
2975
3092
  return this._trackBox.getItemBounds(id);
2976
3093
  }
3094
+ /**
3095
+ * Focus an list item by a given id.
3096
+ */
3097
+ focus(id, align = FocusAlignments.NONE) {
3098
+ validateId(id);
3099
+ validateFocusAlignment(align);
3100
+ const el = this._list()?.nativeElement.querySelector(`[${ITEM_ID}="${id}"]`);
3101
+ if (el) {
3102
+ const focusedEl = el.querySelector(`.${ITEM_CONTAINER}`);
3103
+ if (focusedEl) {
3104
+ this._service.focus(focusedEl, align);
3105
+ }
3106
+ }
3107
+ }
2977
3108
  /**
2978
3109
  * The method scrolls the list to the element with the given id and returns the value of the scrolled area.
2979
3110
  * Behavior accepts the values ​​"auto", "instant" and "smooth".
@@ -3135,13 +3266,13 @@ class NgVirtualListComponent {
3135
3266
  }
3136
3267
  }
3137
3268
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgVirtualListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3138
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", 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 }, collapsedIds: { classPropertyName: "collapsedIds", publicName: "collapsedIds", isSignal: true, isRequired: false, transformFunction: null }, selectByClick: { classPropertyName: "selectByClick", publicName: "selectByClick", isSignal: true, isRequired: false, transformFunction: null }, collapseByClick: { classPropertyName: "collapseByClick", publicName: "collapseByClick", 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 }, 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 }, collectionMode: { classPropertyName: "collectionMode", publicName: "collectionMode", 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", onCollapse: "onCollapse", onScrollReachStart: "onScrollReachStart", onScrollReachEnd: "onScrollReachEnd" }, host: { styleAttribute: "position: relative;" }, 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\" [attr.aria-activedescendant]=\"focusedElement()\" tabindex=\"0\" #list\r\n 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 });
3269
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", 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 }, collapsedIds: { classPropertyName: "collapsedIds", publicName: "collapsedIds", isSignal: true, isRequired: false, transformFunction: null }, selectByClick: { classPropertyName: "selectByClick", publicName: "selectByClick", isSignal: true, isRequired: false, transformFunction: null }, collapseByClick: { classPropertyName: "collapseByClick", publicName: "collapseByClick", 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 }, 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 }, collectionMode: { classPropertyName: "collectionMode", publicName: "collectionMode", 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 }, screenReaderMessage: { classPropertyName: "screenReaderMessage", publicName: "screenReaderMessage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onScroll: "onScroll", onScrollEnd: "onScrollEnd", onViewportChange: "onViewportChange", onItemClick: "onItemClick", onSelect: "onSelect", onCollapse: "onCollapse", onScrollReachStart: "onScrollReachStart", onScrollReachEnd: "onScrollReachEnd" }, host: { styleAttribute: "position: relative;" }, 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: "<div aria-live=\"polite\" aria-atomic=\"true\" class=\"ngvl__screen-reader\">\r\n {{ screenReaderFormattedMessage() }}\r\n</div>\r\n\r\n@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\" [attr.aria-activedescendant]=\"focusedElement()\" tabindex=\"0\" #list\r\n 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%}.ngvl__screen-reader{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom });
3139
3270
  }
3140
3271
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: NgVirtualListComponent, decorators: [{
3141
3272
  type: Component,
3142
3273
  args: [{ selector: 'ng-virtual-list', imports: [CommonModule], host: {
3143
3274
  'style': 'position: relative;'
3144
- }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, providers: [NgVirtualListService], 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\" [attr.aria-activedescendant]=\"focusedElement()\" tabindex=\"0\" #list\r\n 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"] }]
3275
+ }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, providers: [NgVirtualListService], template: "<div aria-live=\"polite\" aria-atomic=\"true\" class=\"ngvl__screen-reader\">\r\n {{ screenReaderFormattedMessage() }}\r\n</div>\r\n\r\n@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\" [attr.aria-activedescendant]=\"focusedElement()\" tabindex=\"0\" #list\r\n 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%}.ngvl__screen-reader{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
3145
3276
  }], ctorParameters: () => [], propDecorators: { _listContainerRef: [{
3146
3277
  type: ViewChild,
3147
3278
  args: ['renderersContainer', { read: ViewContainerRef }]
@@ -3158,5 +3289,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImpor
3158
3289
  * Generated bundle index. Do not edit.
3159
3290
  */
3160
3291
 
3161
- export { CollectionModes, Directions, MethodsForSelecting, NgVirtualListComponent, NgVirtualListItemComponent, ScrollEvent, SnappingMethods, debounce, toggleClassName };
3292
+ export { CollectionModes, Directions, FocusAlignments, MethodsForSelecting, NgVirtualListComponent, NgVirtualListItemComponent, ScrollEvent, SnappingMethods, debounce, toggleClassName };
3162
3293
  //# sourceMappingURL=ng-virtual-list.mjs.map