@@ -12,10 +12,12 @@ import {
12
12
Inject ,
13
13
Injectable ,
14
14
InjectionToken ,
15
+ Injector ,
15
16
Input ,
16
17
OnDestroy ,
17
18
Optional ,
18
19
Output ,
20
+ TemplateRef ,
19
21
ViewContainerRef ,
20
22
} from '@angular/core' ;
21
23
import { Directionality } from '@angular/cdk/bidi' ;
@@ -30,10 +32,9 @@ import {Portal, TemplatePortal} from '@angular/cdk/portal';
30
32
import { BooleanInput , coerceBooleanProperty } from '@angular/cdk/coercion' ;
31
33
import { merge , partition , Subject } from 'rxjs' ;
32
34
import { skip , takeUntil } from 'rxjs/operators' ;
33
- import { CdkMenuPanel } from './menu-panel' ;
34
- import { MenuStack } from './menu-stack' ;
35
- import { throwExistingMenuStackError } from './menu-errors' ;
35
+ import { MENU_STACK , MenuStack } from './menu-stack' ;
36
36
import { isClickInsideMenuOverlay } from './menu-item-trigger' ;
37
+ import { MENU_TRIGGER , MenuTrigger } from './menu-trigger' ;
37
38
38
39
/** Tracks the last open context menu trigger across the entire application. */
39
40
@Injectable ( { providedIn : 'root' } )
@@ -85,26 +86,14 @@ export type ContextMenuCoordinates = {x: number; y: number};
85
86
// In cases where the first menu item in the context menu is a trigger the submenu opens on a
86
87
// hover event. Offsetting the opened context menu by 2px prevents this from occurring.
87
88
{ provide : CDK_CONTEXT_MENU_DEFAULT_OPTIONS , useValue : { offsetX : 2 , offsetY : 2 } } ,
89
+ { provide : MENU_TRIGGER , useExisting : CdkContextMenuTrigger } ,
90
+ { provide : MENU_STACK , useClass : MenuStack } ,
88
91
] ,
89
92
} )
90
- export class CdkContextMenuTrigger implements OnDestroy {
93
+ export class CdkContextMenuTrigger extends MenuTrigger implements OnDestroy {
91
94
/** Template reference variable to the menu to open on right click. */
92
95
@Input ( 'cdkContextMenuTriggerFor' )
93
- get menuPanel ( ) : CdkMenuPanel {
94
- return this . _menuPanel ;
95
- }
96
- set menuPanel ( panel : CdkMenuPanel ) {
97
- if ( ( typeof ngDevMode === 'undefined' || ngDevMode ) && panel . _menuStack ) {
98
- throwExistingMenuStackError ( ) ;
99
- }
100
- this . _menuPanel = panel ;
101
-
102
- if ( this . _menuPanel ) {
103
- this . _menuPanel . _menuStack = this . _menuStack ;
104
- }
105
- }
106
- /** Reference to the MenuPanel this trigger toggles. */
107
- private _menuPanel : CdkMenuPanel ;
96
+ private _menuTemplateRef : TemplateRef < unknown > ;
108
97
109
98
/** Emits when the attached menu is requested to open. */
110
99
@Output ( 'cdkContextMenuOpened' ) readonly opened : EventEmitter < void > = new EventEmitter ( ) ;
@@ -126,24 +115,24 @@ export class CdkContextMenuTrigger implements OnDestroy {
126
115
private _overlayRef : OverlayRef | null = null ;
127
116
128
117
/** The content of the menu panel opened by this trigger. */
129
- private _panelContent : TemplatePortal ;
118
+ private _menuPortal : TemplatePortal ;
130
119
131
120
/** Emits when the element is destroyed. */
132
121
private readonly _destroyed = new Subject < void > ( ) ;
133
122
134
- /** The menu stack for this trigger and its associated menus. */
135
- private readonly _menuStack = new MenuStack ( ) ;
136
-
137
123
/** Emits when the outside pointer events listener on the overlay should be stopped. */
138
124
private readonly _stopOutsideClicksListener = merge ( this . closed , this . _destroyed ) ;
139
125
140
126
constructor (
127
+ injector : Injector ,
141
128
protected readonly _viewContainerRef : ViewContainerRef ,
142
129
private readonly _overlay : Overlay ,
143
130
private readonly _contextMenuTracker : ContextMenuTracker ,
131
+ @Inject ( MENU_STACK ) menuStack : MenuStack ,
144
132
@Inject ( CDK_CONTEXT_MENU_DEFAULT_OPTIONS ) private readonly _options : ContextMenuOptions ,
145
133
@Optional ( ) private readonly _directionality ?: Directionality ,
146
134
) {
135
+ super ( injector , menuStack ) ;
147
136
this . _setMenuStackListener ( ) ;
148
137
}
149
138
@@ -161,7 +150,7 @@ export class CdkContextMenuTrigger implements OnDestroy {
161
150
} else if ( this . isOpen ( ) ) {
162
151
// since we're moving this menu we need to close any submenus first otherwise they end up
163
152
// disconnected from this one.
164
- this . _menuStack . closeSubMenuOf ( this . _menuPanel . _menu ! ) ;
153
+ this . menuStack . closeSubMenuOf ( this . childMenu ! ) ;
165
154
166
155
(
167
156
this . _overlayRef ! . getConfig ( ) . positionStrategy as FlexibleConnectedPositionStrategy
@@ -186,7 +175,7 @@ export class CdkContextMenuTrigger implements OnDestroy {
186
175
187
176
/** Close the opened menu. */
188
177
close ( ) {
189
- this . _menuStack . closeAll ( ) ;
178
+ this . menuStack . closeAll ( ) ;
190
179
}
191
180
192
181
/**
@@ -208,11 +197,11 @@ export class CdkContextMenuTrigger implements OnDestroy {
208
197
209
198
// A context menu can be triggered via a mouse right click or a keyboard shortcut.
210
199
if ( event . button === 2 ) {
211
- this . _menuPanel . _menu ?. focusFirstItem ( 'mouse' ) ;
200
+ this . childMenu ?. focusFirstItem ( 'mouse' ) ;
212
201
} else if ( event . button === 0 ) {
213
- this . _menuPanel . _menu ?. focusFirstItem ( 'keyboard' ) ;
202
+ this . childMenu ?. focusFirstItem ( 'keyboard' ) ;
214
203
} else {
215
- this . _menuPanel . _menu ?. focusFirstItem ( 'program' ) ;
204
+ this . childMenu ?. focusFirstItem ( 'program' ) ;
216
205
}
217
206
}
218
207
}
@@ -267,18 +256,23 @@ export class CdkContextMenuTrigger implements OnDestroy {
267
256
* content to change dynamically and be reflected in the application.
268
257
*/
269
258
private _getMenuContent ( ) : Portal < unknown > {
270
- const hasMenuContentChanged = this . menuPanel . _templateRef !== this . _panelContent ?. templateRef ;
271
- if ( this . menuPanel && ( ! this . _panelContent || hasMenuContentChanged ) ) {
272
- this . _panelContent = new TemplatePortal ( this . menuPanel . _templateRef , this . _viewContainerRef ) ;
259
+ const hasMenuContentChanged = this . _menuTemplateRef !== this . _menuPortal ?. templateRef ;
260
+ if ( this . _menuTemplateRef && ( ! this . _menuPortal || hasMenuContentChanged ) ) {
261
+ this . _menuPortal = new TemplatePortal (
262
+ this . _menuTemplateRef ,
263
+ this . _viewContainerRef ,
264
+ undefined ,
265
+ this . getChildMenuInjector ( ) ,
266
+ ) ;
273
267
}
274
268
275
- return this . _panelContent ;
269
+ return this . _menuPortal ;
276
270
}
277
271
278
272
/** Subscribe to the menu stack close events and close this menu when requested. */
279
273
private _setMenuStackListener ( ) {
280
- this . _menuStack . closed . pipe ( takeUntil ( this . _destroyed ) ) . subscribe ( item => {
281
- if ( item === this . _menuPanel . _menu && this . isOpen ( ) ) {
274
+ this . menuStack . closed . pipe ( takeUntil ( this . _destroyed ) ) . subscribe ( item => {
275
+ if ( item === this . childMenu && this . isOpen ( ) ) {
282
276
this . closed . next ( ) ;
283
277
this . _overlayRef ! . detach ( ) ;
284
278
}
@@ -300,15 +294,14 @@ export class CdkContextMenuTrigger implements OnDestroy {
300
294
}
301
295
outsideClicks . pipe ( takeUntil ( this . _stopOutsideClicksListener ) ) . subscribe ( event => {
302
296
if ( ! isClickInsideMenuOverlay ( event . target as Element ) ) {
303
- this . _menuStack . closeAll ( ) ;
297
+ this . menuStack . closeAll ( ) ;
304
298
}
305
299
} ) ;
306
300
}
307
301
}
308
302
309
303
ngOnDestroy ( ) {
310
304
this . _destroyOverlay ( ) ;
311
- this . _resetPanelMenuStack ( ) ;
312
305
313
306
this . _destroyed . next ( ) ;
314
307
this . _destroyed . complete ( ) ;
@@ -321,17 +314,4 @@ export class CdkContextMenuTrigger implements OnDestroy {
321
314
this . _overlayRef = null ;
322
315
}
323
316
}
324
-
325
- /** Set the menu panels menu stack back to null. */
326
- private _resetPanelMenuStack ( ) {
327
- // If a ContextMenuTrigger is placed in a conditionally rendered view, each time the trigger is
328
- // rendered the panel setter for ContextMenuTrigger is called. From the first render onward,
329
- // the attached CdkMenuPanel has the MenuStack set. Since we throw an error if a panel already
330
- // has a stack set, we want to reset the attached stack here to prevent the error from being
331
- // thrown if the trigger re-configures its attached panel (in the case where there is a 1:1
332
- // relationship between the panel and trigger).
333
- if ( this . _menuPanel ) {
334
- this . _menuPanel . _menuStack = null ;
335
- }
336
- }
337
317
}
0 commit comments