@@ -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';
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. */
@@ -90,18 +89,28 @@ const MAT_MENU_BASE_ELEVATION = 2;
90
89
styleUrls : [ 'menu.css' ] ,
91
90
changeDetection : ChangeDetectionStrategy . OnPush ,
92
91
encapsulation : ViewEncapsulation . None ,
92
+ preserveWhitespaces : false ,
93
+ exportAs : 'matMenu' ,
93
94
animations : [
94
95
matMenuAnimations . transformMenu ,
95
96
matMenuAnimations . fadeInItems
96
97
] ,
97
- exportAs : 'matMenu'
98
+ providers : [
99
+ { provide : MAT_MENU_PANEL , useExisting : MatMenu }
100
+ ]
98
101
} )
99
- export class MatMenu implements OnInit , AfterContentInit , MatMenuPanel , OnDestroy {
102
+ export class MatMenu implements OnInit , AfterContentInit , MatMenuPanel < MatMenuItem > , OnDestroy {
100
103
private _keyManager : FocusKeyManager < MatMenuItem > ;
101
104
private _xPosition : MenuPositionX = this . _defaultOptions . xPosition ;
102
105
private _yPosition : MenuPositionY = this . _defaultOptions . yPosition ;
103
106
private _previousElevation : string ;
104
107
108
+ /** Menu items inside the current menu. */
109
+ private _items : MatMenuItem [ ] = [ ] ;
110
+
111
+ /** Emits whenever the amount of menu items changes. */
112
+ private _itemChanges = new Subject < MatMenuItem [ ] > ( ) ;
113
+
105
114
/** Subscription to tab events on the menu panel */
106
115
private _tabSubscription = Subscription . EMPTY ;
107
116
@@ -112,7 +121,10 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
112
121
_panelAnimationState : 'void' | 'enter' = 'void' ;
113
122
114
123
/** Emits whenever an animation on the menu completes. */
115
- _animationDone = new Subject < void > ( ) ;
124
+ _animationDone = new Subject < AnimationEvent > ( ) ;
125
+
126
+ /** Whether the menu is animating. */
127
+ _isAnimating : boolean ;
116
128
117
129
/** Parent menu of the current menu panel. */
118
130
parentMenu : MatMenuPanel | undefined ;
@@ -148,9 +160,6 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
148
160
/** @docs -private */
149
161
@ViewChild ( TemplateRef ) templateRef : TemplateRef < any > ;
150
162
151
- /** List of the items inside of a menu. */
152
- @ContentChildren ( MatMenuItem ) items : QueryList < MatMenuItem > ;
153
-
154
163
/**
155
164
* Menu content that will be rendered lazily.
156
165
* @docs -private
@@ -224,7 +233,7 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
224
233
}
225
234
226
235
ngAfterContentInit ( ) {
227
- this . _keyManager = new FocusKeyManager < MatMenuItem > ( this . items ) . withWrap ( ) . withTypeAhead ( ) ;
236
+ this . _keyManager = new FocusKeyManager < MatMenuItem > ( this . _items ) . withWrap ( ) . withTypeAhead ( ) ;
228
237
this . _tabSubscription = this . _keyManager . tabOut . subscribe ( ( ) => this . close . emit ( 'tab' ) ) ;
229
238
}
230
239
@@ -235,16 +244,10 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
235
244
236
245
/** Stream that emits whenever the hovered menu item changes. */
237
246
_hovered ( ) : Observable < MatMenuItem > {
238
- if ( this . items ) {
239
- return this . items . changes . pipe (
240
- startWith ( this . items ) ,
241
- switchMap ( items => merge ( ...items . map ( item => item . _hovered ) ) )
242
- ) ;
243
- }
244
-
245
- return this . _ngZone . onStable
246
- . asObservable ( )
247
- . pipe ( take ( 1 ) , switchMap ( ( ) => this . _hovered ( ) ) ) ;
247
+ return this . _itemChanges . pipe (
248
+ startWith ( this . _items ) ,
249
+ switchMap ( items => merge ( ...items . map ( item => item . _hovered ) ) )
250
+ ) ;
248
251
}
249
252
250
253
/** Handle a keyboard event from the menu, delegating to the appropriate action. */
@@ -322,6 +325,35 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
322
325
}
323
326
}
324
327
328
+ /**
329
+ * Registers a menu item with the menu.
330
+ * @docs -private
331
+ */
332
+ addItem ( item : MatMenuItem ) {
333
+ // We register the items through this method, rather than picking them up through
334
+ // `ContentChildren`, because we need the items to be picked up by their closest
335
+ // `mat-menu` ancestor. If we used `@ContentChildren(MatMenuItem, {descendants: true})`,
336
+ // all descendant items will bleed into the top-level menu in the case where the consumer
337
+ // has `mat-menu` instances nested inside each other.
338
+ if ( this . _items . indexOf ( item ) === - 1 ) {
339
+ this . _items . push ( item ) ;
340
+ this . _itemChanges . next ( this . _items ) ;
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Removes an item from the menu.
346
+ * @docs -private
347
+ */
348
+ removeItem ( item : MatMenuItem ) {
349
+ const index = this . _items . indexOf ( item ) ;
350
+
351
+ if ( this . _items . indexOf ( item ) > - 1 ) {
352
+ this . _items . splice ( index , 1 ) ;
353
+ this . _itemChanges . next ( this . _items ) ;
354
+ }
355
+ }
356
+
325
357
/** Starts the enter animation. */
326
358
_startAnimation ( ) {
327
359
// @deletion -target 6.0.0 Combine with _resetAnimation.
@@ -335,7 +367,8 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
335
367
}
336
368
337
369
/** Callback that is invoked when the panel animation completes. */
338
- _onAnimationDone ( ) {
339
- this . _animationDone . next ( ) ;
370
+ _onAnimationDone ( event : AnimationEvent ) {
371
+ this . _animationDone . next ( event ) ;
372
+ this . _isAnimating = false ;
340
373
}
341
374
}
0 commit comments