mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(dialog): add enter/exit animations (#2825)
* Adds enter/exit animations to the dialog. * Refactors the `MdDialogContainer` and `MdDialogRef` to accommodate the animations. * Fixes some test failures due to the animations. * Allows for the backdrop to be detached before the rest of the overlay, in order to allow for it to be transitioned in parallel. Fixes #2665.
This commit is contained in:
committed by
mmalerba
parent
e3b2486c4b
commit
549222534c
@@ -52,7 +52,7 @@ export class OverlayRef implements PortalHost {
|
||||
* @returns Resolves when the overlay has been detached.
|
||||
*/
|
||||
detach(): Promise<any> {
|
||||
this._detachBackdrop();
|
||||
this.detachBackdrop();
|
||||
|
||||
// When the overlay is detached, the pane element should disable pointer events.
|
||||
// This is necessary because otherwise the pane element will cover the page and disable
|
||||
@@ -70,7 +70,7 @@ export class OverlayRef implements PortalHost {
|
||||
this._state.positionStrategy.dispose();
|
||||
}
|
||||
|
||||
this._detachBackdrop();
|
||||
this.detachBackdrop();
|
||||
this._portalHost.dispose();
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ export class OverlayRef implements PortalHost {
|
||||
}
|
||||
|
||||
/** Detaches the backdrop (if any) associated with the overlay. */
|
||||
private _detachBackdrop(): void {
|
||||
detachBackdrop(): void {
|
||||
let backdropToDetach = this._backdropElement;
|
||||
|
||||
if (backdropToDetach) {
|
||||
|
||||
@@ -5,18 +5,28 @@ import {
|
||||
ViewEncapsulation,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
Renderer,
|
||||
animate,
|
||||
state,
|
||||
style,
|
||||
transition,
|
||||
trigger,
|
||||
AnimationTransitionEvent,
|
||||
EventEmitter,
|
||||
} from '@angular/core';
|
||||
import {BasePortalHost, ComponentPortal, PortalHostDirective, TemplatePortal} from '../core';
|
||||
import {MdDialogConfig} from './dialog-config';
|
||||
import {MdDialogRef} from './dialog-ref';
|
||||
import {MdDialogContentAlreadyAttachedError} from './dialog-errors';
|
||||
import {FocusTrap} from '../core/a11y/focus-trap';
|
||||
import 'rxjs/add/operator/first';
|
||||
|
||||
|
||||
/** Possible states for the dialog container animation. */
|
||||
export type MdDialogContainerAnimationState = 'void' | 'enter' | 'exit' | 'exit-start';
|
||||
|
||||
|
||||
/**
|
||||
* Internal component that wraps user-provided dialog content.
|
||||
* Animation is based on https://material.io/guidelines/motion/choreography.html.
|
||||
* @docs-private
|
||||
*/
|
||||
@Component({
|
||||
@@ -24,11 +34,21 @@ import 'rxjs/add/operator/first';
|
||||
selector: 'md-dialog-container, mat-dialog-container',
|
||||
templateUrl: 'dialog-container.html',
|
||||
styleUrls: ['dialog.css'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
animations: [
|
||||
trigger('slideDialog', [
|
||||
state('void', style({ transform: 'translateY(25%) scale(0.9)', opacity: 0 })),
|
||||
state('enter', style({ transform: 'translateY(0%) scale(1)', opacity: 1 })),
|
||||
state('exit', style({ transform: 'translateY(25%)', opacity: 0 })),
|
||||
transition('* => *', animate('400ms cubic-bezier(0.25, 0.8, 0.25, 1)')),
|
||||
])
|
||||
],
|
||||
host: {
|
||||
'[class.mat-dialog-container]': 'true',
|
||||
'[attr.role]': 'dialogConfig?.role',
|
||||
'[@slideDialog]': '_state',
|
||||
'(@slideDialog.done)': '_onAnimationDone($event)',
|
||||
},
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class MdDialogContainer extends BasePortalHost implements OnDestroy {
|
||||
/** The portal host inside of this container into which the dialog content will be loaded. */
|
||||
@@ -38,15 +58,18 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
|
||||
@ViewChild(FocusTrap) _focusTrap: FocusTrap;
|
||||
|
||||
/** Element that was focused before the dialog was opened. Save this to restore upon close. */
|
||||
private _elementFocusedBeforeDialogWasOpened: Element = null;
|
||||
private _elementFocusedBeforeDialogWasOpened: HTMLElement = null;
|
||||
|
||||
/** The dialog configuration. */
|
||||
dialogConfig: MdDialogConfig;
|
||||
|
||||
/** Reference to the open dialog. */
|
||||
dialogRef: MdDialogRef<any>;
|
||||
/** State of the dialog animation. */
|
||||
_state: MdDialogContainerAnimationState = 'enter';
|
||||
|
||||
constructor(private _ngZone: NgZone, private _renderer: Renderer) {
|
||||
/** Emits the current animation state whenever it changes. */
|
||||
_onAnimationStateChange = new EventEmitter<MdDialogContainerAnimationState>();
|
||||
|
||||
constructor(private _ngZone: NgZone) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -87,20 +110,43 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
|
||||
// ready in instances where change detection has to run first. To deal with this, we simply
|
||||
// wait for the microtask queue to be empty.
|
||||
this._ngZone.onMicrotaskEmpty.first().subscribe(() => {
|
||||
this._elementFocusedBeforeDialogWasOpened = document.activeElement;
|
||||
this._elementFocusedBeforeDialogWasOpened = document.activeElement as HTMLElement;
|
||||
this._focusTrap.focusFirstTabbableElement();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks off the leave animation.
|
||||
* @docs-private
|
||||
*/
|
||||
_exit(): void {
|
||||
this._state = 'exit';
|
||||
this._onAnimationStateChange.emit('exit-start');
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback, invoked whenever an animation on the host completes.
|
||||
* @docs-private
|
||||
*/
|
||||
_onAnimationDone(event: AnimationTransitionEvent) {
|
||||
this._onAnimationStateChange.emit(event.toState as MdDialogContainerAnimationState);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
// When the dialog is destroyed, return focus to the element that originally had it before
|
||||
// the dialog was opened. Wait for the DOM to finish settling before changing the focus so
|
||||
// that it doesn't end up back on the <body>. Also note that we need the extra check, because
|
||||
// IE can set the `activeElement` to null in some cases.
|
||||
if (this._elementFocusedBeforeDialogWasOpened) {
|
||||
this._ngZone.onMicrotaskEmpty.first().subscribe(() => {
|
||||
this._renderer.invokeElementMethod(this._elementFocusedBeforeDialogWasOpened, 'focus');
|
||||
});
|
||||
}
|
||||
this._ngZone.onMicrotaskEmpty.first().subscribe(() => {
|
||||
let toFocus = this._elementFocusedBeforeDialogWasOpened as HTMLElement;
|
||||
|
||||
// We need to check whether the focus method exists at all, because IE seems to throw an
|
||||
// exception, even if the element is the document.body.
|
||||
if (toFocus && 'focus' in toFocus) {
|
||||
toFocus.focus();
|
||||
}
|
||||
|
||||
this._onAnimationStateChange.complete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {OverlayRef} from '../core';
|
||||
import {MdDialogConfig} from './dialog-config';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
import {MdDialogContainer, MdDialogContainerAnimationState} from './dialog-container';
|
||||
|
||||
|
||||
// TODO(jelbourn): resizing
|
||||
@@ -18,16 +18,30 @@ export class MdDialogRef<T> {
|
||||
/** Subject for notifying the user that the dialog has finished closing. */
|
||||
private _afterClosed: Subject<any> = new Subject();
|
||||
|
||||
constructor(private _overlayRef: OverlayRef, public config: MdDialogConfig) { }
|
||||
/** Result to be passed to afterClosed. */
|
||||
private _result: any;
|
||||
|
||||
constructor(private _overlayRef: OverlayRef, public _containerInstance: MdDialogContainer) {
|
||||
_containerInstance._onAnimationStateChange.subscribe(
|
||||
(state: MdDialogContainerAnimationState) => {
|
||||
if (state === 'exit-start') {
|
||||
// Transition the backdrop in parallel with the dialog.
|
||||
this._overlayRef.detachBackdrop();
|
||||
} else if (state === 'exit') {
|
||||
this._overlayRef.dispose();
|
||||
this._afterClosed.next(this._result);
|
||||
this._afterClosed.complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the dialog.
|
||||
* @param dialogResult Optional result to return to the dialog opener.
|
||||
*/
|
||||
close(dialogResult?: any): void {
|
||||
this._overlayRef.dispose();
|
||||
this._afterClosed.next(dialogResult);
|
||||
this._afterClosed.complete();
|
||||
this._result = dialogResult;
|
||||
this._containerInstance._exit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+115
-58
@@ -15,12 +15,13 @@ import {NgModule,
|
||||
Injector,
|
||||
Inject,
|
||||
} from '@angular/core';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {MdDialogModule} from './index';
|
||||
import {MdDialog} from './dialog';
|
||||
import {OverlayContainer} from '../core';
|
||||
import {MdDialogContainer} from './dialog-container';
|
||||
import {OverlayContainer, ESCAPE} from '../core';
|
||||
import {MdDialogRef} from './dialog-ref';
|
||||
import {MD_DIALOG_DATA} from './dialog-injector';
|
||||
import {ESCAPE} from '../core/keyboard/keycodes';
|
||||
|
||||
|
||||
describe('MdDialog', () => {
|
||||
@@ -109,38 +110,35 @@ describe('MdDialog', () => {
|
||||
expect(dialogContainerElement.getAttribute('role')).toBe('alertdialog');
|
||||
});
|
||||
|
||||
it('should close a dialog and get back a result', () => {
|
||||
let dialogRef = dialog.open(PizzaMsg, {
|
||||
viewContainerRef: testViewContainerRef
|
||||
});
|
||||
it('should close a dialog and get back a result', async(() => {
|
||||
let dialogRef = dialog.open(PizzaMsg, { viewContainerRef: testViewContainerRef });
|
||||
let afterCloseCallback = jasmine.createSpy('afterClose callback');
|
||||
|
||||
dialogRef.afterClosed().subscribe(afterCloseCallback);
|
||||
dialogRef.close('Charmander');
|
||||
viewContainerFixture.detectChanges();
|
||||
|
||||
let afterCloseResult: string;
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
afterCloseResult = result;
|
||||
viewContainerFixture.whenStable().then(() => {
|
||||
expect(afterCloseCallback).toHaveBeenCalledWith('Charmander');
|
||||
expect(overlayContainerElement.querySelector('md-dialog-container')).toBeNull();
|
||||
});
|
||||
|
||||
dialogRef.close('Charmander');
|
||||
|
||||
expect(afterCloseResult).toBe('Charmander');
|
||||
expect(overlayContainerElement.querySelector('md-dialog-container')).toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should close a dialog via the escape key', () => {
|
||||
it('should close a dialog via the escape key', async(() => {
|
||||
dialog.open(PizzaMsg, {
|
||||
viewContainerRef: testViewContainerRef
|
||||
});
|
||||
|
||||
dispatchKeydownEvent(document, ESCAPE);
|
||||
viewContainerFixture.detectChanges();
|
||||
|
||||
dispatchKeydownEvent(document, ESCAPE);
|
||||
viewContainerFixture.whenStable().then(() => {
|
||||
expect(overlayContainerElement.querySelector('md-dialog-container')).toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
expect(overlayContainerElement.querySelector('md-dialog-container')).toBeNull();
|
||||
});
|
||||
|
||||
it('should close when clicking on the overlay backdrop', () => {
|
||||
it('should close when clicking on the overlay backdrop', async(() => {
|
||||
dialog.open(PizzaMsg, {
|
||||
viewContainerRef: testViewContainerRef
|
||||
});
|
||||
@@ -148,10 +146,14 @@ describe('MdDialog', () => {
|
||||
viewContainerFixture.detectChanges();
|
||||
|
||||
let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement;
|
||||
backdrop.click();
|
||||
|
||||
expect(overlayContainerElement.querySelector('md-dialog-container')).toBeFalsy();
|
||||
});
|
||||
backdrop.click();
|
||||
viewContainerFixture.detectChanges();
|
||||
|
||||
viewContainerFixture.whenStable().then(() => {
|
||||
expect(overlayContainerElement.querySelector('md-dialog-container')).toBeFalsy();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should notify the observers if a dialog has been opened', () => {
|
||||
let ref: MdDialogRef<PizzaMsg>;
|
||||
@@ -163,7 +165,7 @@ describe('MdDialog', () => {
|
||||
})).toBe(ref);
|
||||
});
|
||||
|
||||
it('should notify the observers if all open dialogs have finished closing', () => {
|
||||
it('should notify the observers if all open dialogs have finished closing', async(() => {
|
||||
const ref1 = dialog.open(PizzaMsg, {
|
||||
viewContainerRef: testViewContainerRef
|
||||
});
|
||||
@@ -177,10 +179,19 @@ describe('MdDialog', () => {
|
||||
});
|
||||
|
||||
ref1.close();
|
||||
expect(allClosed).toBeFalsy();
|
||||
ref2.close();
|
||||
expect(allClosed).toBeTruthy();
|
||||
});
|
||||
viewContainerFixture.detectChanges();
|
||||
|
||||
viewContainerFixture.whenStable().then(() => {
|
||||
expect(allClosed).toBeFalsy();
|
||||
|
||||
ref2.close();
|
||||
viewContainerFixture.detectChanges();
|
||||
|
||||
viewContainerFixture.whenStable().then(() => {
|
||||
expect(allClosed).toBeTruthy();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should should override the width of the overlay pane', () => {
|
||||
dialog.open(PizzaMsg, {
|
||||
@@ -262,7 +273,7 @@ describe('MdDialog', () => {
|
||||
expect(overlayPane.style.marginRight).toBe('125px');
|
||||
});
|
||||
|
||||
it('should close all of the dialogs', () => {
|
||||
it('should close all of the dialogs', async(() => {
|
||||
dialog.open(PizzaMsg);
|
||||
dialog.open(PizzaMsg);
|
||||
dialog.open(PizzaMsg);
|
||||
@@ -270,10 +281,47 @@ describe('MdDialog', () => {
|
||||
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(3);
|
||||
|
||||
dialog.closeAll();
|
||||
viewContainerFixture.detectChanges();
|
||||
|
||||
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0);
|
||||
viewContainerFixture.whenStable().then(() => {
|
||||
expect(overlayContainerElement.querySelectorAll('md-dialog-container').length).toBe(0);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should set the proper animation states', () => {
|
||||
let dialogRef = dialog.open(PizzaMsg, { viewContainerRef: testViewContainerRef });
|
||||
let dialogContainer: MdDialogContainer =
|
||||
viewContainerFixture.debugElement.query(By.directive(MdDialogContainer)).componentInstance;
|
||||
|
||||
expect(dialogContainer._state).toBe('enter');
|
||||
|
||||
dialogRef.close();
|
||||
|
||||
expect(dialogContainer._state).toBe('exit');
|
||||
});
|
||||
|
||||
it('should emit an event with the proper animation state', async(() => {
|
||||
let dialogRef = dialog.open(PizzaMsg, { viewContainerRef: testViewContainerRef });
|
||||
let dialogContainer: MdDialogContainer =
|
||||
viewContainerFixture.debugElement.query(By.directive(MdDialogContainer)).componentInstance;
|
||||
let spy = jasmine.createSpy('animation state callback');
|
||||
|
||||
dialogContainer._onAnimationStateChange.subscribe(spy);
|
||||
viewContainerFixture.detectChanges();
|
||||
|
||||
viewContainerFixture.whenStable().then(() => {
|
||||
expect(spy).toHaveBeenCalledWith('enter');
|
||||
|
||||
dialogRef.close();
|
||||
viewContainerFixture.detectChanges();
|
||||
expect(spy).toHaveBeenCalledWith('exit-start');
|
||||
|
||||
viewContainerFixture.whenStable().then(() => {
|
||||
expect(spy).toHaveBeenCalledWith('exit');
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
describe('passing in data', () => {
|
||||
it('should be able to pass in data', () => {
|
||||
let config = {
|
||||
@@ -318,7 +366,6 @@ describe('MdDialog', () => {
|
||||
});
|
||||
|
||||
viewContainerFixture.detectChanges();
|
||||
|
||||
dispatchKeydownEvent(document, ESCAPE);
|
||||
|
||||
expect(overlayContainerElement.querySelector('md-dialog-container')).toBeTruthy();
|
||||
@@ -385,13 +432,15 @@ describe('MdDialog', () => {
|
||||
viewContainerFixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should close the dialog when clicking on the close button', () => {
|
||||
it('should close the dialog when clicking on the close button', async(() => {
|
||||
expect(overlayContainerElement.querySelectorAll('.mat-dialog-container').length).toBe(1);
|
||||
|
||||
(overlayContainerElement.querySelector('button[md-dialog-close]') as HTMLElement).click();
|
||||
|
||||
expect(overlayContainerElement.querySelectorAll('.mat-dialog-container').length).toBe(0);
|
||||
});
|
||||
viewContainerFixture.whenStable().then(() => {
|
||||
expect(overlayContainerElement.querySelectorAll('.mat-dialog-container').length).toBe(0);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not close the dialog if [md-dialog-close] is applied on a non-button node', () => {
|
||||
expect(overlayContainerElement.querySelectorAll('.mat-dialog-container').length).toBe(1);
|
||||
@@ -401,14 +450,16 @@ describe('MdDialog', () => {
|
||||
expect(overlayContainerElement.querySelectorAll('.mat-dialog-container').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should allow for a user-specified aria-label on the close button', () => {
|
||||
it('should allow for a user-specified aria-label on the close button', async(() => {
|
||||
let button = overlayContainerElement.querySelector('button[md-dialog-close]');
|
||||
|
||||
dialogRef.componentInstance.closeButtonAriaLabel = 'Best close button ever';
|
||||
viewContainerFixture.detectChanges();
|
||||
|
||||
expect(button.getAttribute('aria-label')).toBe('Best close button ever');
|
||||
});
|
||||
viewContainerFixture.whenStable().then(() => {
|
||||
expect(button.getAttribute('aria-label')).toBe('Best close button ever');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should override the "type" attribute of the close button', () => {
|
||||
let button = overlayContainerElement.querySelector('button[md-dialog-close]');
|
||||
@@ -452,33 +503,39 @@ describe('MdDialog with a parent MdDialog', () => {
|
||||
overlayContainerElement.innerHTML = '';
|
||||
});
|
||||
|
||||
it('should close dialogs opened by a parent when calling closeAll on a child MdDialog', () => {
|
||||
parentDialog.open(PizzaMsg);
|
||||
fixture.detectChanges();
|
||||
it('should close dialogs opened by a parent when calling closeAll on a child MdDialog',
|
||||
async(() => {
|
||||
parentDialog.open(PizzaMsg);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(overlayContainerElement.textContent)
|
||||
.toContain('Pizza', 'Expected a dialog to be opened');
|
||||
expect(overlayContainerElement.textContent)
|
||||
.toContain('Pizza', 'Expected a dialog to be opened');
|
||||
|
||||
childDialog.closeAll();
|
||||
fixture.detectChanges();
|
||||
childDialog.closeAll();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(overlayContainerElement.textContent.trim())
|
||||
.toBe('', 'Expected closeAll on child MdDialog to close dialog opened by parent');
|
||||
});
|
||||
fixture.whenStable().then(() => {
|
||||
expect(overlayContainerElement.textContent.trim())
|
||||
.toBe('', 'Expected closeAll on child MdDialog to close dialog opened by parent');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should close dialogs opened by a child when calling closeAll on a parent MdDialog', () => {
|
||||
childDialog.open(PizzaMsg);
|
||||
fixture.detectChanges();
|
||||
it('should close dialogs opened by a child when calling closeAll on a parent MdDialog',
|
||||
async(() => {
|
||||
childDialog.open(PizzaMsg);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(overlayContainerElement.textContent)
|
||||
.toContain('Pizza', 'Expected a dialog to be opened');
|
||||
expect(overlayContainerElement.textContent)
|
||||
.toContain('Pizza', 'Expected a dialog to be opened');
|
||||
|
||||
parentDialog.closeAll();
|
||||
fixture.detectChanges();
|
||||
parentDialog.closeAll();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(overlayContainerElement.textContent.trim())
|
||||
.toBe('', 'Expected closeAll on parent MdDialog to close dialog opened by child');
|
||||
});
|
||||
fixture.whenStable().then(() => {
|
||||
expect(overlayContainerElement.textContent.trim())
|
||||
.toBe('', 'Expected closeAll on parent MdDialog to close dialog opened by child');
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -9,10 +9,7 @@ import {MdDialogConfig} from './dialog-config';
|
||||
import {MdDialogRef} from './dialog-ref';
|
||||
import {MdDialogContainer} from './dialog-container';
|
||||
import {TemplatePortal} from '../core/portal/portal';
|
||||
|
||||
|
||||
// TODO(jelbourn): animations
|
||||
|
||||
import 'rxjs/add/operator/first';
|
||||
|
||||
|
||||
/**
|
||||
@@ -135,16 +132,13 @@ export class MdDialog {
|
||||
config?: MdDialogConfig): MdDialogRef<T> {
|
||||
// Create a reference to the dialog we're creating in order to give the user a handle
|
||||
// to modify and close it.
|
||||
let dialogRef = <MdDialogRef<T>> new MdDialogRef(overlayRef, config);
|
||||
let dialogRef = new MdDialogRef(overlayRef, dialogContainer) as MdDialogRef<T>;
|
||||
|
||||
if (!config.disableClose) {
|
||||
// When the dialog backdrop is clicked, we want to close it.
|
||||
overlayRef.backdropClick().first().subscribe(() => dialogRef.close());
|
||||
}
|
||||
|
||||
// Set the dialogRef to the container so that it can use the ref to close the dialog.
|
||||
dialogContainer.dialogRef = dialogRef;
|
||||
|
||||
// We create an injector specifically for the component we're instantiating so that it can
|
||||
// inject the MdDialogRef. This allows a component loaded inside of a dialog to close itself
|
||||
// and, optionally, to return a value.
|
||||
@@ -217,7 +211,9 @@ export class MdDialog {
|
||||
private _handleKeydown(event: KeyboardEvent): void {
|
||||
let topDialog = this._openDialogs[this._openDialogs.length - 1];
|
||||
|
||||
if (event.keyCode === ESCAPE && topDialog && !topDialog.config.disableClose) {
|
||||
if (event.keyCode === ESCAPE && topDialog &&
|
||||
!topDialog._containerInstance.dialogConfig.disableClose) {
|
||||
|
||||
topDialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user