mirror of
https://github.com/bitwarden/browser.git
synced 2024-09-29 04:17:41 +02:00
[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:
parent
7112f44375
commit
280cb7e2c0
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,12 +2,13 @@ import { CommonModule, DatePipe } from "@angular/common";
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||||
|
|
||||||
|
import { AutofocusDirective } from "@bitwarden/components";
|
||||||
|
|
||||||
import { CalloutComponent } from "./components/callout.component";
|
import { CalloutComponent } from "./components/callout.component";
|
||||||
import { BitwardenToastModule } from "./components/toastr.component";
|
import { BitwardenToastModule } from "./components/toastr.component";
|
||||||
import { A11yInvalidDirective } from "./directives/a11y-invalid.directive";
|
import { A11yInvalidDirective } from "./directives/a11y-invalid.directive";
|
||||||
import { A11yTitleDirective } from "./directives/a11y-title.directive";
|
import { A11yTitleDirective } from "./directives/a11y-title.directive";
|
||||||
import { ApiActionDirective } from "./directives/api-action.directive";
|
import { ApiActionDirective } from "./directives/api-action.directive";
|
||||||
import { AutofocusDirective } from "./directives/autofocus.directive";
|
|
||||||
import { BoxRowDirective } from "./directives/box-row.directive";
|
import { BoxRowDirective } from "./directives/box-row.directive";
|
||||||
import { CopyClickDirective } from "./directives/copy-click.directive";
|
import { CopyClickDirective } from "./directives/copy-click.directive";
|
||||||
import { CopyTextDirective } from "./directives/copy-text.directive";
|
import { CopyTextDirective } from "./directives/copy-text.directive";
|
||||||
|
54
libs/components/src/input/autofocus.directive.ts
Normal file
54
libs/components/src/input/autofocus.directive.ts
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,2 @@
|
|||||||
export * from "./input.module";
|
export * from "./input.module";
|
||||||
|
export * from "./autofocus.directive";
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
<i class="bwi bwi-search bwi-fw tw-text-muted"></i>
|
<i class="bwi bwi-search bwi-fw tw-text-muted"></i>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
#input
|
||||||
bitInput
|
bitInput
|
||||||
type="search"
|
type="search"
|
||||||
[id]="id"
|
[id]="id"
|
||||||
|
@ -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 { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||||
|
|
||||||
|
import { FocusableElement } from "../input/autofocus.directive";
|
||||||
|
|
||||||
let nextId = 0;
|
let nextId = 0;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -12,18 +14,28 @@ let nextId = 0;
|
|||||||
multi: true,
|
multi: true,
|
||||||
useExisting: SearchComponent,
|
useExisting: SearchComponent,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: FocusableElement,
|
||||||
|
useExisting: SearchComponent,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SearchComponent implements ControlValueAccessor {
|
export class SearchComponent implements ControlValueAccessor, FocusableElement {
|
||||||
private notifyOnChange: (v: string) => void;
|
private notifyOnChange: (v: string) => void;
|
||||||
private notifyOnTouch: () => void;
|
private notifyOnTouch: () => void;
|
||||||
|
|
||||||
|
@ViewChild("input") private input: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
protected id = `search-id-${nextId++}`;
|
protected id = `search-id-${nextId++}`;
|
||||||
protected searchText: string;
|
protected searchText: string;
|
||||||
|
|
||||||
@Input() disabled: boolean;
|
@Input() disabled: boolean;
|
||||||
@Input() placeholder: string;
|
@Input() placeholder: string;
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.input.nativeElement.focus();
|
||||||
|
}
|
||||||
|
|
||||||
onChange(searchText: string) {
|
onChange(searchText: string) {
|
||||||
if (this.notifyOnChange != undefined) {
|
if (this.notifyOnChange != undefined) {
|
||||||
this.notifyOnChange(searchText);
|
this.notifyOnChange(searchText);
|
||||||
|
Loading…
Reference in New Issue
Block a user