| 123456789 |
- import { AfterViewInit, Directive, ElementRef, HostListener, inject, OnDestroy, output } from '@angular/core';
- @Directive({selector:'[appAccessibleDialog]',standalone:true})
- export class AccessibleDialogDirective implements AfterViewInit,OnDestroy {
- readonly dialogEscape=output<void>();private readonly element:HTMLElement=inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;private readonly previous=document.activeElement as HTMLElement|null;
- private controls():HTMLElement[]{return Array.from(this.element.querySelectorAll<HTMLElement>('button:not([disabled]),a[href],input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex="0"]')).filter(x=>x.getClientRects().length>0);}
- ngAfterViewInit():void {this.element.tabIndex=-1;queueMicrotask(()=>{if(this.element.isConnected)(this.controls()[0]||this.element).focus();});}
- @HostListener('keydown',['$event']) onKey(event:KeyboardEvent):void {if(event.key==='Escape'){event.preventDefault();event.stopPropagation();this.dialogEscape.emit();}if(event.key==='Tab'){const items=this.controls(),first=items[0],last=items[items.length-1];if(!first){event.preventDefault();this.element.focus();}else if(event.shiftKey&&(document.activeElement===first||document.activeElement===this.element)){event.preventDefault();last.focus();}else if(!event.shiftKey&&document.activeElement===last){event.preventDefault();first.focus();}}}
- ngOnDestroy():void{if(this.previous?.isConnected)this.previous.focus();}
- }
|