From 342e3c688e44a5a27d35e2c876e451851a4e89b5 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 20 Feb 2018 19:21:42 +0100 Subject: [PATCH] fix(expansion-panel,menu,select): nested animations not working (#9134) Fixes issues with the expansion panel, menu and select animations where the child animations were being blocked by the parent ones. **Note:** the menu animations needed a bit more refactoring since the old approach wasn't very idiomatic and made it harder to run the parallel animations. Fixes #8814. Fixes #8953. --- src/lib/expansion/expansion-animations.ts | 8 +++++++- src/lib/menu/menu-animations.ts | 25 ++++++++++++++--------- src/lib/menu/menu-directive.ts | 21 +++++++++++-------- src/lib/menu/menu.html | 3 +-- src/lib/menu/menu.scss | 14 ++++++------- src/lib/select/select-animations.ts | 8 ++++++-- 6 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/lib/expansion/expansion-animations.ts b/src/lib/expansion/expansion-animations.ts index e638ec247cb9..47e243f9d1ee 100644 --- a/src/lib/expansion/expansion-animations.ts +++ b/src/lib/expansion/expansion-animations.ts @@ -7,10 +7,13 @@ */ import { animate, + animateChild, + group, state, style, transition, trigger, + query, AnimationTriggerMetadata, } from '@angular/animations'; @@ -42,7 +45,10 @@ export const matExpansionAnimations: { }), { params: {expandedHeight: '64px'} }), - transition('expanded <=> collapsed', animate(EXPANSION_PANEL_ANIMATION_TIMING)), + transition('expanded <=> collapsed', group([ + query('@indicatorRotate', animateChild(), {optional: true}), + animate(EXPANSION_PANEL_ANIMATION_TIMING), + ])), ]), /** Animation that expands and collapses the panel content. */ diff --git a/src/lib/menu/menu-animations.ts b/src/lib/menu/menu-animations.ts index 253d619d351d..76705e2f3ce2 100644 --- a/src/lib/menu/menu-animations.ts +++ b/src/lib/menu/menu-animations.ts @@ -12,6 +12,9 @@ import{ style, animate, transition, + query, + group, + sequence, AnimationTriggerMetadata, } from '@angular/animations'; @@ -33,22 +36,22 @@ export const matMenuAnimations: { * delay to display the ripple. */ transformMenu: trigger('transformMenu', [ - // TODO(kara): switch to :enter and :leave once Mobile Safari is sorted out. state('void', style({ opacity: 0, // This starts off from 0.01, instead of 0, because there's an issue in the Angular animations // as of 4.2, which causes the animation to be skipped if it starts from 0. transform: 'scale(0.01, 0.01)' })), - state('enter-start', style({ - opacity: 1, - transform: 'scale(1, 0.5)' - })), - state('enter', style({ - transform: 'scale(1, 1)' - })), - transition('void => enter-start', animate('100ms linear')), - transition('enter-start => enter', animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)')), + transition('void => enter', sequence([ + query('.mat-menu-content', style({opacity: 0})), + animate('100ms linear', style({opacity: 1, transform: 'scale(1, 0.5)'})), + group([ + query('.mat-menu-content', animate('400ms cubic-bezier(0.55, 0, 0.55, 0.2)', + style({opacity: 1}) + )), + animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({transform: 'scale(1, 1)'})), + ]) + ])), transition('* => void', animate('150ms 50ms linear', style({opacity: 0}))) ]), @@ -58,6 +61,8 @@ export const matMenuAnimations: { * after its containing element is scaled in. */ fadeInItems: trigger('fadeInItems', [ + // TODO(crisbeto): this is inside the `transformMenu` + // now. Remove next time we do breaking changes. state('showing', style({opacity: 1})), transition('void => *', [ style({opacity: 0}), diff --git a/src/lib/menu/menu-directive.ts b/src/lib/menu/menu-directive.ts index 9cc38eef767d..a88cfae94061 100644 --- a/src/lib/menu/menu-directive.ts +++ b/src/lib/menu/menu-directive.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AnimationEvent} from '@angular/animations'; import {FocusKeyManager} from '@angular/cdk/a11y'; import {Direction} from '@angular/cdk/bidi'; import {ESCAPE, LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes'; @@ -31,6 +30,7 @@ import { ViewChild, ViewEncapsulation, NgZone, + OnInit, } from '@angular/core'; import {Observable} from 'rxjs/Observable'; import {merge} from 'rxjs/observable/merge'; @@ -82,7 +82,7 @@ const MAT_MENU_BASE_ELEVATION = 2; ], exportAs: 'matMenu' }) -export class MatMenu implements AfterContentInit, MatMenuPanel, OnDestroy { +export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestroy { private _keyManager: FocusKeyManager; private _xPosition: MenuPositionX = this._defaultOptions.xPosition; private _yPosition: MenuPositionY = this._defaultOptions.yPosition; @@ -95,7 +95,7 @@ export class MatMenu implements AfterContentInit, MatMenuPanel, OnDestroy { _classList: {[key: string]: boolean} = {}; /** Current state of the panel animation. */ - _panelAnimationState: 'void' | 'enter-start' | 'enter' = 'void'; + _panelAnimationState: 'void' | 'enter' = 'void'; /** Parent menu of the current menu panel. */ parentMenu: MatMenuPanel | undefined; @@ -191,6 +191,10 @@ export class MatMenu implements AfterContentInit, MatMenuPanel, OnDestroy { private _ngZone: NgZone, @Inject(MAT_MENU_DEFAULT_OPTIONS) private _defaultOptions: MatMenuDefaultOptions) { } + ngOnInit() { + this.setPositionClasses(); + } + ngAfterContentInit() { this._keyManager = new FocusKeyManager(this.items).withWrap().withTypeAhead(); this._tabSubscription = this._keyManager.tabOut.subscribe(() => this.close.emit('keydown')); @@ -292,19 +296,18 @@ export class MatMenu implements AfterContentInit, MatMenuPanel, OnDestroy { /** Starts the enter animation. */ _startAnimation() { - this._panelAnimationState = 'enter-start'; + // @deletion-target 6.0.0 Combine with _resetAnimation. + this._panelAnimationState = 'enter'; } /** Resets the panel animation to its initial state. */ _resetAnimation() { + // @deletion-target 6.0.0 Combine with _startAnimation. this._panelAnimationState = 'void'; } /** Callback that is invoked when the panel animation completes. */ - _onAnimationDone(event: AnimationEvent) { - // After the initial expansion is done, trigger the second phase of the enter animation. - if (event.toState === 'enter-start') { - this._panelAnimationState = 'enter'; - } + _onAnimationDone(_event: AnimationEvent) { + // @deletion-target 6.0.0 Not being used anymore. To be removed. } } diff --git a/src/lib/menu/menu.html b/src/lib/menu/menu.html index 6c3450a52fcf..fb226b437e35 100644 --- a/src/lib/menu/menu.html +++ b/src/lib/menu/menu.html @@ -5,10 +5,9 @@ (keydown)="_handleKeydown($event)" (click)="closed.emit('click')" [@transformMenu]="_panelAnimationState" - (@transformMenu.done)="_onAnimationDone($event)" tabindex="-1" role="menu"> -
+
diff --git a/src/lib/menu/menu.scss b/src/lib/menu/menu.scss index f82fcfcac2c7..043ca34909a2 100644 --- a/src/lib/menu/menu.scss +++ b/src/lib/menu/menu.scss @@ -16,13 +16,6 @@ $mat-menu-submenu-indicator-size: 10px !default; border-radius: $mat-menu-border-radius; outline: 0; - // Prevent the user from interacting while the panel is still animating. - // This avoids issues where the user could accidentally open a sub-menu, - // because of the `overlapTrigger` option. - &.ng-animating { - pointer-events: none; - } - @include cdk-high-contrast { outline: solid 1px; } @@ -70,6 +63,13 @@ $mat-menu-submenu-indicator-size: 10px !default; transform: rotateY(180deg) translateY(-50%); } } + + // Prevent the user from interacting while the panel is still animating. + // This avoids issues where the user could accidentally open a sub-menu, + // because of the `overlapTrigger` option. + .mat-menu-panel.ng-animating & { + pointer-events: none; + } } button.mat-menu-item { diff --git a/src/lib/select/select-animations.ts b/src/lib/select/select-animations.ts index 89690102b5a3..756f12f4abaa 100644 --- a/src/lib/select/select-animations.ts +++ b/src/lib/select/select-animations.ts @@ -13,6 +13,9 @@ import { style, transition, trigger, + query, + animateChild, + group, } from '@angular/animations'; /** @@ -45,14 +48,15 @@ export const matSelectAnimations: { minWidth: 'calc(100% + 64px)', // 64px = 48px padding on the left + 16px padding on the right transform: 'scaleY(1)' })), - transition('void => *', [ + transition('void => *', group([ + query('@fadeInContent', animateChild()), style({ opacity: 0, minWidth: '100%', transform: 'scaleY(0)' }), animate('150ms cubic-bezier(0.25, 0.8, 0.25, 1)') - ]), + ])), transition('* => void', [ animate('250ms 100ms linear', style({opacity: 0})) ])