Skip to content

Commit f826805

Browse files
crisbetojelbourn
authored andcommitted
refactor: export component animations for reuse (#8971)
Exports all of the component animations so they can be reused by consumers. Fixes #8904.
1 parent 21d004a commit f826805

35 files changed

+524
-315
lines changed

src/lib/dialog/dialog-animations.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import {
9+
animate,
10+
state,
11+
style,
12+
transition,
13+
trigger,
14+
AnimationTriggerMetadata,
15+
} from '@angular/animations';
16+
17+
/** Animations used by MatDialog. */
18+
export const matDialogAnimations: {
19+
readonly slideDialog: AnimationTriggerMetadata;
20+
} = {
21+
/** Animation that slides the dialog in and out of view and fades the opacity. */
22+
slideDialog: trigger('slideDialog', [
23+
// Note: The `enter` animation doesn't transition to something like `translate3d(0, 0, 0)
24+
// scale(1)`, because for some reason specifying the transform explicitly, causes IE both
25+
// to blur the dialog content and decimate the animation performance. Leaving it as `none`
26+
// solves both issues.
27+
state('enter', style({ transform: 'none', opacity: 1 })),
28+
state('void', style({ transform: 'translate3d(0, 25%, 0) scale(0.9)', opacity: 0 })),
29+
state('exit', style({ transform: 'translate3d(0, 25%, 0)', opacity: 0 })),
30+
transition('* => *', animate('400ms cubic-bezier(0.25, 0.8, 0.25, 1)')),
31+
])
32+
};

src/lib/dialog/dialog-container.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ import {
1919
ViewEncapsulation,
2020
ChangeDetectionStrategy,
2121
} from '@angular/core';
22-
import {animate, AnimationEvent, state, style, transition, trigger} from '@angular/animations';
2322
import {DOCUMENT} from '@angular/common';
23+
import {AnimationEvent} from '@angular/animations';
24+
import {matDialogAnimations} from './dialog-animations';
2425
import {
2526
BasePortalOutlet,
2627
ComponentPortal,
@@ -55,18 +56,7 @@ export function throwMatDialogContentAlreadyAttachedError() {
5556
// Using OnPush for dialogs caused some G3 sync issues. Disabled until we can track them down.
5657
// tslint:disable-next-line:validate-decorators
5758
changeDetection: ChangeDetectionStrategy.Default,
58-
animations: [
59-
trigger('slideDialog', [
60-
// Note: The `enter` animation doesn't transition to something like `translate3d(0, 0, 0)
61-
// scale(1)`, because for some reason specifying the transform explicitly, causes IE both
62-
// to blur the dialog content and decimate the animation performance. Leaving it as `none`
63-
// solves both issues.
64-
state('enter', style({ transform: 'none', opacity: 1 })),
65-
state('void', style({ transform: 'translate3d(0, 25%, 0) scale(0.9)', opacity: 0 })),
66-
state('exit', style({ transform: 'translate3d(0, 25%, 0)', opacity: 0 })),
67-
transition('* => *', animate('400ms cubic-bezier(0.25, 0.8, 0.25, 1)')),
68-
])
69-
],
59+
animations: [matDialogAnimations.slideDialog],
7060
host: {
7161
'class': 'mat-dialog-container',
7262
'tabindex': '-1',

src/lib/dialog/public-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ export * from './dialog-container';
1212
export * from './dialog-content-directives';
1313
export * from './dialog-config';
1414
export * from './dialog-ref';
15-
15+
export * from './dialog-animations';
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import {
9+
animate,
10+
state,
11+
style,
12+
transition,
13+
trigger,
14+
AnimationTriggerMetadata,
15+
} from '@angular/animations';
16+
17+
/** Time and timing curve for expansion panel animations. */
18+
export const EXPANSION_PANEL_ANIMATION_TIMING = '225ms cubic-bezier(0.4,0.0,0.2,1)';
19+
20+
/** Animations used by the Material expansion panel. */
21+
export const matExpansionAnimations: {
22+
readonly indicatorRotate: AnimationTriggerMetadata;
23+
readonly expansionHeaderHeight: AnimationTriggerMetadata;
24+
readonly bodyExpansion: AnimationTriggerMetadata;
25+
} = {
26+
/** Animation that rotates the indicator arrow. */
27+
indicatorRotate: trigger('indicatorRotate', [
28+
state('collapsed', style({transform: 'rotate(0deg)'})),
29+
state('expanded', style({transform: 'rotate(180deg)'})),
30+
transition('expanded <=> collapsed', animate(EXPANSION_PANEL_ANIMATION_TIMING)),
31+
]),
32+
33+
/** Animation that expands and collapses the panel header height. */
34+
expansionHeaderHeight: trigger('expansionHeight', [
35+
state('collapsed', style({
36+
height: '{{collapsedHeight}}',
37+
}), {
38+
params: {collapsedHeight: '48px'},
39+
}),
40+
state('expanded', style({
41+
height: '{{expandedHeight}}'
42+
}), {
43+
params: {expandedHeight: '64px'}
44+
}),
45+
transition('expanded <=> collapsed', animate(EXPANSION_PANEL_ANIMATION_TIMING)),
46+
]),
47+
48+
/** Animation that expands and collapses the panel content. */
49+
bodyExpansion: trigger('bodyExpansion', [
50+
state('collapsed', style({height: '0px', visibility: 'hidden'})),
51+
state('expanded', style({height: '*', visibility: 'visible'})),
52+
transition('expanded <=> collapsed', animate(EXPANSION_PANEL_ANIMATION_TIMING)),
53+
])
54+
};

src/lib/expansion/expansion-panel-header.ts

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {animate, state, style, transition, trigger} from '@angular/animations';
109
import {FocusMonitor} from '@angular/cdk/a11y';
1110
import {ENTER, SPACE} from '@angular/cdk/keycodes';
1211
import {filter} from 'rxjs/operators/filter';
@@ -23,7 +22,8 @@ import {
2322
} from '@angular/core';
2423
import {merge} from 'rxjs/observable/merge';
2524
import {Subscription} from 'rxjs/Subscription';
26-
import {EXPANSION_PANEL_ANIMATION_TIMING, MatExpansionPanel} from './expansion-panel';
25+
import {MatExpansionPanel} from './expansion-panel';
26+
import {matExpansionAnimations} from './expansion-animations';
2727

2828

2929
/**
@@ -41,6 +41,10 @@ import {EXPANSION_PANEL_ANIMATION_TIMING, MatExpansionPanel} from './expansion-p
4141
encapsulation: ViewEncapsulation.None,
4242
preserveWhitespaces: false,
4343
changeDetection: ChangeDetectionStrategy.OnPush,
44+
animations: [
45+
matExpansionAnimations.indicatorRotate,
46+
matExpansionAnimations.expansionHeaderHeight
47+
],
4448
host: {
4549
'class': 'mat-expansion-panel-header',
4650
'role': 'button',
@@ -59,26 +63,6 @@ import {EXPANSION_PANEL_ANIMATION_TIMING, MatExpansionPanel} from './expansion-p
5963
}
6064
}`,
6165
},
62-
animations: [
63-
trigger('indicatorRotate', [
64-
state('collapsed', style({transform: 'rotate(0deg)'})),
65-
state('expanded', style({transform: 'rotate(180deg)'})),
66-
transition('expanded <=> collapsed', animate(EXPANSION_PANEL_ANIMATION_TIMING)),
67-
]),
68-
trigger('expansionHeight', [
69-
state('collapsed', style({
70-
height: '{{collapsedHeight}}',
71-
}), {
72-
params: {collapsedHeight: '48px'},
73-
}),
74-
state('expanded', style({
75-
height: '{{expandedHeight}}'
76-
}), {
77-
params: {expandedHeight: '64px'}
78-
}),
79-
transition('expanded <=> collapsed', animate(EXPANSION_PANEL_ANIMATION_TIMING)),
80-
]),
81-
],
8266
})
8367
export class MatExpansionPanelHeader implements OnDestroy {
8468
private _parentChangeSubscription = Subscription.EMPTY;

src/lib/expansion/expansion-panel.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {animate, state, style, transition, trigger} from '@angular/animations';
109
import {
1110
ChangeDetectionStrategy,
1211
ChangeDetectorRef,
@@ -35,9 +34,7 @@ import {startWith} from 'rxjs/operators/startWith';
3534
import {MatAccordion} from './accordion';
3635
import {coerceBooleanProperty} from '@angular/cdk/coercion';
3736
import {MatExpansionPanelContent} from './expansion-panel-content';
38-
39-
/** Time and timing curve for expansion panel animations. */
40-
export const EXPANSION_PANEL_ANIMATION_TIMING = '225ms cubic-bezier(0.4,0.0,0.2,1)';
37+
import {matExpansionAnimations} from './expansion-animations';
4138

4239
// Boilerplate for applying mixins to MatExpansionPanel.
4340
/** @docs-private */
@@ -79,6 +76,7 @@ export type MatExpansionPanelState = 'expanded' | 'collapsed';
7976
changeDetection: ChangeDetectionStrategy.OnPush,
8077
inputs: ['disabled', 'expanded'],
8178
outputs: ['opened', 'closed'],
79+
animations: [matExpansionAnimations.bodyExpansion],
8280
host: {
8381
'class': 'mat-expansion-panel',
8482
'[class.mat-expanded]': 'expanded',
@@ -87,13 +85,6 @@ export type MatExpansionPanelState = 'expanded' | 'collapsed';
8785
providers: [
8886
{provide: _MatExpansionPanelMixinBase, useExisting: forwardRef(() => MatExpansionPanel)}
8987
],
90-
animations: [
91-
trigger('bodyExpansion', [
92-
state('collapsed', style({height: '0px', visibility: 'hidden'})),
93-
state('expanded', style({height: '*', visibility: 'visible'})),
94-
transition('expanded <=> collapsed', animate(EXPANSION_PANEL_ANIMATION_TIMING)),
95-
]),
96-
],
9788
})
9889
export class MatExpansionPanel extends _MatExpansionPanelMixinBase
9990
implements CanDisable, AfterContentInit, OnChanges, OnDestroy {

src/lib/expansion/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ export * from './accordion';
1111
export * from './expansion-panel';
1212
export * from './expansion-panel-header';
1313
export * from './expansion-panel-content';
14+
export * from './expansion-animations';
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import {
9+
animate,
10+
state,
11+
style,
12+
transition,
13+
trigger,
14+
AnimationTriggerMetadata,
15+
} from '@angular/animations';
16+
17+
/** Animations used by the MatFormField. */
18+
export const matFormFieldAnimations: {
19+
readonly transitionMessages: AnimationTriggerMetadata
20+
} = {
21+
/** Animation that transitions the form field's error and hint messages. */
22+
transitionMessages: trigger('transitionMessages', [
23+
// TODO(mmalerba): Use angular animations for label animation as well.
24+
state('enter', style({ opacity: 1, transform: 'translateY(0%)' })),
25+
transition('void => enter', [
26+
style({ opacity: 0, transform: 'translateY(-100%)' }),
27+
animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)'),
28+
]),
29+
])
30+
};

src/lib/form-field/form-field.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {animate, state, style, transition, trigger} from '@angular/animations';
109
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1110
import {take} from 'rxjs/operators/take';
1211
import {startWith} from 'rxjs/operators/startWith';
@@ -41,6 +40,7 @@ import {MatPlaceholder} from './placeholder';
4140
import {MatLabel} from './label';
4241
import {MatPrefix} from './prefix';
4342
import {MatSuffix} from './suffix';
43+
import {matFormFieldAnimations} from './form-field-animations';
4444

4545

4646
let nextUniqueId = 0;
@@ -57,16 +57,7 @@ let nextUniqueId = 0;
5757
// The MatInput styles are fairly minimal so it shouldn't be a big deal for people who
5858
// aren't using MatInput.
5959
styleUrls: ['form-field.css', '../input/input.css'],
60-
animations: [
61-
// TODO(mmalerba): Use angular animations for label animation as well.
62-
trigger('transitionMessages', [
63-
state('enter', style({ opacity: 1, transform: 'translateY(0%)' })),
64-
transition('void => enter', [
65-
style({ opacity: 0, transform: 'translateY(-100%)' }),
66-
animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)'),
67-
]),
68-
]),
69-
],
60+
animations: [matFormFieldAnimations.transitionMessages],
7061
host: {
7162
'class': 'mat-input-container mat-form-field',
7263
'[class.mat-input-invalid]': '_control.errorState',

src/lib/form-field/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ export * from './placeholder';
1616
export * from './prefix';
1717
export * from './suffix';
1818
export * from './label';
19+
export * from './form-field-animations';

src/lib/menu/menu-animations.ts

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,50 +16,58 @@ import{
1616
} from '@angular/animations';
1717

1818
/**
19-
* Below are all the animations for the mat-menu component.
19+
* Animations used by the mat-menu component.
2020
* Animation duration and timing values are based on:
2121
* https://material.io/guidelines/components/menus.html#menus-usage
2222
*/
23+
export const matMenuAnimations: {
24+
readonly transformMenu: AnimationTriggerMetadata;
25+
readonly fadeInItems: AnimationTriggerMetadata;
26+
} = {
27+
/**
28+
* This animation controls the menu panel's entry and exit from the page.
29+
*
30+
* When the menu panel is added to the DOM, it scales in and fades in its border.
31+
*
32+
* When the menu panel is removed from the DOM, it simply fades out after a brief
33+
* delay to display the ripple.
34+
*/
35+
transformMenu: trigger('transformMenu', [
36+
// TODO(kara): switch to :enter and :leave once Mobile Safari is sorted out.
37+
state('void', style({
38+
opacity: 0,
39+
// This starts off from 0.01, instead of 0, because there's an issue in the Angular animations
40+
// as of 4.2, which causes the animation to be skipped if it starts from 0.
41+
transform: 'scale(0.01, 0.01)'
42+
})),
43+
state('enter-start', style({
44+
opacity: 1,
45+
transform: 'scale(1, 0.5)'
46+
})),
47+
state('enter', style({
48+
transform: 'scale(1, 1)'
49+
})),
50+
transition('void => enter-start', animate('100ms linear')),
51+
transition('enter-start => enter', animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)')),
52+
transition('* => void', animate('150ms 50ms linear', style({opacity: 0})))
53+
]),
2354

2455

25-
/**
26-
* This animation controls the menu panel's entry and exit from the page.
27-
*
28-
* When the menu panel is added to the DOM, it scales in and fades in its border.
29-
*
30-
* When the menu panel is removed from the DOM, it simply fades out after a brief
31-
* delay to display the ripple.
32-
*/
33-
34-
// TODO(kara): switch to :enter and :leave once Mobile Safari is sorted out.
35-
export const transformMenu: AnimationTriggerMetadata = trigger('transformMenu', [
36-
state('void', style({
37-
opacity: 0,
38-
// This starts off from 0.01, instead of 0, because there's an issue in the Angular animations
39-
// as of 4.2, which causes the animation to be skipped if it starts from 0.
40-
transform: 'scale(0.01, 0.01)'
41-
})),
42-
state('enter-start', style({
43-
opacity: 1,
44-
transform: 'scale(1, 0.5)'
45-
})),
46-
state('enter', style({
47-
transform: 'scale(1, 1)'
48-
})),
49-
transition('void => enter-start', animate('100ms linear')),
50-
transition('enter-start => enter', animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)')),
51-
transition('* => void', animate('150ms 50ms linear', style({opacity: 0})))
52-
]);
56+
/**
57+
* This animation fades in the background color and content of the menu panel
58+
* after its containing element is scaled in.
59+
*/
60+
fadeInItems: trigger('fadeInItems', [
61+
state('showing', style({opacity: 1})),
62+
transition('void => *', [
63+
style({opacity: 0}),
64+
animate('400ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)')
65+
])
66+
])
67+
};
5368

69+
/** @deprecated */
70+
export const fadeInItems = matMenuAnimations.fadeInItems;
5471

55-
/**
56-
* This animation fades in the background color and content of the menu panel
57-
* after its containing element is scaled in.
58-
*/
59-
export const fadeInItems: AnimationTriggerMetadata = trigger('fadeInItems', [
60-
state('showing', style({opacity: 1})),
61-
transition('void => *', [
62-
style({opacity: 0}),
63-
animate('400ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)')
64-
])
65-
]);
72+
/** @deprecated */
73+
export const transformMenu = matMenuAnimations.transformMenu;

0 commit comments

Comments
 (0)