@@ -35,9 +35,8 @@ import {merge, Observable} from 'rxjs';
35
35
import { CdkMenuGroup } from './menu-group' ;
36
36
import { CdkMenuPanel } from './menu-panel' ;
37
37
import { Menu , CDK_MENU } from './menu-interface' ;
38
- import { throwMissingMenuPanelError } from './menu-errors' ;
39
38
import { CdkMenuItem } from './menu-item' ;
40
- import { MenuStack , MenuStackItem , FocusNext } from './menu-stack' ;
39
+ import { MenuStack , MenuStackItem , FocusNext , NoopMenuStack } from './menu-stack' ;
41
40
import { getItemPointerEntries } from './item-pointer-entries' ;
42
41
43
42
/**
@@ -52,6 +51,8 @@ import {getItemPointerEntries} from './item-pointer-entries';
52
51
exportAs : 'cdkMenu' ,
53
52
host : {
54
53
'(keydown)' : '_handleKeyEvent($event)' ,
54
+ '(focus)' : '_focusFirstItem()' ,
55
+ '[tabindex]' : '_isInline() ? 0 : null' ,
55
56
'role' : 'menu' ,
56
57
'class' : 'cdk-menu' ,
57
58
'[attr.aria-orientation]' : 'orientation' ,
@@ -71,8 +72,11 @@ export class CdkMenu extends CdkMenuGroup implements Menu, AfterContentInit, OnI
71
72
/** Event emitted when the menu is closed. */
72
73
@Output ( ) readonly closed : EventEmitter < void | 'click' | 'tab' | 'escape' > = new EventEmitter ( ) ;
73
74
75
+ // We provide a default MenuStack implementation in case the menu is an inline menu.
76
+ // For Menus part of a MenuBar nested within a MenuPanel this will be overwritten
77
+ // to the correct parent MenuStack.
74
78
/** Track the Menus making up the open menu stack. */
75
- _menuStack : MenuStack ;
79
+ _menuStack : MenuStack = new NoopMenuStack ( ) ;
76
80
77
81
/** Handles keyboard events for the menu. */
78
82
private _keyManager : FocusKeyManager < CdkMenuItem > ;
@@ -136,6 +140,11 @@ export class CdkMenu extends CdkMenuGroup implements Menu, AfterContentInit, OnI
136
140
this . _keyManager . setLastItemActive ( ) ;
137
141
}
138
142
143
+ /** Set focus to the first menu item if this is an inline menu. */
144
+ _focusFirstItem ( ) {
145
+ this . _keyManager . setFirstItemActive ( ) ;
146
+ }
147
+
139
148
/** Handle keyboard events for the Menu. */
140
149
_handleKeyEvent ( event : KeyboardEvent ) {
141
150
const keyManager = this . _keyManager ;
@@ -176,12 +185,7 @@ export class CdkMenu extends CdkMenuGroup implements Menu, AfterContentInit, OnI
176
185
177
186
/** Register this menu with its enclosing parent menu panel */
178
187
private _registerWithParentPanel ( ) {
179
- const parent = this . _getMenuPanel ( ) ;
180
- if ( parent ) {
181
- parent . _registerMenu ( this ) ;
182
- } else {
183
- throwMissingMenuPanelError ( ) ;
184
- }
188
+ this . _getMenuPanel ( ) ?. _registerMenu ( this ) ;
185
189
}
186
190
187
191
/**
@@ -318,6 +322,16 @@ export class CdkMenu extends CdkMenuGroup implements Menu, AfterContentInit, OnI
318
322
return this . orientation === 'horizontal' ;
319
323
}
320
324
325
+ /**
326
+ * Return true if this menu is an inline menu. That is, it does not exist in a pop-up and is
327
+ * always visible in the dom.
328
+ */
329
+ _isInline ( ) {
330
+ // NoopMenuStack is the default. If this menu is not inline than the NoopMenuStack is replaced
331
+ // automatically.
332
+ return this . _menuStack instanceof NoopMenuStack ;
333
+ }
334
+
321
335
ngOnDestroy ( ) {
322
336
this . _emitClosedEvent ( ) ;
323
337
}
0 commit comments