1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 00:33:44 +00:00

[EC-558] Reflecting async progress on buttons and forms (#3548)

* [EC-556] feat: convert button into component

* [EC-556] feat: implement loading state

* [EC-556] feat: remove loading from submit button

* [EC-556] fix: add missing import

* [EC-556] fix: disabling button using regular attribute

* [EC-556] feat: implement bitFormButton

* [EC-556] feat: use bitFormButton in submit button

* [EC-556] fix: missing import

* [EC-558] chore: rename file to match class name

* [EC-558] feat: allow skipping bitButton on form buttons

* [EC-558]: only show spinner on submit button

* [EC-558] feat: add new bit async directive

* [EC-558] feat: add functionToObservable util

* [EC-558] feat: implement bitAction directive

* [EC-558] refactor: simplify bitSubmit using functionToObservable

* [EC-558] feat: connect bit action with form button

* [EC-558] feat: execute function immediately to allow for form validation

* [EC-558] feat: disable form on loading

* [EC-558] chore: remove duplicate types

* [EC-558] feat: move validation service to common

* [EC-558] feat: add error handling using validation service

* [EC-558] feat: add support for icon button

* [EC-558] fix: icon button hover border styles

* [EC-558] chore: refactor icon button story to show all styles

* [EC-558] fix: better align loading spinner to middle

* [EC-558] fix: simplify try catch

* [EC-558] chore: reorganize async actions

* [EC-558] chore: rename stories

* [EC-558] docs: add documentation

* [EC-558] feat: decouple buttons and form buttons

* [EC-558] chore: rename button like abstraction

* [EC-558] chore: remove null check

* [EC-558] docs: add jsdocs to directives

* [EC-558] fix: switch abs imports to relative

* [EC-558] chore: add async actions module to web shared module

* [EC-558] chore: remove unecessary null check

* [EC-558] chore: apply suggestions from code review

Co-authored-by: Oscar Hinton <Hinton@users.noreply.github.com>

* [EC-558] fix: whitespaces

* [EC-558] feat: dont disable form by default

* [EC-558] fix: bug where form could be submit during a previous submit

* [EC-558] feat: remove ability to disable form

Co-authored-by: Oscar Hinton <Hinton@users.noreply.github.com>
This commit is contained in:
Andreas Coroiu
2022-10-10 16:04:29 +02:00
committed by GitHub
parent 96c99058c4
commit bb4f063fe7
32 changed files with 955 additions and 65 deletions

View File

@@ -0,0 +1,83 @@
import { Directive, Input, OnDestroy, OnInit, Optional } from "@angular/core";
import { FormGroupDirective } from "@angular/forms";
import { BehaviorSubject, catchError, filter, of, Subject, switchMap, takeUntil } from "rxjs";
import { ValidationService } from "@bitwarden/common/abstractions/validation.service";
import { FunctionReturningAwaitable, functionToObservable } from "../utils/function-to-observable";
/**
* Allow a form to perform async actions on submit, disabling the form while the action is processing.
*/
@Directive({
selector: "[formGroup][bitSubmit]",
})
export class BitSubmitDirective implements OnInit, OnDestroy {
private destroy$ = new Subject<void>();
private _loading$ = new BehaviorSubject<boolean>(false);
private _disabled$ = new BehaviorSubject<boolean>(false);
@Input("bitSubmit") protected handler: FunctionReturningAwaitable;
@Input("disableFormOnLoading") protected disableFormOnLoading = false;
readonly loading$ = this._loading$.asObservable();
readonly disabled$ = this._disabled$.asObservable();
constructor(
private formGroupDirective: FormGroupDirective,
@Optional() validationService?: ValidationService
) {
formGroupDirective.ngSubmit
.pipe(
filter(() => !this.disabled),
switchMap(() => {
// Calling functionToObservable exectues the sync part of the handler
// allowing the function to check form validity before it gets disabled.
const awaitable = functionToObservable(this.handler);
// Disable form
this.loading = true;
return awaitable.pipe(
catchError((err: unknown) => {
validationService?.showError(err);
return of(undefined);
})
);
}),
takeUntil(this.destroy$)
)
.subscribe({
next: () => (this.loading = false),
complete: () => (this.loading = false),
});
}
ngOnInit(): void {
this.formGroupDirective.statusChanges
.pipe(takeUntil(this.destroy$))
.subscribe((c) => this._disabled$.next(c === "DISABLED"));
}
get disabled() {
return this._disabled$.value;
}
set disabled(value: boolean) {
this._disabled$.next(value);
}
get loading() {
return this._loading$.value;
}
set loading(value: boolean) {
this.disabled = value;
this._loading$.next(value);
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}