Skip to content

Commit cc4fc11

Browse files
crisbetoandrewseguin
authored andcommitted
fix(dialog): don't block other dialogs from opening while animating (#8051)
The behavior where we don't allow new dialogs to be opened while a dialog is animating was introduced initially in #5769 to avoid issues where the user might open multiple dialogs by accident if they press enter one too many times. This is problematic because it also means that the consumer is blocked from opening dialogs for certain 300ms intervals. These changes approach the issue differently by moving focus onto the dialog container immediately while it's animating. Fixes #6560.
1 parent d17f9d2 commit cc4fc11

File tree

3 files changed

+8
-26
lines changed

3 files changed

+8
-26
lines changed

src/lib/dialog/dialog-container.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,6 @@ export class MatDialogContainer extends BasePortalOutlet {
100100
/** ID of the element that should be considered as the dialog's label. */
101101
_ariaLabelledBy: string | null = null;
102102

103-
/** Whether the container is currently mid-animation. */
104-
_isAnimating = false;
105-
106103
constructor(
107104
private _elementRef: ElementRef,
108105
private _focusTrapFactory: FocusTrapFactory,
@@ -147,13 +144,7 @@ export class MatDialogContainer extends BasePortalOutlet {
147144
// If were to attempt to focus immediately, then the content of the dialog would not yet be
148145
// ready in instances where change detection has to run first. To deal with this, we simply
149146
// wait for the microtask queue to be empty.
150-
this._focusTrap.focusInitialElementWhenReady().then(hasMovedFocus => {
151-
// If we didn't find any focusable elements inside the dialog, focus the
152-
// container so the user can't tab into other elements behind it.
153-
if (!hasMovedFocus) {
154-
this._elementRef.nativeElement.focus();
155-
}
156-
});
147+
this._focusTrap.focusInitialElementWhenReady();
157148
}
158149

159150
/** Restores focus to the element that was focused before the dialog opened. */
@@ -174,6 +165,11 @@ export class MatDialogContainer extends BasePortalOutlet {
174165
private _savePreviouslyFocusedElement() {
175166
if (this._document) {
176167
this._elementFocusedBeforeDialogWasOpened = this._document.activeElement as HTMLElement;
168+
169+
// Move focus onto the dialog immediately in order to prevent the user from accidentally
170+
// opening multiple dialogs at the same time. Needs to be async, because the element
171+
// may not be focusable immediately.
172+
Promise.resolve().then(() => this._elementRef.nativeElement.focus());
177173
}
178174
}
179175

@@ -186,12 +182,10 @@ export class MatDialogContainer extends BasePortalOutlet {
186182
}
187183

188184
this._animationStateChanged.emit(event);
189-
this._isAnimating = false;
190185
}
191186

192187
/** Callback, invoked when an animation on the host starts. */
193188
_onAnimationStart(event: AnimationEvent) {
194-
this._isAnimating = true;
195189
this._animationStateChanged.emit(event);
196190
}
197191

src/lib/dialog/dialog-ref.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,6 @@ export class MatDialogRef<T> {
161161
return this;
162162
}
163163

164-
/** Returns whether the dialog is animating. */
165-
_isAnimating(): boolean {
166-
return this._containerInstance._isAnimating;
167-
}
168-
169164
/** Fetches the position strategy object from the overlay ref. */
170165
private _getPositionStrategy(): GlobalPositionStrategy {
171166
return this._overlayRef.getConfig().positionStrategy as GlobalPositionStrategy;

src/lib/dialog/dialog.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,6 @@ export class MatDialog {
117117
open<T, D = any>(componentOrTemplateRef: ComponentType<T> | TemplateRef<T>,
118118
config?: MatDialogConfig<D>): MatDialogRef<T> {
119119

120-
const inProgressDialog = this.openDialogs.find(dialog => dialog._isAnimating());
121-
122-
// If there's a dialog that is in the process of being opened, return it instead.
123-
if (inProgressDialog) {
124-
return inProgressDialog;
125-
}
126-
127120
config = _applyConfigDefaults(config);
128121

129122
if (config.id && this.getDialogById(config.id)) {
@@ -133,7 +126,7 @@ export class MatDialog {
133126
const overlayRef = this._createOverlay(config);
134127
const dialogContainer = this._attachDialogContainer(overlayRef, config);
135128
const dialogRef =
136-
this._attachDialogContent(componentOrTemplateRef, dialogContainer, overlayRef, config);
129+
this._attachDialogContent<T>(componentOrTemplateRef, dialogContainer, overlayRef, config);
137130

138131
this.openDialogs.push(dialogRef);
139132
dialogRef.afterClosed().subscribe(() => this._removeOpenDialog(dialogRef));
@@ -253,7 +246,7 @@ export class MatDialog {
253246
<any>{ $implicit: config.data, dialogRef }));
254247
} else {
255248
const injector = this._createInjector<T>(config, dialogRef, dialogContainer);
256-
const contentRef = dialogContainer.attachComponentPortal(
249+
const contentRef = dialogContainer.attachComponentPortal<T>(
257250
new ComponentPortal(componentOrTemplateRef, undefined, injector));
258251
dialogRef.componentInstance = contentRef.instance;
259252
}

0 commit comments

Comments
 (0)