dga-ui-lib
Version:
A dga/ui-inspired Angular component library with full developer ownership
77 lines • 8.92 kB
JavaScript
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=