Skip to content

Commit

Permalink
[CL-161] Update bit-search support autofocus (#7272)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Hinton committed Jan 11, 2024
1 parent 7112f44 commit 280cb7e
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 33 deletions.
30 changes: 0 additions & 30 deletions libs/angular/src/directives/autofocus.directive.ts

This file was deleted.

3 changes: 2 additions & 1 deletion libs/angular/src/jslib.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
54 changes: 54 additions & 0 deletions libs/components/src/input/autofocus.directive.ts
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions libs/components/src/input/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./input.module";
export * from "./autofocus.directive";
1 change: 1 addition & 0 deletions libs/components/src/search/search.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<i class="bwi bwi-search bwi-fw tw-text-muted"></i>
</label>
<input
#input
bitInput
type="search"
[id]="id"
Expand Down
16 changes: 14 additions & 2 deletions libs/components/src/search/search.component.ts
Original file line number Diff line number Diff line change
@@ -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({
Expand All @@ -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);
Expand Down

0 comments on commit 280cb7e

Please sign in to comment.