mirror of
https://github.com/bitwarden/browser.git
synced 2024-09-27 04:03:00 +02:00
3ffeb684a7
* docs: clarify submit handler in standalone example * docs: clarify protection against re-running actions * docs: clarify that these directives replace click and ngSubmit * docs: clarify `void` * feat: disable action directive on bitsubmit disable * docs: fix grammar * docs: change to note * feat: guard against double running bitAction
68 lines
2.0 KiB
TypeScript
68 lines
2.0 KiB
TypeScript
import { Directive, HostListener, Input, OnDestroy, Optional } from "@angular/core";
|
|
import { BehaviorSubject, finalize, Subject, takeUntil, tap } from "rxjs";
|
|
|
|
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
|
import { ValidationService } from "@bitwarden/common/abstractions/validation.service";
|
|
|
|
import { ButtonLikeAbstraction } from "../shared/button-like.abstraction";
|
|
import { FunctionReturningAwaitable, functionToObservable } from "../utils/function-to-observable";
|
|
|
|
/**
|
|
* Allow a single button to perform async actions on click and reflect the progress in the UI by automatically
|
|
* activating the loading effect while the action is processed.
|
|
*/
|
|
@Directive({
|
|
selector: "[bitAction]",
|
|
})
|
|
export class BitActionDirective implements OnDestroy {
|
|
private destroy$ = new Subject<void>();
|
|
private _loading$ = new BehaviorSubject<boolean>(false);
|
|
|
|
disabled = false;
|
|
|
|
@Input("bitAction") protected handler: FunctionReturningAwaitable;
|
|
|
|
readonly loading$ = this._loading$.asObservable();
|
|
|
|
constructor(
|
|
private buttonComponent: ButtonLikeAbstraction,
|
|
@Optional() private validationService?: ValidationService,
|
|
@Optional() private logService?: LogService
|
|
) {}
|
|
|
|
get loading() {
|
|
return this._loading$.value;
|
|
}
|
|
|
|
set loading(value: boolean) {
|
|
this._loading$.next(value);
|
|
this.buttonComponent.loading = value;
|
|
}
|
|
|
|
@HostListener("click")
|
|
protected async onClick() {
|
|
if (!this.handler || this.loading || this.disabled || this.buttonComponent.disabled) {
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
functionToObservable(this.handler)
|
|
.pipe(
|
|
tap({
|
|
error: (err: unknown) => {
|
|
this.logService?.error(`Async action exception: ${err}`);
|
|
this.validationService?.showError(err);
|
|
},
|
|
}),
|
|
finalize(() => (this.loading = false)),
|
|
takeUntil(this.destroy$)
|
|
)
|
|
.subscribe();
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
this.destroy$.next();
|
|
this.destroy$.complete();
|
|
}
|
|
}
|