@@ -5,31 +5,51 @@ import {
5
5
ViewEncapsulation ,
6
6
NgZone ,
7
7
OnDestroy ,
8
- Renderer ,
8
+ animate ,
9
+ state ,
10
+ style ,
11
+ transition ,
12
+ trigger ,
13
+ AnimationTransitionEvent ,
14
+ EventEmitter ,
9
15
} from '@angular/core' ;
10
16
import { BasePortalHost , ComponentPortal , PortalHostDirective , TemplatePortal } from '../core' ;
11
17
import { MdDialogConfig } from './dialog-config' ;
12
- import { MdDialogRef } from './dialog-ref' ;
13
18
import { MdDialogContentAlreadyAttachedError } from './dialog-errors' ;
14
19
import { FocusTrap } from '../core/a11y/focus-trap' ;
15
20
import 'rxjs/add/operator/first' ;
16
21
17
22
23
+ /** Possible states for the dialog container animation. */
24
+ export type MdDialogContainerAnimationState = 'void' | 'enter' | 'exit' | 'exit-start' ;
25
+
26
+
18
27
/**
19
28
* Internal component that wraps user-provided dialog content.
29
+ * Animation is based on https://material.io/guidelines/motion/choreography.html.
20
30
* @docs -private
21
31
*/
22
32
@Component ( {
23
33
moduleId : module . id ,
24
34
selector : 'md-dialog-container, mat-dialog-container' ,
25
35
templateUrl : 'dialog-container.html' ,
26
36
styleUrls : [ 'dialog.css' ] ,
37
+ encapsulation : ViewEncapsulation . None ,
38
+ animations : [
39
+ trigger ( 'slideDialog' , [
40
+ state ( 'void' , style ( { transform : 'translateY(25%) scale(0.9)' , opacity : 0 } ) ) ,
41
+ state ( 'enter' , style ( { transform : 'translateY(0%) scale(1)' , opacity : 1 } ) ) ,
42
+ state ( 'exit' , style ( { transform : 'translateY(25%)' , opacity : 0 } ) ) ,
43
+ transition ( '* => *' , animate ( '400ms cubic-bezier(0.25, 0.8, 0.25, 1)' ) ) ,
44
+ ] )
45
+ ] ,
27
46
host : {
28
47
'class' : 'md-dialog-container' ,
29
48
'[attr.role]' : 'dialogConfig?.role' ,
30
- '(keydown.escape)' : 'handleEscapeKey()' ,
49
+ '(keydown.escape)' : '_handleEscapeKey()' ,
50
+ '[@slideDialog]' : '_state' ,
51
+ '(@slideDialog.done)' : '_onAnimationDone($event)' ,
31
52
} ,
32
- encapsulation : ViewEncapsulation . None ,
33
53
} )
34
54
export class MdDialogContainer extends BasePortalHost implements OnDestroy {
35
55
/** The portal host inside of this container into which the dialog content will be loaded. */
@@ -44,10 +64,13 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
44
64
/** The dialog configuration. */
45
65
dialogConfig : MdDialogConfig ;
46
66
47
- /** Reference to the open dialog. */
48
- dialogRef : MdDialogRef < any > ;
67
+ /** State of the dialog animation . */
68
+ _state : MdDialogContainerAnimationState = 'enter' ;
49
69
50
- constructor ( private _ngZone : NgZone , private _renderer : Renderer ) {
70
+ /** Emits the current animation state whenever it changes. */
71
+ _onAnimationStateChange = new EventEmitter < MdDialogContainerAnimationState > ( ) ;
72
+
73
+ constructor ( private _ngZone : NgZone ) {
51
74
super ( ) ;
52
75
}
53
76
@@ -78,25 +101,46 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
78
101
throw Error ( 'Not yet implemented' ) ;
79
102
}
80
103
104
+ ngOnDestroy ( ) {
105
+ // When the dialog is destroyed, return focus to the element that originally had it before
106
+ // the dialog was opened. Wait for the DOM to finish settling before changing the focus so
107
+ // that it doesn't end up back on the <body>.
108
+ this . _ngZone . onMicrotaskEmpty . first ( ) . subscribe ( ( ) => {
109
+ let toFocus = this . _elementFocusedBeforeDialogWasOpened as HTMLElement ;
110
+
111
+ // We need to check whether the focus method exists at all, because IE seems to throw an
112
+ // exception, even if the element is the document.body.
113
+ if ( toFocus && 'focus' in toFocus ) {
114
+ toFocus . focus ( ) ;
115
+ }
116
+ this . _onAnimationStateChange . complete ( ) ;
117
+ } ) ;
118
+ }
119
+
81
120
/**
82
121
* Handles the user pressing the Escape key.
83
122
* @docs -private
84
123
*/
85
- handleEscapeKey ( ) {
124
+ _handleEscapeKey ( ) {
86
125
if ( ! this . dialogConfig . disableClose ) {
87
- this . dialogRef . close ( ) ;
126
+ this . _exit ( ) ;
88
127
}
89
128
}
90
129
91
- ngOnDestroy ( ) {
92
- // When the dialog is destroyed, return focus to the element that originally had it before
93
- // the dialog was opened. Wait for the DOM to finish settling before changing the focus so
94
- // that it doesn't end up back on the <body>. Also note that we need the extra check, because
95
- // IE can set the `activeElement` to null in some cases.
96
- if ( this . _elementFocusedBeforeDialogWasOpened ) {
97
- this . _ngZone . onMicrotaskEmpty . first ( ) . subscribe ( ( ) => {
98
- this . _renderer . invokeElementMethod ( this . _elementFocusedBeforeDialogWasOpened , 'focus' ) ;
99
- } ) ;
100
- }
130
+ /**
131
+ * Kicks off the leave animation.
132
+ * @docs -private
133
+ */
134
+ _exit ( ) : void {
135
+ this . _state = 'exit' ;
136
+ this . _onAnimationStateChange . emit ( 'exit-start' ) ;
137
+ }
138
+
139
+ /**
140
+ * Callback, invoked whenever an animation on the host completes.
141
+ * @docs -private
142
+ */
143
+ _onAnimationDone ( event : AnimationTransitionEvent ) {
144
+ this . _onAnimationStateChange . emit ( event . toState as MdDialogContainerAnimationState ) ;
101
145
}
102
146
}
0 commit comments