@@ -17,15 +17,13 @@ import {
17
17
ChangeDetectionStrategy ,
18
18
Component ,
19
19
ContentChild ,
20
- ContentChildren ,
21
20
ElementRef ,
22
21
EventEmitter ,
23
22
Inject ,
24
23
InjectionToken ,
25
24
Input ,
26
25
OnDestroy ,
27
26
Output ,
28
- QueryList ,
29
27
TemplateRef ,
30
28
ViewChild ,
31
29
ViewEncapsulation ,
@@ -39,11 +37,12 @@ import {Subscription} from 'rxjs/Subscription';
39
37
import { matMenuAnimations } from './menu-animations' ;
40
38
import { throwMatMenuInvalidPositionX , throwMatMenuInvalidPositionY } from './menu-errors' ;
41
39
import { MatMenuItem } from './menu-item' ;
42
- import { MatMenuPanel } from './menu-panel' ;
40
+ import { MatMenuPanel , MAT_MENU_PANEL } from './menu-panel' ;
43
41
import { MatMenuContent } from './menu-content' ;
44
42
import { MenuPositionX , MenuPositionY } from './menu-positions' ;
45
43
import { coerceBooleanProperty } from '@angular/cdk/coercion' ;
46
44
import { FocusOrigin } from '@angular/cdk/a11y' ;
45
+ import { AnimationEvent } from '@angular/animations' ;
47
46
48
47
49
48
/** Default `mat-menu` options that can be overridden. */
@@ -82,18 +81,28 @@ const MAT_MENU_BASE_ELEVATION = 2;
82
81
styleUrls : [ 'menu.css' ] ,
83
82
changeDetection : ChangeDetectionStrategy . OnPush ,
84
83
encapsulation : ViewEncapsulation . None ,
84
+ preserveWhitespaces : false ,
85
+ exportAs : 'matMenu' ,
85
86
animations : [
86
87
matMenuAnimations . transformMenu ,
87
88
matMenuAnimations . fadeInItems
88
89
] ,
89
- exportAs : 'matMenu'
90
+ providers : [
91
+ { provide : MAT_MENU_PANEL , useExisting : MatMenu }
92
+ ]
90
93
} )
91
- export class MatMenu implements OnInit , AfterContentInit , MatMenuPanel , OnDestroy {
94
+ export class MatMenu implements OnInit , AfterContentInit , MatMenuPanel < MatMenuItem > , OnDestroy {
92
95
private _keyManager : FocusKeyManager < MatMenuItem > ;
93
96
private _xPosition : MenuPositionX = this . _defaultOptions . xPosition ;
94
97
private _yPosition : MenuPositionY = this . _defaultOptions . yPosition ;
95
98
private _previousElevation : string ;
96
99
100
+ /** Menu items inside the current menu. */
101
+ private _items : MatMenuItem [ ] = [ ] ;
102
+
103
+ /** Emits whenever the amount of menu items changes. */
104
+ private _itemChanges = new Subject < MatMenuItem [ ] > ( ) ;
105
+
97
106
/** Subscription to tab events on the menu panel */
98
107
private _tabSubscription = Subscription . EMPTY ;
99
108
@@ -104,7 +113,10 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
104
113
_panelAnimationState : 'void' | 'enter' = 'void' ;
105
114
106
115
/** Emits whenever an animation on the menu completes. */
107
- _animationDone = new Subject < void > ( ) ;
116
+ _animationDone = new Subject < AnimationEvent > ( ) ;
117
+
118
+ /** Whether the menu is animating. */
119
+ _isAnimating : boolean ;
108
120
109
121
/** Parent menu of the current menu panel. */
110
122
parentMenu : MatMenuPanel | undefined ;
@@ -140,9 +152,6 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
140
152
/** @docs -private */
141
153
@ViewChild ( TemplateRef ) templateRef : TemplateRef < any > ;
142
154
143
- /** List of the items inside of a menu. */
144
- @ContentChildren ( MatMenuItem ) items : QueryList < MatMenuItem > ;
145
-
146
155
/**
147
156
* Menu content that will be rendered lazily.
148
157
* @docs -private
@@ -216,7 +225,7 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
216
225
}
217
226
218
227
ngAfterContentInit ( ) {
219
- this . _keyManager = new FocusKeyManager < MatMenuItem > ( this . items ) . withWrap ( ) . withTypeAhead ( ) ;
228
+ this . _keyManager = new FocusKeyManager < MatMenuItem > ( this . _items ) . withWrap ( ) . withTypeAhead ( ) ;
220
229
this . _tabSubscription = this . _keyManager . tabOut . subscribe ( ( ) => this . close . emit ( 'tab' ) ) ;
221
230
}
222
231
@@ -227,16 +236,10 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
227
236
228
237
/** Stream that emits whenever the hovered menu item changes. */
229
238
_hovered ( ) : Observable < MatMenuItem > {
230
- if ( this . items ) {
231
- return this . items . changes . pipe (
232
- startWith ( this . items ) ,
233
- switchMap ( items => merge ( ...items . map ( item => item . _hovered ) ) )
234
- ) ;
235
- }
236
-
237
- return this . _ngZone . onStable
238
- . asObservable ( )
239
- . pipe ( take ( 1 ) , switchMap ( ( ) => this . _hovered ( ) ) ) ;
239
+ return this . _itemChanges . pipe (
240
+ startWith ( this . _items ) ,
241
+ switchMap ( items => merge ( ...items . map ( item => item . _hovered ) ) )
242
+ ) ;
240
243
}
241
244
242
245
/** Handle a keyboard event from the menu, delegating to the appropriate action. */
@@ -314,6 +317,35 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
314
317
}
315
318
}
316
319
320
+ /**
321
+ * Registers a menu item with the menu.
322
+ * @docs -private
323
+ */
324
+ addItem ( item : MatMenuItem ) {
325
+ // We register the items through this method, rather than picking them up through
326
+ // `ContentChildren`, because we need the items to be picked up by their closest
327
+ // `mat-menu` ancestor. If we used `@ContentChildren(MatMenuItem, {descendants: true})`,
328
+ // all descendant items will bleed into the top-level menu in the case where the consumer
329
+ // has `mat-menu` instances nested inside each other.
330
+ if ( this . _items . indexOf ( item ) === - 1 ) {
331
+ this . _items . push ( item ) ;
332
+ this . _itemChanges . next ( this . _items ) ;
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Removes an item from the menu.
338
+ * @docs -private
339
+ */
340
+ removeItem ( item : MatMenuItem ) {
341
+ const index = this . _items . indexOf ( item ) ;
342
+
343
+ if ( this . _items . indexOf ( item ) > - 1 ) {
344
+ this . _items . splice ( index , 1 ) ;
345
+ this . _itemChanges . next ( this . _items ) ;
346
+ }
347
+ }
348
+
317
349
/** Starts the enter animation. */
318
350
_startAnimation ( ) {
319
351
// @deletion -target 6.0.0 Combine with _resetAnimation.
@@ -327,7 +359,8 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
327
359
}
328
360
329
361
/** Callback that is invoked when the panel animation completes. */
330
- _onAnimationDone ( ) {
331
- this . _animationDone . next ( ) ;
362
+ _onAnimationDone ( event : AnimationEvent ) {
363
+ this . _animationDone . next ( event ) ;
364
+ this . _isAnimating = false ;
332
365
}
333
366
}
0 commit comments