Usage
import { trapFocus } from 'ngwr/utils';
@HostListener('keydown', ['$event']) onKey(e: KeyboardEvent) {
// Cycle Tab focus inside the dialog while it's open.
trapFocus(dialogRef.nativeElement, e);
}Why ngwr provides this
Doing this by hand is ~30 lines of Tab detection, focusable enumeration, and active-element checks — repeated in every dialog, drawer, command palette, and tooltip-with-actions. Getting it wrong is an accessibility bug. One helper, called from any keydown handler, makes the trap a one-liner.
// Native — what an accessible dialog actually needs to do.
@HostListener('keydown', ['$event'])
onKey(e: KeyboardEvent) {
if (e.key !== 'Tab') return;
const focusables = getAllVisibleFocusableSortedByTabindex(this.el.nativeElement);
if (focusables.length === 0) return;
const first = focusables[0];
const last = focusables[focusables.length - 1];
const active = document.activeElement;
if (e.shiftKey && active === first) { e.preventDefault(); last.focus(); }
else if (!e.shiftKey && active === last) { e.preventDefault(); first.focus(); }
}
// → Then you implement `getAllVisibleFocusableSortedByTabindex` per dialog.
// ngwr — one line.
@HostListener('keydown', ['$event'])
onKey(e: KeyboardEvent) {
trapFocus(this.el.nativeElement, e);
}API
| Name | Description | Type | Default |
|---|---|---|---|
trapFocus(root, event) | Cycle Tab focus inside root — call from a keydown handler. Returns true when the event was a Tab that got handled. | (root: HTMLElement, e: KeyboardEvent) => boolean | — |