@@ -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 ,
@@ -35,14 +33,16 @@ import {
35
33
import { Observable } from 'rxjs/Observable' ;
36
34
import { merge } from 'rxjs/observable/merge' ;
37
35
import { Subscription } from 'rxjs/Subscription' ;
36
+ import { Subject } from 'rxjs/Subject' ;
38
37
import { matMenuAnimations } from './menu-animations' ;
39
38
import { throwMatMenuInvalidPositionX , throwMatMenuInvalidPositionY } from './menu-errors' ;
40
39
import { MatMenuItem } from './menu-item' ;
41
- import { MatMenuPanel } from './menu-panel' ;
40
+ import { MatMenuPanel , MAT_MENU_PANEL } from './menu-panel' ;
42
41
import { MatMenuContent } from './menu-content' ;
43
42
import { MenuPositionX , MenuPositionY } from './menu-positions' ;
44
43
import { coerceBooleanProperty } from '@angular/cdk/coercion' ;
45
44
import { FocusOrigin } from '@angular/cdk/a11y' ;
45
+ import { AnimationEvent } from '@angular/animations' ;
46
46
47
47
48
48
/** Default `mat-menu` options that can be overridden. */
@@ -79,18 +79,27 @@ const MAT_MENU_BASE_ELEVATION = 2;
79
79
changeDetection : ChangeDetectionStrategy . OnPush ,
80
80
encapsulation : ViewEncapsulation . None ,
81
81
preserveWhitespaces : false ,
82
+ exportAs : 'matMenu' ,
82
83
animations : [
83
84
matMenuAnimations . transformMenu ,
84
85
matMenuAnimations . fadeInItems
85
86
] ,
86
- exportAs : 'matMenu'
87
+ providers : [
88
+ { provide : MAT_MENU_PANEL , useExisting : MatMenu }
89
+ ]
87
90
} )
88
- export class MatMenu implements OnInit , AfterContentInit , MatMenuPanel , OnDestroy {
91
+ export class MatMenu implements OnInit , AfterContentInit , MatMenuPanel < MatMenuItem > , OnDestroy {
89
92
private _keyManager : FocusKeyManager < MatMenuItem > ;
90
93
private _xPosition : MenuPositionX = this . _defaultOptions . xPosition ;
91
94
private _yPosition : MenuPositionY = this . _defaultOptions . yPosition ;
92
95
private _previousElevation : string ;
93
96
97
+ /** Menu items inside the current menu. */
98
+ private _items : MatMenuItem [ ] = [ ] ;
99
+
100
+ /** Emits whenever the amount of menu items changes. */
101
+ private _itemChanges = new Subject < MatMenuItem [ ] > ( ) ;
102
+
94
103
/** Subscription to tab events on the menu panel */
95
104
private _tabSubscription = Subscription . EMPTY ;
96
105
@@ -100,6 +109,12 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
100
109
/** Current state of the panel animation. */
101
110
_panelAnimationState : 'void' | 'enter' = 'void' ;
102
111
112
+ /** Emits whenever an animation on the menu completes. */
113
+ _animationDone = new Subject < AnimationEvent > ( ) ;
114
+
115
+ /** Whether the menu is animating. */
116
+ _isAnimating : boolean ;
117
+
103
118
/** Parent menu of the current menu panel. */
104
119
parentMenu : MatMenuPanel | undefined ;
105
120
@@ -134,9 +149,6 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
134
149
/** @docs -private */
135
150
@ViewChild ( TemplateRef ) templateRef : TemplateRef < any > ;
136
151
137
- /** List of the items inside of a menu. */
138
- @ContentChildren ( MatMenuItem ) items : QueryList < MatMenuItem > ;
139
-
140
152
/**
141
153
* Menu content that will be rendered lazily.
142
154
* @docs -private
@@ -202,7 +214,7 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
202
214
}
203
215
204
216
ngAfterContentInit ( ) {
205
- this . _keyManager = new FocusKeyManager < MatMenuItem > ( this . items ) . withWrap ( ) . withTypeAhead ( ) ;
217
+ this . _keyManager = new FocusKeyManager < MatMenuItem > ( this . _items ) . withWrap ( ) . withTypeAhead ( ) ;
206
218
this . _tabSubscription = this . _keyManager . tabOut . subscribe ( ( ) => this . close . emit ( 'keydown' ) ) ;
207
219
}
208
220
@@ -213,16 +225,10 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
213
225
214
226
/** Stream that emits whenever the hovered menu item changes. */
215
227
_hovered ( ) : Observable < MatMenuItem > {
216
- if ( this . items ) {
217
- return this . items . changes . pipe (
218
- startWith ( this . items ) ,
219
- switchMap ( items => merge ( ...items . map ( item => item . _hovered ) ) )
220
- ) ;
221
- }
222
-
223
- return this . _ngZone . onStable
224
- . asObservable ( )
225
- . pipe ( take ( 1 ) , switchMap ( ( ) => this . _hovered ( ) ) ) ;
228
+ return this . _itemChanges . pipe (
229
+ startWith ( this . _items ) ,
230
+ switchMap ( items => merge ( ...items . map ( item => item . _hovered ) ) )
231
+ ) ;
226
232
}
227
233
228
234
/** Handle a keyboard event from the menu, delegating to the appropriate action. */
@@ -300,6 +306,35 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
300
306
}
301
307
}
302
308
309
+ /**
310
+ * Registers a menu item with the menu.
311
+ * @docs -private
312
+ */
313
+ addItem ( item : MatMenuItem ) {
314
+ // We register the items through this method, rather than picking them up through
315
+ // `ContentChildren`, because we need the items to be picked up by their closest
316
+ // `mat-menu` ancestor. If we used `@ContentChildren(MatMenuItem, {descendants: true})`,
317
+ // all descendant items will bleed into the top-level menu in the case where the consumer
318
+ // has `mat-menu` instances nested inside each other.
319
+ if ( this . _items . indexOf ( item ) === - 1 ) {
320
+ this . _items . push ( item ) ;
321
+ this . _itemChanges . next ( this . _items ) ;
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Removes an item from the menu.
327
+ * @docs -private
328
+ */
329
+ removeItem ( item : MatMenuItem ) {
330
+ const index = this . _items . indexOf ( item ) ;
331
+
332
+ if ( this . _items . indexOf ( item ) > - 1 ) {
333
+ this . _items . splice ( index , 1 ) ;
334
+ this . _itemChanges . next ( this . _items ) ;
335
+ }
336
+ }
337
+
303
338
/** Starts the enter animation. */
304
339
_startAnimation ( ) {
305
340
// @deletion -target 6.0.0 Combine with _resetAnimation.
@@ -313,7 +348,8 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
313
348
}
314
349
315
350
/** Callback that is invoked when the panel animation completes. */
316
- _onAnimationDone ( _event : AnimationEvent ) {
317
- // @deletion -target 6.0.0 Not being used anymore. To be removed.
351
+ _onAnimationDone ( event : AnimationEvent ) {
352
+ this . _animationDone . next ( event ) ;
353
+ this . _isAnimating = false ;
318
354
}
319
355
}
0 commit comments