1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-03 10:13:31 +00:00

move animation done logic to service

This commit is contained in:
Bryan Cunningham
2025-11-07 16:04:33 -05:00
parent 5b12e190d0
commit 2ff2cf67d6
4 changed files with 41 additions and 9 deletions

View File

@@ -7,7 +7,7 @@ import {
} from "@angular/cdk/dialog";
import { ComponentType, ScrollStrategy } from "@angular/cdk/overlay";
import { ComponentPortal, Portal } from "@angular/cdk/portal";
import { Injectable, Injector, TemplateRef, inject } from "@angular/core";
import { Injectable, Injector, TemplateRef, inject, signal } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { NavigationEnd, Router } from "@angular/router";
import { filter, firstValueFrom, map, Observable, Subject, switchMap } from "rxjs";
@@ -18,6 +18,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { DrawerService } from "../drawer/drawer.service";
import { ANIMATION_IN_DURATION, ANIMATION_OUT_DURATION } from "./animations";
import { SimpleConfigurableDialogComponent } from "./simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component";
import { SimpleDialogOptions } from "./simple-dialog/types";
@@ -132,6 +133,9 @@ export class DialogService {
private defaultScrollStrategy = new CustomBlockScrollStrategy();
private activeDrawer: DrawerDialogRef<any, any> | null = null;
/** Signal to control when focus trap auto-capture should be enabled after animation */
readonly animationDone = signal(false);
constructor() {
/**
* TODO: This logic should exist outside of `libs/components`.
@@ -177,6 +181,10 @@ export class DialogService {
};
ref.cdkDialogRefBase = this.dialog.open<R, D, C>(componentOrTemplateRef, _config);
// Handle focus timing after animation completes
this.setupFocusTiming();
return ref;
}
@@ -198,6 +206,10 @@ export class DialogService {
);
this.activeDrawer.portal = portal;
this.drawerService.open(portal);
// Handle focus timing after animation completes
this.setupFocusTiming();
return this.activeDrawer;
}
@@ -254,4 +266,20 @@ export class DialogService {
parent: this.injector,
});
}
/**
* Sets up the focus timing for a dialog component after the animation completes.
* This ensures the focus trap and autofocus only activate after the entrance animation finishes.
*/
private setupFocusTiming(): void {
// Reset the signal to false for the new dialog
this.animationDone.set(false);
const totalDuration = Math.max(ANIMATION_IN_DURATION, ANIMATION_OUT_DURATION);
// Set to true after animation completes
setTimeout(() => {
this.animationDone.set(true);
}, totalDuration);
}
}

View File

@@ -3,7 +3,6 @@
class="tw-flex tw-w-full tw-flex-col tw-self-center tw-overflow-hidden tw-border tw-border-solid tw-border-secondary-100 tw-bg-background tw-text-main"
[ngClass]="[width, isDrawer ? 'tw-h-screen tw-border-t-0' : 'tw-rounded-xl tw-shadow-lg']"
[@dialogAnimation]="isDrawer ? 'drawer' : 'dialog'"
(@dialogAnimation.done)="onAnimationDone()"
cdkTrapFocus
[cdkTrapFocusAutoCapture]="animationDone()"
>

View File

@@ -9,7 +9,6 @@ import {
input,
booleanAttribute,
ElementRef,
signal,
} from "@angular/core";
import { toObservable } from "@angular/core/rxjs-interop";
import { combineLatest, switchMap } from "rxjs";
@@ -22,7 +21,7 @@ import { TypographyDirective } from "../../typography/typography.directive";
import { hasScrollableContent$ } from "../../utils/";
import { hasScrolledFrom } from "../../utils/has-scrolled-from";
import { dialogAnimation } from "../animations";
import { DialogRef } from "../dialog.service";
import { DialogRef, DialogService } from "../dialog.service";
import { DialogCloseDirective } from "../directives/dialog-close.directive";
import { DialogTitleContainerDirective } from "../directives/dialog-title-container.directive";
@@ -52,8 +51,9 @@ export class DialogComponent {
private readonly scrollBottom = viewChild.required<ElementRef<HTMLDivElement>>("scrollBottom");
protected dialogRef = inject(DialogRef, { optional: true });
private dialogService = inject(DialogService);
protected bodyHasScrolledFrom = hasScrolledFrom(this.scrollableBody);
protected readonly animationDone = signal(false);
protected readonly animationDone = this.dialogService.animationDone;
private scrollableBody$ = toObservable(this.scrollableBody);
private scrollBottom$ = toObservable(this.scrollBottom);
@@ -111,10 +111,6 @@ export class DialogComponent {
}
}
onAnimationDone() {
this.animationDone.set(true);
}
get width() {
switch (this.dialogSize()) {
case "small": {

View File

@@ -0,0 +1,9 @@
import { NgModule } from "@angular/core";
import { SwitchComponent } from "./switch.component";
@NgModule({
imports: [SwitchComponent],
exports: [SwitchComponent],
})
export class SwitchModule {}