diff --git a/src/components-examples/material/dialog/dialog-animations/dialog-animations-example-dialog.html b/src/components-examples/material/dialog/dialog-animations/dialog-animations-example-dialog.html new file mode 100644 index 000000000000..1fabf9a6d0c9 --- /dev/null +++ b/src/components-examples/material/dialog/dialog-animations/dialog-animations-example-dialog.html @@ -0,0 +1,8 @@ +

Delete file

+
+ Would you like to delete cat.jpeg? +
+
+ + +
diff --git a/src/components-examples/material/dialog/dialog-animations/dialog-animations-example.css b/src/components-examples/material/dialog/dialog-animations/dialog-animations-example.css new file mode 100644 index 000000000000..83117bf83c68 --- /dev/null +++ b/src/components-examples/material/dialog/dialog-animations/dialog-animations-example.css @@ -0,0 +1,3 @@ +button { + margin-right: 8px; +} diff --git a/src/components-examples/material/dialog/dialog-animations/dialog-animations-example.html b/src/components-examples/material/dialog/dialog-animations/dialog-animations-example.html new file mode 100644 index 000000000000..e50b6eecdcdb --- /dev/null +++ b/src/components-examples/material/dialog/dialog-animations/dialog-animations-example.html @@ -0,0 +1,2 @@ + + diff --git a/src/components-examples/material/dialog/dialog-animations/dialog-animations-example.ts b/src/components-examples/material/dialog/dialog-animations/dialog-animations-example.ts new file mode 100644 index 000000000000..81d23753fee9 --- /dev/null +++ b/src/components-examples/material/dialog/dialog-animations/dialog-animations-example.ts @@ -0,0 +1,30 @@ +import {Component} from '@angular/core'; +import {MatDialog, MatDialogRef} from '@angular/material/dialog'; + +/** + * @title Dialog Animations + */ +@Component({ + selector: 'dialog-animations-example', + styleUrls: ['dialog-animations-example.css'], + templateUrl: 'dialog-animations-example.html', +}) +export class DialogAnimationsExample { + constructor(public dialog: MatDialog) {} + + openDialog(enterAnimationDuration: string, exitAnimationDuration: string): void { + this.dialog.open(DialogAnimationsExampleDialog, { + width: '250px', + enterAnimationDuration, + exitAnimationDuration, + }); + } +} + +@Component({ + selector: 'dialog-animations-example-dialog', + templateUrl: 'dialog-animations-example-dialog.html', +}) +export class DialogAnimationsExampleDialog { + constructor(public dialogRef: MatDialogRef) {} +} diff --git a/src/components-examples/material/dialog/index.ts b/src/components-examples/material/dialog/index.ts index 187a4df2419d..3230cdd52d10 100644 --- a/src/components-examples/material/dialog/index.ts +++ b/src/components-examples/material/dialog/index.ts @@ -23,6 +23,10 @@ import { DialogFromMenuExampleDialog, } from './dialog-from-menu/dialog-from-menu-example'; import {DialogHarnessExample} from './dialog-harness/dialog-harness-example'; +import { + DialogAnimationsExample, + DialogAnimationsExampleDialog, +} from './dialog-animations/dialog-animations-example'; export { DialogContentExample, @@ -36,6 +40,8 @@ export { DialogHarnessExample, DialogOverviewExample, DialogOverviewExampleDialog, + DialogAnimationsExample, + DialogAnimationsExampleDialog, }; const EXAMPLES = [ @@ -50,6 +56,8 @@ const EXAMPLES = [ DialogHarnessExample, DialogOverviewExample, DialogOverviewExampleDialog, + DialogAnimationsExample, + DialogAnimationsExampleDialog, ]; @NgModule({ diff --git a/src/dev-app/dialog/dialog-demo.html b/src/dev-app/dialog/dialog-demo.html index 7605af0d8911..49ad140ae1de 100644 --- a/src/dev-app/dialog/dialog-demo.html +++ b/src/dev-app/dialog/dialog-demo.html @@ -102,6 +102,17 @@

Other options

+

+ + Enter duration + + + + Exit duration + + +

+

Disable close

diff --git a/src/dev-app/dialog/dialog-demo.ts b/src/dev-app/dialog/dialog-demo.ts index cc2ce89bcabd..851f00ecd602 100644 --- a/src/dev-app/dialog/dialog-demo.ts +++ b/src/dev-app/dialog/dialog-demo.ts @@ -31,6 +31,8 @@ export class DialogDemo { height: '', minWidth: '', minHeight: '', + enterAnimationDuration: defaultDialogConfig.enterAnimationDuration, + exitAnimationDuration: defaultDialogConfig.exitAnimationDuration, maxWidth: defaultDialogConfig.maxWidth, maxHeight: '', position: { diff --git a/src/material/dialog/dialog-animations.ts b/src/material/dialog/dialog-animations.ts index 815c140ef9aa..960b4879fc96 100644 --- a/src/material/dialog/dialog-animations.ts +++ b/src/material/dialog/dialog-animations.ts @@ -34,14 +34,17 @@ export const matDialogAnimations: { transition( '* => enter', group([ - animate('150ms cubic-bezier(0, 0, 0.2, 1)', style({transform: 'none', opacity: 1})), + animate( + '{{enterAnimationDuration}} cubic-bezier(0, 0, 0.2, 1)', + style({transform: 'none', opacity: 1}), + ), query('@*', animateChild(), {optional: true}), ]), ), transition( '* => void, * => exit', group([ - animate('75ms cubic-bezier(0.4, 0.0, 0.2, 1)', style({opacity: 0})), + animate('{{exitAnimationDuration}} cubic-bezier(0.4, 0.0, 0.2, 1)', style({opacity: 0})), query('@*', animateChild(), {optional: true}), ]), ), diff --git a/src/material/dialog/dialog-config.ts b/src/material/dialog/dialog-config.ts index 9fa06481bf1f..79854fb8f8f6 100644 --- a/src/material/dialog/dialog-config.ts +++ b/src/material/dialog/dialog-config.ts @@ -126,5 +126,11 @@ export class MatDialogConfig { /** Alternate `ComponentFactoryResolver` to use when resolving the associated component. */ componentFactoryResolver?: ComponentFactoryResolver; + /** Duration of the enter animation. Has to be a valid CSS value (e.g. 100ms). */ + enterAnimationDuration?: string = '150ms'; + + /** Duration of the exit animation. Has to be a valid CSS value (e.g. 50ms). */ + exitAnimationDuration?: string = '75ms'; + // TODO(jelbourn): add configuration for lifecycle hooks, ARIA labelling. } diff --git a/src/material/dialog/dialog-container.ts b/src/material/dialog/dialog-container.ts index 162b3e94d9c8..4e3300f42232 100644 --- a/src/material/dialog/dialog-container.ts +++ b/src/material/dialog/dialog-container.ts @@ -312,7 +312,7 @@ export abstract class _MatDialogContainerBase extends BasePortalOutlet { '[attr.aria-labelledby]': '_config.ariaLabel ? null : _ariaLabelledBy', '[attr.aria-label]': '_config.ariaLabel', '[attr.aria-describedby]': '_config.ariaDescribedBy || null', - '[@dialogContainer]': '_state', + '[@dialogContainer]': `_getAnimationState()`, '(@dialogContainer.start)': '_onAnimationStart($event)', '(@dialogContainer.done)': '_onAnimationDone($event)', }, @@ -360,4 +360,14 @@ export class MatDialogContainer extends _MatDialogContainerBase { this._trapFocus(); } } + + _getAnimationState() { + return { + value: this._state, + params: { + enterAnimationDuration: this._config.enterAnimationDuration || '150ms', + exitAnimationDuration: this._config.exitAnimationDuration || '75ms', + }, + }; + } } diff --git a/src/material/dialog/dialog.md b/src/material/dialog/dialog.md index 6aefedfce50b..49795fc4db2c 100644 --- a/src/material/dialog/dialog.md +++ b/src/material/dialog/dialog.md @@ -118,6 +118,44 @@ You can control which elements are tab stops with the `tabindex` attribute +### Configuring dialog content via `entryComponents` + +Because `MatDialog` instantiates components at run-time, the Angular compiler needs extra +information to create the necessary `ComponentFactory` for your dialog content component. + +For any component loaded into a dialog, you must include your component class in the list of +`entryComponents` in your NgModule definition so that the Angular compiler knows to create +the `ComponentFactory` for it. + +```ts +@NgModule({ + imports: [ + // ... + MatDialogModule + ], + + declarations: [ + AppComponent, + ExampleDialogComponent + ], + + entryComponents: [ + ExampleDialogComponent + ], + + providers: [], + bootstrap: [AppComponent] +}) +export class AppModule {} +``` + +### Controlling the dialog animation +You can control the duration of the dialog's enter and exit animations using the +`enterAnimationDuration` and `exitAnimationDuration` options. If you want to disable the dialog's +animation completely, you can do so by setting the properties to `0ms`. + + + ### Accessibility `MatDialog` creates modal dialogs that implements the ARIA `role="dialog"` pattern by default. diff --git a/src/material/dialog/dialog.spec.ts b/src/material/dialog/dialog.spec.ts index c874e01e4389..63221b92ea1e 100644 --- a/src/material/dialog/dialog.spec.ts +++ b/src/material/dialog/dialog.spec.ts @@ -1959,6 +1959,8 @@ describe('MatDialog with default options', () => { minHeight: '50px', maxWidth: '150px', maxHeight: '150px', + enterAnimationDuration: '100ms', + exitAnimationDuration: '50ms', autoFocus: false, }; diff --git a/tools/public_api_guard/material/dialog.md b/tools/public_api_guard/material/dialog.md index e8cbe9d65967..2a6049b393eb 100644 --- a/tools/public_api_guard/material/dialog.md +++ b/tools/public_api_guard/material/dialog.md @@ -168,6 +168,8 @@ export class MatDialogConfig { delayFocusTrap?: boolean; direction?: Direction; disableClose?: boolean; + enterAnimationDuration?: string; + exitAnimationDuration?: string; hasBackdrop?: boolean; height?: string; id?: string; @@ -186,6 +188,14 @@ export class MatDialogConfig { // @public export class MatDialogContainer extends _MatDialogContainerBase { + // (undocumented) + _getAnimationState(): { + value: "enter" | "void" | "exit"; + params: { + enterAnimationDuration: string; + exitAnimationDuration: string; + }; + }; // (undocumented) _initializeWithAttachedContent(): void; _onAnimationDone({ toState, totalTime }: AnimationEvent_2): void;