UNPKG

dga-ui-lib

Version:

A dga/ui-inspired Angular component library with full developer ownership

77 lines 8.92 kB
import { Directive, Input } from '@angular/core'; import { Subject } from 'rxjs'; import * as i0 from "@angular/core"; /** * Focus management directive for accessibility * Helps manage focus within components */ export class FocusManagerDirective { constructor(elementRef) { this.elementRef = elementRef; this.focusOnInit = false; this.trapFocus = false; this.destroy$ = new Subject(); this.focusableElements = []; } ngOnInit() { if (this.focusOnInit) { setTimeout(() => this.focus()); } if (this.trapFocus) { this.setupFocusTrap(); } } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } focus() { this.elementRef.nativeElement.focus(); } setupFocusTrap() { const element = this.elementRef.nativeElement; // Find all focusable elements this.updateFocusableElements(); element.addEventListener('keydown', this.handleKeydown.bind(this)); } updateFocusableElements() { const element = this.elementRef.nativeElement; const focusableSelectors = [ 'button:not([disabled])', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'a[href]', '[tabindex]:not([tabindex="-1"])' ]; this.focusableElements = Array.from(element.querySelectorAll(focusableSelectors.join(','))); } handleKeydown(event) { if (event.key !== 'Tab') return; const firstElement = this.focusableElements[0]; const lastElement = this.focusableElements[this.focusableElements.length - 1]; if (event.shiftKey && document.activeElement === firstElement) { event.preventDefault(); lastElement?.focus(); } else if (!event.shiftKey && document.activeElement === lastElement) { event.preventDefault(); firstElement?.focus(); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FocusManagerDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: FocusManagerDirective, isStandalone: true, selector: "[dgaFocusManager]", inputs: { focusOnInit: "focusOnInit", trapFocus: "trapFocus" }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FocusManagerDirective, decorators: [{ type: Directive, args: [{ selector: '[dgaFocusManager]', standalone: true }] }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { focusOnInit: [{ type: Input }], trapFocus: [{ type: Input }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9jdXMtbWFuYWdlci5kaXJlY3RpdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy91aS1saWJyYXJ5L3NyYy9saWIvdXRpbHMvZm9jdXMtbWFuYWdlci5kaXJlY3RpdmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBYyxLQUFLLEVBQXFCLE1BQU0sZUFBZSxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxPQUFPLEVBQWEsTUFBTSxNQUFNLENBQUM7O0FBRTFDOzs7R0FHRztBQUtILE1BQU0sT0FBTyxxQkFBcUI7SUFPaEMsWUFBb0IsVUFBbUM7UUFBbkMsZUFBVSxHQUFWLFVBQVUsQ0FBeUI7UUFOOUMsZ0JBQVcsR0FBWSxLQUFLLENBQUM7UUFDN0IsY0FBUyxHQUFZLEtBQUssQ0FBQztRQUU1QixhQUFRLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FBQztRQUMvQixzQkFBaUIsR0FBa0IsRUFBRSxDQUFDO0lBRVksQ0FBQztJQUUzRCxRQUFRO1FBQ04sSUFBSSxJQUFJLENBQUMsV0FBVyxFQUFFO1lBQ3BCLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztTQUNoQztRQUVELElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRTtZQUNsQixJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7U0FDdkI7SUFDSCxDQUFDO0lBRUQsV0FBVztRQUNULElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUMzQixDQUFDO0lBRUQsS0FBSztRQUNILElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ3hDLENBQUM7SUFFTyxjQUFjO1FBQ3BCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDO1FBRTlDLDhCQUE4QjtRQUM5QixJQUFJLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztRQUUvQixPQUFPLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDckUsQ0FBQztJQUVPLHVCQUF1QjtRQUM3QixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQztRQUM5QyxNQUFNLGtCQUFrQixHQUFHO1lBQ3pCLHdCQUF3QjtZQUN4Qix1QkFBdUI7WUFDdkIsd0JBQXdCO1lBQ3hCLDBCQUEwQjtZQUMxQixTQUFTO1lBQ1QsaUNBQWlDO1NBQ2xDLENBQUM7UUFFRixJQUFJLENBQUMsaUJBQWlCLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FDakMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUN0QyxDQUFDO0lBQ3JCLENBQUM7SUFFTyxhQUFhLENBQUMsS0FBb0I7UUFDeEMsSUFBSSxLQUFLLENBQUMsR0FBRyxLQUFLLEtBQUs7WUFBRSxPQUFPO1FBRWhDLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMvQyxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQztRQUU5RSxJQUFJLEtBQUssQ0FBQyxRQUFRLElBQUksUUFBUSxDQUFDLGFBQWEsS0FBSyxZQUFZLEVBQUU7WUFDN0QsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3ZCLFdBQVcsRUFBRSxLQUFLLEVBQUUsQ0FBQztTQUN0QjthQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxJQUFJLFFBQVEsQ0FBQyxhQUFhLEtBQUssV0FBVyxFQUFFO1lBQ3BFLEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN2QixZQUFZLEVBQUUsS0FBSyxFQUFFLENBQUM7U0FDdkI7SUFDSCxDQUFDOytHQWxFVSxxQkFBcUI7bUdBQXJCLHFCQUFxQjs7NEZBQXJCLHFCQUFxQjtrQkFKakMsU0FBUzttQkFBQztvQkFDVCxRQUFRLEVBQUUsbUJBQW1CO29CQUM3QixVQUFVLEVBQUUsSUFBSTtpQkFDakI7K0VBRVUsV0FBVztzQkFBbkIsS0FBSztnQkFDRyxTQUFTO3NCQUFqQixLQUFLIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRGlyZWN0aXZlLCBFbGVtZW50UmVmLCBJbnB1dCwgT25EZXN0cm95LCBPbkluaXQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgU3ViamVjdCwgdGFrZVVudGlsIH0gZnJvbSAncnhqcyc7XHJcblxyXG4vKipcclxuICogRm9jdXMgbWFuYWdlbWVudCBkaXJlY3RpdmUgZm9yIGFjY2Vzc2liaWxpdHlcclxuICogSGVscHMgbWFuYWdlIGZvY3VzIHdpdGhpbiBjb21wb25lbnRzXHJcbiAqL1xyXG5ARGlyZWN0aXZlKHtcclxuICBzZWxlY3RvcjogJ1tkZ2FGb2N1c01hbmFnZXJdJyxcclxuICBzdGFuZGFsb25lOiB0cnVlXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBGb2N1c01hbmFnZXJEaXJlY3RpdmUgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XHJcbiAgQElucHV0KCkgZm9jdXNPbkluaXQ6IGJvb2xlYW4gPSBmYWxzZTtcclxuICBASW5wdXQoKSB0cmFwRm9jdXM6IGJvb2xlYW4gPSBmYWxzZTtcclxuICBcclxuICBwcml2YXRlIGRlc3Ryb3kkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcclxuICBwcml2YXRlIGZvY3VzYWJsZUVsZW1lbnRzOiBIVE1MRWxlbWVudFtdID0gW107XHJcbiAgXHJcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBlbGVtZW50UmVmOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50Pikge31cclxuICBcclxuICBuZ09uSW5pdCgpOiB2b2lkIHtcclxuICAgIGlmICh0aGlzLmZvY3VzT25Jbml0KSB7XHJcbiAgICAgIHNldFRpbWVvdXQoKCkgPT4gdGhpcy5mb2N1cygpKTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgaWYgKHRoaXMudHJhcEZvY3VzKSB7XHJcbiAgICAgIHRoaXMuc2V0dXBGb2N1c1RyYXAoKTtcclxuICAgIH1cclxuICB9XHJcbiAgXHJcbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XHJcbiAgICB0aGlzLmRlc3Ryb3kkLm5leHQoKTtcclxuICAgIHRoaXMuZGVzdHJveSQuY29tcGxldGUoKTtcclxuICB9XHJcbiAgXHJcbiAgZm9jdXMoKTogdm9pZCB7XHJcbiAgICB0aGlzLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5mb2N1cygpO1xyXG4gIH1cclxuICBcclxuICBwcml2YXRlIHNldHVwRm9jdXNUcmFwKCk6IHZvaWQge1xyXG4gICAgY29uc3QgZWxlbWVudCA9IHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50O1xyXG4gICAgXHJcbiAgICAvLyBGaW5kIGFsbCBmb2N1c2FibGUgZWxlbWVudHNcclxuICAgIHRoaXMudXBkYXRlRm9jdXNhYmxlRWxlbWVudHMoKTtcclxuICAgIFxyXG4gICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCdrZXlkb3duJywgdGhpcy5oYW5kbGVLZXlkb3duLmJpbmQodGhpcykpO1xyXG4gIH1cclxuICBcclxuICBwcml2YXRlIHVwZGF0ZUZvY3VzYWJsZUVsZW1lbnRzKCk6IHZvaWQge1xyXG4gICAgY29uc3QgZWxlbWVudCA9IHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50O1xyXG4gICAgY29uc3QgZm9jdXNhYmxlU2VsZWN0b3JzID0gW1xyXG4gICAgICAnYnV0dG9uOm5vdChbZGlzYWJsZWRdKScsXHJcbiAgICAgICdpbnB1dDpub3QoW2Rpc2FibGVkXSknLFxyXG4gICAgICAnc2VsZWN0Om5vdChbZGlzYWJsZWRdKScsXHJcbiAgICAgICd0ZXh0YXJlYTpub3QoW2Rpc2FibGVkXSknLFxyXG4gICAgICAnYVtocmVmXScsXHJcbiAgICAgICdbdGFiaW5kZXhdOm5vdChbdGFiaW5kZXg9XCItMVwiXSknXHJcbiAgICBdO1xyXG4gICAgXHJcbiAgICB0aGlzLmZvY3VzYWJsZUVsZW1lbnRzID0gQXJyYXkuZnJvbShcclxuICAgICAgZWxlbWVudC5xdWVyeVNlbGVjdG9yQWxsKGZvY3VzYWJsZVNlbGVjdG9ycy5qb2luKCcsJykpXHJcbiAgICApIGFzIEhUTUxFbGVtZW50W107XHJcbiAgfVxyXG4gIFxyXG4gIHByaXZhdGUgaGFuZGxlS2V5ZG93bihldmVudDogS2V5Ym9hcmRFdmVudCk6IHZvaWQge1xyXG4gICAgaWYgKGV2ZW50LmtleSAhPT0gJ1RhYicpIHJldHVybjtcclxuICAgIFxyXG4gICAgY29uc3QgZmlyc3RFbGVtZW50ID0gdGhpcy5mb2N1c2FibGVFbGVtZW50c1swXTtcclxuICAgIGNvbnN0IGxhc3RFbGVtZW50ID0gdGhpcy5mb2N1c2FibGVFbGVtZW50c1t0aGlzLmZvY3VzYWJsZUVsZW1lbnRzLmxlbmd0aCAtIDFdO1xyXG4gICAgXHJcbiAgICBpZiAoZXZlbnQuc2hpZnRLZXkgJiYgZG9jdW1lbnQuYWN0aXZlRWxlbWVudCA9PT0gZmlyc3RFbGVtZW50KSB7XHJcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICAgIGxhc3RFbGVtZW50Py5mb2N1cygpO1xyXG4gICAgfSBlbHNlIGlmICghZXZlbnQuc2hpZnRLZXkgJiYgZG9jdW1lbnQuYWN0aXZlRWxlbWVudCA9PT0gbGFzdEVsZW1lbnQpIHtcclxuICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuICAgICAgZmlyc3RFbGVtZW50Py5mb2N1cygpO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG4iXX0=