diff --git a/src/lib/autocomplete/autocomplete.scss b/src/lib/autocomplete/autocomplete.scss index a72841cbbd5..3a75e9064b1 100644 --- a/src/lib/autocomplete/autocomplete.scss +++ b/src/lib/autocomplete/autocomplete.scss @@ -13,9 +13,9 @@ $mat-autocomplete-panel-below-offset: 6px !default; $mat-autocomplete-panel-above-offset: -24px !default; .mat-autocomplete-panel { - @include mat-menu-base(); + @include mat-menu-base(8); visibility: hidden; - + max-width: none; max-height: $mat-autocomplete-panel-max-height; position: relative; diff --git a/src/lib/core/style/_menu-common.scss b/src/lib/core/style/_menu-common.scss index 6eea82eb1f9..566f5f643a9 100644 --- a/src/lib/core/style/_menu-common.scss +++ b/src/lib/core/style/_menu-common.scss @@ -13,8 +13,8 @@ $mat-menu-side-padding: 16px !default; $mat-menu-icon-margin: 16px !default; -@mixin mat-menu-base() { - @include mat-elevation(8); +@mixin mat-menu-base($elevation) { + @include mat-elevation($elevation); min-width: $mat-menu-overlay-min-width; max-width: $mat-menu-overlay-max-width; diff --git a/src/lib/menu/_menu-theme.scss b/src/lib/menu/_menu-theme.scss index 1c212391cb9..77271e54f87 100644 --- a/src/lib/menu/_menu-theme.scss +++ b/src/lib/menu/_menu-theme.scss @@ -7,7 +7,7 @@ $background: map-get($theme, background); $foreground: map-get($theme, foreground); - .mat-menu-content { + .mat-menu-panel { background: mat-color($background, 'card'); } diff --git a/src/lib/menu/menu-animations.ts b/src/lib/menu/menu-animations.ts index 07a42bf805c..5756deca72c 100644 --- a/src/lib/menu/menu-animations.ts +++ b/src/lib/menu/menu-animations.ts @@ -32,22 +32,23 @@ import{ // TODO(kara): switch to :enter and :leave once Mobile Safari is sorted out. export const transformMenu: AnimationTriggerMetadata = trigger('transformMenu', [ - state('showing', style({ - opacity: 1, - transform: `scale(1)` + state('void', style({ + opacity: 0, + transform: 'scale(0, 0)' })), - transition('void => *', [ - style({ - opacity: 0, - transform: `scale(0)` - }), - animate(`200ms cubic-bezier(0.25, 0.8, 0.25, 1)`) - ]), - transition('* => void', [ - animate('50ms 100ms linear', style({opacity: 0})) - ]) + 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', animate('150ms 50ms linear', style({opacity: 0}))) ]); + /** * This animation fades in the background color and content of the menu panel * after its containing element is scaled in. @@ -56,6 +57,6 @@ export const fadeInItems: AnimationTriggerMetadata = trigger('fadeInItems', [ state('showing', style({opacity: 1})), transition('void => *', [ style({opacity: 0}), - animate(`200ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)`) + animate(`400ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)`) ]) ]); diff --git a/src/lib/menu/menu-directive.ts b/src/lib/menu/menu-directive.ts index 0720e14e30e..ed2e74ad5bc 100644 --- a/src/lib/menu/menu-directive.ts +++ b/src/lib/menu/menu-directive.ts @@ -21,6 +21,7 @@ import { ElementRef, ChangeDetectionStrategy, } from '@angular/core'; +import {AnimationEvent} from '@angular/animations'; import {MenuPositionX, MenuPositionY} from './menu-positions'; import {throwMdMenuInvalidPositionX, throwMdMenuInvalidPositionY} from './menu-errors'; import {MdMenuItem} from './menu-item'; @@ -55,6 +56,9 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy { /** Config object to be passed into the menu's ngClass */ _classList: any = {}; + /** Current state of the panel animation. */ + _panelAnimationState: 'void' | 'enter-start' | 'enter' = 'void'; + /** Position of the menu in the X axis. */ @Input() get xPosition() { return this._xPosition; } @@ -161,4 +165,21 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy { this._classList['mat-menu-below'] = posY === 'below'; } + /** Starts the enter animation. */ + _startAnimation() { + this._panelAnimationState = 'enter-start'; + } + + /** Resets the panel animation to its initial state. */ + _resetAnimation() { + 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'; + } + } } diff --git a/src/lib/menu/menu-trigger.ts b/src/lib/menu/menu-trigger.ts index e70c4265805..d1f6e3f3718 100644 --- a/src/lib/menu/menu-trigger.ts +++ b/src/lib/menu/menu-trigger.ts @@ -33,6 +33,7 @@ import { } from '../core'; import {Subscription} from 'rxjs/Subscription'; import {MenuPositionX, MenuPositionY} from './menu-positions'; +import {MdMenu} from './menu-directive'; // TODO(andrewseguin): Remove the kebab versions in favor of camelCased attribute selectors @@ -110,6 +111,10 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { this._createOverlay().attach(this._portal); this._subscribeToBackdrop(); this._initMenu(); + + if (this.menu instanceof MdMenu) { + this.menu._startAnimation(); + } } } @@ -119,6 +124,10 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { this._overlayRef.detach(); this._backdropSubscription.unsubscribe(); this._resetMenu(); + + if (this.menu instanceof MdMenu) { + this.menu._resetAnimation(); + } } } diff --git a/src/lib/menu/menu.html b/src/lib/menu/menu.html index a4f41ded140..1dd7193f47c 100644 --- a/src/lib/menu/menu.html +++ b/src/lib/menu/menu.html @@ -1,6 +1,12 @@ -