[CL-161] Update bit-search support autofocus (#7272)

appAutofocus currently doesn't work on the bit-search component. This PR resolves this issue by introducing a FocusableElement interface components can implement, which is respected by the autofocus directive.
This commit is contained in:
Oscar Hinton 2024-01-11 16:52:22 +01:00 committed by GitHub
parent 7112f44375
commit 280cb7e2c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 72 additions and 33 deletions

View File

@ -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());
}
}
}
}

View File

@ -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";

View File

@ -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();
}
}
}

View File

@ -1 +1,2 @@
export * from "./input.module";
export * from "./autofocus.directive";

View File

@ -8,6 +8,7 @@
<i class="bwi bwi-search bwi-fw tw-text-muted"></i>
</label>
<input
#input
bitInput
type="search"
[id]="id"

View File

@ -1,6 +1,8 @@
import { Component, Input } from "@angular/core";
import { Component, ElementRef, Input, ViewChild } from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
import { FocusableElement } from "../input/autofocus.directive";
let nextId = 0;
@Component({
@ -12,18 +14,28 @@ let nextId = 0;
multi: true,
useExisting: SearchComponent,
},
{
provide: FocusableElement,
useExisting: SearchComponent,
},
],
})
export class SearchComponent implements ControlValueAccessor {
export class SearchComponent implements ControlValueAccessor, FocusableElement {
private notifyOnChange: (v: string) => void;
private notifyOnTouch: () => void;
@ViewChild("input") private input: ElementRef<HTMLInputElement>;
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);