diff --git a/libs/angular/src/directives/autofocus.directive.ts b/libs/angular/src/directives/autofocus.directive.ts deleted file mode 100644 index c11b473f99..0000000000 --- a/libs/angular/src/directives/autofocus.directive.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Directive, ElementRef, Input, NgZone } from "@angular/core"; -import { take } from "rxjs/operators"; - -import { Utils } from "@bitwarden/common/platform/misc/utils"; - -@Directive({ - selector: "[appAutofocus]", -}) -export class AutofocusDirective { - @Input() set appAutofocus(condition: boolean | string) { - this.autofocus = condition === "" || condition === true; - } - - private autofocus: boolean; - - constructor( - private el: ElementRef, - private ngZone: NgZone, - ) {} - - ngOnInit() { - if (!Utils.isMobileBrowser && this.autofocus) { - if (this.ngZone.isStable) { - this.el.nativeElement.focus(); - } else { - this.ngZone.onStable.pipe(take(1)).subscribe(() => this.el.nativeElement.focus()); - } - } - } -} diff --git a/libs/angular/src/jslib.module.ts b/libs/angular/src/jslib.module.ts index b585591aa6..1177106767 100644 --- a/libs/angular/src/jslib.module.ts +++ b/libs/angular/src/jslib.module.ts @@ -2,12 +2,13 @@ import { CommonModule, DatePipe } from "@angular/common"; import { NgModule } from "@angular/core"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { AutofocusDirective } from "@bitwarden/components"; + import { CalloutComponent } from "./components/callout.component"; import { BitwardenToastModule } from "./components/toastr.component"; import { A11yInvalidDirective } from "./directives/a11y-invalid.directive"; import { A11yTitleDirective } from "./directives/a11y-title.directive"; import { ApiActionDirective } from "./directives/api-action.directive"; -import { AutofocusDirective } from "./directives/autofocus.directive"; import { BoxRowDirective } from "./directives/box-row.directive"; import { CopyClickDirective } from "./directives/copy-click.directive"; import { CopyTextDirective } from "./directives/copy-text.directive"; diff --git a/libs/components/src/input/autofocus.directive.ts b/libs/components/src/input/autofocus.directive.ts new file mode 100644 index 0000000000..f8161ee6e0 --- /dev/null +++ b/libs/components/src/input/autofocus.directive.ts @@ -0,0 +1,54 @@ +import { Directive, ElementRef, Input, NgZone, Optional } from "@angular/core"; +import { take } from "rxjs/operators"; + +import { Utils } from "@bitwarden/common/platform/misc/utils"; + +/** + * Interface for implementing focusable components. Used by the AutofocusDirective. + */ +export abstract class FocusableElement { + focus: () => void; +} + +/** + * Directive to focus an element. + * + * @remarks + * + * If the component provides the `FocusableElement` interface, the `focus` + * method will be called. Otherwise, the native element will be focused. + */ +@Directive({ + selector: "[appAutofocus], [bitAutofocus]", +}) +export class AutofocusDirective { + @Input() set appAutofocus(condition: boolean | string) { + this.autofocus = condition === "" || condition === true; + } + + private autofocus: boolean; + + constructor( + private el: ElementRef, + private ngZone: NgZone, + @Optional() private focusableElement: FocusableElement, + ) {} + + ngOnInit() { + if (!Utils.isMobileBrowser && this.autofocus) { + if (this.ngZone.isStable) { + this.focus(); + } else { + this.ngZone.onStable.pipe(take(1)).subscribe(this.focus.bind(this)); + } + } + } + + private focus() { + if (this.focusableElement) { + this.focusableElement.focus(); + } else { + this.el.nativeElement.focus(); + } + } +} diff --git a/libs/components/src/input/index.ts b/libs/components/src/input/index.ts index 4036b8ab94..9713d2b919 100644 --- a/libs/components/src/input/index.ts +++ b/libs/components/src/input/index.ts @@ -1 +1,2 @@ export * from "./input.module"; +export * from "./autofocus.directive"; diff --git a/libs/components/src/search/search.component.html b/libs/components/src/search/search.component.html index 2396dc3423..82bd153e10 100644 --- a/libs/components/src/search/search.component.html +++ b/libs/components/src/search/search.component.html @@ -8,6 +8,7 @@ void; private notifyOnTouch: () => void; + @ViewChild("input") private input: ElementRef; + protected id = `search-id-${nextId++}`; protected searchText: string; @Input() disabled: boolean; @Input() placeholder: string; + focus() { + this.input.nativeElement.focus(); + } + onChange(searchText: string) { if (this.notifyOnChange != undefined) { this.notifyOnChange(searchText);