@@ -36,9 +36,8 @@ import {merge, Observable} from 'rxjs';
36
36
import { CdkMenuGroup } from './menu-group' ;
37
37
import { CdkMenuPanel } from './menu-panel' ;
38
38
import { Menu , CDK_MENU } from './menu-interface' ;
39
- import { throwMissingMenuPanelError } from './menu-errors' ;
40
39
import { CdkMenuItem } from './menu-item' ;
41
- import { MenuStack , MenuStackItem , FocusNext } from './menu-stack' ;
40
+ import { MenuStack , MenuStackItem , FocusNext , NoopMenuStack } from './menu-stack' ;
42
41
import { getItemPointerEntries } from './item-pointer-entries' ;
43
42
44
43
/**
@@ -52,6 +51,7 @@ import {getItemPointerEntries} from './item-pointer-entries';
52
51
selector : '[cdkMenu]' ,
53
52
exportAs : 'cdkMenu' ,
54
53
host : {
54
+ '[tabindex]' : '_isInline() ? 0 : null' ,
55
55
'role' : 'menu' ,
56
56
'class' : 'cdk-menu' ,
57
57
'[attr.aria-orientation]' : 'orientation' ,
@@ -71,8 +71,11 @@ export class CdkMenu extends CdkMenuGroup implements Menu, AfterContentInit, OnI
71
71
/** Event emitted when the menu is closed. */
72
72
@Output ( ) readonly closed : EventEmitter < void | 'click' | 'tab' | 'escape' > = new EventEmitter ( ) ;
73
73
74
+ // We provide a default MenuStack implementation in case the menu is an inline menu.
75
+ // For Menus part of a MenuBar nested within a MenuPanel this will be overwritten
76
+ // to the correct parent MenuStack.
74
77
/** Track the Menus making up the open menu stack. */
75
- _menuStack : MenuStack ;
78
+ _menuStack : MenuStack = new NoopMenuStack ( ) ;
76
79
77
80
/** Handles keyboard events for the menu. */
78
81
private _keyManager : FocusKeyManager < CdkMenuItem > ;
@@ -124,6 +127,11 @@ export class CdkMenu extends CdkMenuGroup implements Menu, AfterContentInit, OnI
124
127
this . _subscribeToMouseManager ( ) ;
125
128
}
126
129
130
+ // In Ivy the `host` metadata will be merged, whereas in ViewEngine it is overridden. In order
131
+ // to avoid double event listeners, we need to use `HostListener`. Once Ivy is the default, we
132
+ // can move this back into `host`.
133
+ // tslint:disable:no-host-decorator-in-concrete
134
+ @HostListener ( 'focus' )
127
135
/** Place focus on the first MenuItem in the menu and set the focus origin. */
128
136
focusFirstItem ( focusOrigin : FocusOrigin = 'program' ) {
129
137
this . _keyManager . setFocusOrigin ( focusOrigin ) ;
@@ -181,12 +189,7 @@ export class CdkMenu extends CdkMenuGroup implements Menu, AfterContentInit, OnI
181
189
182
190
/** Register this menu with its enclosing parent menu panel */
183
191
private _registerWithParentPanel ( ) {
184
- const parent = this . _getMenuPanel ( ) ;
185
- if ( parent ) {
186
- parent . _registerMenu ( this ) ;
187
- } else {
188
- throwMissingMenuPanelError ( ) ;
189
- }
192
+ this . _getMenuPanel ( ) ?. _registerMenu ( this ) ;
190
193
}
191
194
192
195
/**
@@ -323,6 +326,16 @@ export class CdkMenu extends CdkMenuGroup implements Menu, AfterContentInit, OnI
323
326
return this . orientation === 'horizontal' ;
324
327
}
325
328
329
+ /**
330
+ * Return true if this menu is an inline menu. That is, it does not exist in a pop-up and is
331
+ * always visible in the dom.
332
+ */
333
+ _isInline ( ) {
334
+ // NoopMenuStack is the default. If this menu is not inline than the NoopMenuStack is replaced
335
+ // automatically.
336
+ return this . _menuStack instanceof NoopMenuStack ;
337
+ }
338
+
326
339
ngOnDestroy ( ) {
327
340
this . _emitClosedEvent ( ) ;
328
341
}
0 commit comments