diff --git a/projects/angular-mentions/src/lib/mention-list.component.ts b/projects/angular-mentions/src/lib/mention-list.component.ts index 663d103f..c47204c2 100644 --- a/projects/angular-mentions/src/lib/mention-list.component.ts +++ b/projects/angular-mentions/src/lib/mention-list.component.ts @@ -18,15 +18,15 @@ import { getCaretCoordinates } from './caret-coords'; {{item[labelKey]}} - `, standalone: false @@ -34,7 +34,9 @@ import { getCaretCoordinates } from './caret-coords'; export class MentionListComponent implements AfterContentChecked { @Input() labelKey: string = 'label'; @Input() itemTemplate: TemplateRef; + @Input() listAriaLabel: string; @Output() itemClick = new EventEmitter(); + @Output() itemActivated = new EventEmitter(true); @ViewChild('list', { static: true }) list: ElementRef; @ViewChild('defaultItemTemplate', { static: true }) defaultItemTemplate: TemplateRef; items = []; @@ -85,6 +87,7 @@ export class MentionListComponent implements AfterContentChecked { } activateNextItem() { + this.itemActivated.emit(); // adjust scrollable-menu offset if the next item is out of view let listEl: HTMLElement = this.list.nativeElement; let activeEl = listEl.getElementsByClassName('active').item(0); @@ -102,6 +105,7 @@ export class MentionListComponent implements AfterContentChecked { } activatePreviousItem() { + this.itemActivated.emit(); // adjust the scrollable-menu offset if the previous item is out of view let listEl: HTMLElement = this.list.nativeElement; let activeEl = listEl.getElementsByClassName('active').item(0); diff --git a/projects/angular-mentions/src/lib/mention-utils.ts b/projects/angular-mentions/src/lib/mention-utils.ts index fb7fec28..7cf8552e 100644 --- a/projects/angular-mentions/src/lib/mention-utils.ts +++ b/projects/angular-mentions/src/lib/mention-utils.ts @@ -176,3 +176,7 @@ function localToRelativeCoordinates( } } } + +export function setAriaActiveDescendant(nativeElement: any, list: HTMLElement) { + nativeElement.ariaActiveDescendantElement = list.querySelector('[aria-selected="true"]'); +} \ No newline at end of file diff --git a/projects/angular-mentions/src/lib/mention.directive.ts b/projects/angular-mentions/src/lib/mention.directive.ts index 89ce35a2..3ce979cd 100644 --- a/projects/angular-mentions/src/lib/mention.directive.ts +++ b/projects/angular-mentions/src/lib/mention.directive.ts @@ -1,6 +1,6 @@ -import { ComponentFactoryResolver, Directive, ElementRef, TemplateRef, ViewContainerRef } from "@angular/core"; +import { Directive, ElementRef, TemplateRef, ViewContainerRef } from "@angular/core"; import { EventEmitter, Input, OnChanges, Output, SimpleChanges } from "@angular/core"; -import { getCaretPosition, getValue, insertValue, setCaretPosition } from './mention-utils'; +import { getCaretPosition, getValue, insertValue, setAriaActiveDescendant, setCaretPosition } from './mention-utils'; import { MentionConfig } from "./mention-config"; import { MentionListComponent } from './mention-list.component'; @@ -66,6 +66,8 @@ export class MentionDirective implements OnChanges { // template to use for rendering list items @Input() mentionListTemplate: TemplateRef; + @Input() listAriaLabel = 'Suggestions'; + // event emitted whenever the search term changes @Output() searchTerm = new EventEmitter(); @@ -88,7 +90,6 @@ export class MentionDirective implements OnChanges { constructor( private _element: ElementRef, - private _componentResolver: ComponentFactoryResolver, private _viewContainerRef: ViewContainerRef ) { } @@ -350,6 +351,9 @@ export class MentionDirective implements OnChanges { if (this.searchList) { this.searchList.items = matches; this.searchList.hidden = matches.length == 0; + setTimeout(() => { + setAriaActiveDescendant(this._element.nativeElement, this.searchList.list.nativeElement); + }, 50); } } @@ -357,15 +361,18 @@ export class MentionDirective implements OnChanges { this.opened.emit(); if (this.searchList == null) { - let componentFactory = this._componentResolver.resolveComponentFactory(MentionListComponent); - let componentRef = this._viewContainerRef.createComponent(componentFactory); + let componentRef = this._viewContainerRef.createComponent(MentionListComponent); this.searchList = componentRef.instance; this.searchList.itemTemplate = this.mentionListTemplate; + this.searchList.listAriaLabel = this.listAriaLabel; componentRef.instance['itemClick'].subscribe(() => { nativeElement.focus(); let fakeKeydown = { key: 'Enter', keyCode: KEY_ENTER, wasClick: true }; this.keyHandler(fakeKeydown, nativeElement); }); + componentRef.instance['itemActivated'].subscribe(() => { + setAriaActiveDescendant(nativeElement, this.searchList.list.nativeElement); + }); } this.searchList.labelKey = this.activeConfig.labelKey; this.searchList.dropUp = this.activeConfig.dropUp;