import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, HostListener, input, OnChanges, output, SimpleChanges, ViewChild } from '@angular/core'; export type DialogSize = 'sm' | 'md' | 'lg'; const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'; @Component({ selector: 'app-dialog', standalone: true, templateUrl: './dialog.component.html', styleUrl: './dialog.component.scss', changeDetection: ChangeDetectionStrategy.OnPush }) export class DialogComponent implements OnChanges, AfterViewInit { readonly open = input(false); readonly titleText = input(null); readonly size = input('md'); readonly closed = output(); @ViewChild('panel') private panelRef?: ElementRef; private previouslyFocused: HTMLElement | null = null; ngOnChanges(changes: SimpleChanges): void { if (changes['open']) { if (this.open()) { this.previouslyFocused = document.activeElement as HTMLElement | null; queueMicrotask(() => this.focusPanel()); } else { this.previouslyFocused?.focus(); this.previouslyFocused = null; } } } ngAfterViewInit(): void { if (this.open()) { this.focusPanel(); } } @HostListener('document:keydown', ['$event']) protected handleKeydown(event: KeyboardEvent): void { if (!this.open()) { return; } if (event.key === 'Escape') { this.requestClose(); return; } if (event.key === 'Tab') { this.trapFocus(event); } } protected requestClose(): void { this.closed.emit(); } protected handleBackdropClick(): void { this.requestClose(); } private focusPanel(): void { const panel = this.panelRef?.nativeElement; if (!panel) { return; } const focusable = panel.querySelectorAll(FOCUSABLE_SELECTOR); (focusable[0] ?? panel).focus(); } private trapFocus(event: KeyboardEvent): void { const panel = this.panelRef?.nativeElement; if (!panel) { return; } const focusable = Array.from(panel.querySelectorAll(FOCUSABLE_SELECTOR)); if (focusable.length === 0) { return; } const first = focusable[0]; const last = focusable[focusable.length - 1]; const active = document.activeElement; if (event.shiftKey && active === first) { event.preventDefault(); last.focus(); } else if (!event.shiftKey && active === last) { event.preventDefault(); first.focus(); } } }