7
7
*/
8
8
9
9
import {
10
- Directive ,
11
- Input ,
12
- ContentChildren ,
13
- QueryList ,
14
10
AfterContentInit ,
15
- OnDestroy ,
16
- Optional ,
17
- NgZone ,
11
+ Directive ,
18
12
ElementRef ,
19
13
Inject ,
14
+ NgZone ,
15
+ OnDestroy ,
16
+ Optional ,
20
17
Self ,
21
18
} from '@angular/core' ;
22
19
import { Directionality } from '@angular/cdk/bidi' ;
23
- import { FocusKeyManager , FocusOrigin } from '@angular/cdk/a11y' ;
24
- import { LEFT_ARROW , RIGHT_ARROW , UP_ARROW , DOWN_ARROW , ESCAPE , TAB } from '@angular/cdk/keycodes' ;
25
- import { takeUntil , mergeAll , mapTo , startWith , mergeMap , switchMap } from 'rxjs/operators' ;
26
- import { Subject , merge } from 'rxjs' ;
20
+ import { DOWN_ARROW , ESCAPE , LEFT_ARROW , RIGHT_ARROW , TAB , UP_ARROW } from '@angular/cdk/keycodes' ;
21
+ import { takeUntil } from 'rxjs/operators' ;
27
22
import { CdkMenuGroup } from './menu-group' ;
28
- import { CDK_MENU , Menu } from './menu-interface' ;
29
- import { CdkMenuItem } from './menu-item' ;
30
- import { MenuStack , MenuStackItem , FocusNext , MENU_STACK } from './menu-stack' ;
23
+ import { CDK_MENU } from './menu-interface' ;
24
+ import { FocusNext , MENU_STACK , MenuStack } from './menu-stack' ;
31
25
import { PointerFocusTracker } from './pointer-focus-tracker' ;
32
- import { MenuAim , MENU_AIM } from './menu-aim' ;
26
+ import { MENU_AIM , MenuAim } from './menu-aim' ;
27
+ import { CdkMenuBase } from './menu-base' ;
33
28
34
29
/**
35
30
* Directive applied to an element which configures it as a MenuBar by setting the appropriate
@@ -44,8 +39,6 @@ import {MenuAim, MENU_AIM} from './menu-aim';
44
39
'role' : 'menubar' ,
45
40
'class' : 'cdk-menu-bar' ,
46
41
'tabindex' : '0' ,
47
- '[attr.aria-orientation]' : 'orientation' ,
48
- '(focus)' : 'focusFirstItem()' ,
49
42
'(keydown)' : '_handleKeyEvent($event)' ,
50
43
} ,
51
44
providers : [
@@ -54,60 +47,31 @@ import {MenuAim, MENU_AIM} from './menu-aim';
54
47
{ provide : MENU_STACK , useClass : MenuStack } ,
55
48
] ,
56
49
} )
57
- export class CdkMenuBar extends CdkMenuGroup implements Menu , AfterContentInit , OnDestroy {
58
- /**
59
- * Sets the aria-orientation attribute and determines where menus will be opened.
60
- * Does not affect styling/layout.
61
- */
62
- @Input ( 'cdkMenuBarOrientation' ) orientation : 'horizontal' | 'vertical' = 'horizontal' ;
63
-
64
- /** Handles keyboard events for the MenuBar. */
65
- private _keyManager : FocusKeyManager < CdkMenuItem > ;
66
-
67
- /** Manages items under mouse focus */
68
- private _pointerTracker ?: PointerFocusTracker < CdkMenuItem > ;
50
+ export class CdkMenuBar extends CdkMenuBase implements AfterContentInit , OnDestroy {
51
+ override readonly orientation : 'horizontal' | 'vertical' = 'horizontal' ;
69
52
70
- /** Emits when the MenuBar is destroyed. */
71
- private readonly _destroyed : Subject < void > = new Subject ( ) ;
72
-
73
- /** All child MenuItem elements nested in this MenuBar. */
74
- @ContentChildren ( CdkMenuItem , { descendants : true } )
75
- private readonly _allItems : QueryList < CdkMenuItem > ;
76
-
77
- /** The Menu Item which triggered the open submenu. */
78
- private _openItem ?: CdkMenuItem ;
53
+ override menuStack : MenuStack ;
79
54
80
55
constructor (
81
56
private readonly _ngZone : NgZone ,
82
- readonly _elementRef : ElementRef < HTMLElement > ,
83
- @Inject ( MENU_STACK ) readonly _menuStack : MenuStack ,
57
+ elementRef : ElementRef < HTMLElement > ,
58
+ @Inject ( MENU_STACK ) menuStack : MenuStack ,
84
59
@Self ( ) @Optional ( ) @Inject ( MENU_AIM ) private readonly _menuAim ?: MenuAim ,
85
- @Optional ( ) private readonly _dir ?: Directionality ,
60
+ @Optional ( ) dir ?: Directionality ,
86
61
) {
87
- super ( ) ;
62
+ super ( elementRef , menuStack , dir ) ;
88
63
}
89
64
90
65
override ngAfterContentInit ( ) {
91
66
super . ngAfterContentInit ( ) ;
92
-
93
- this . _setKeyManager ( ) ;
94
- this . _subscribeToMenuOpen ( ) ;
95
- this . _subscribeToMenuStack ( ) ;
67
+ this . _subscribeToMenuStackEmptied ( ) ;
96
68
this . _subscribeToMouseManager ( ) ;
97
-
98
- this . _menuAim ?. initialize ( this , this . _pointerTracker ! ) ;
99
- }
100
-
101
- /** Place focus on the first MenuItem in the menu and set the focus origin. */
102
- focusFirstItem ( focusOrigin : FocusOrigin = 'program' ) {
103
- this . _keyManager . setFocusOrigin ( focusOrigin ) ;
104
- this . _keyManager . setFirstItemActive ( ) ;
69
+ this . _menuAim ?. initialize ( this , this . pointerTracker ! ) ;
105
70
}
106
71
107
- /** Place focus on the last MenuItem in the menu and set the focus origin. */
108
- focusLastItem ( focusOrigin : FocusOrigin = 'program' ) {
109
- this . _keyManager . setFocusOrigin ( focusOrigin ) ;
110
- this . _keyManager . setLastItemActive ( ) ;
72
+ override ngOnDestroy ( ) {
73
+ super . ngOnDestroy ( ) ;
74
+ this . pointerTracker ?. destroy ( ) ;
111
75
}
112
76
113
77
/**
@@ -116,7 +80,7 @@ export class CdkMenuBar extends CdkMenuGroup implements Menu, AfterContentInit,
116
80
* @param event the KeyboardEvent to handle.
117
81
*/
118
82
_handleKeyEvent ( event : KeyboardEvent ) {
119
- const keyManager = this . _keyManager ;
83
+ const keyManager = this . keyManager ;
120
84
switch ( event . keyCode ) {
121
85
case UP_ARROW :
122
86
case DOWN_ARROW :
@@ -127,8 +91,8 @@ export class CdkMenuBar extends CdkMenuGroup implements Menu, AfterContentInit,
127
91
// up/down keys were clicked: if the current menu is open, close it then focus and open the
128
92
// next menu.
129
93
if (
130
- ( this . _isHorizontal ( ) && horizontalArrows ) ||
131
- ( ! this . _isHorizontal ( ) && ! horizontalArrows )
94
+ ( this . isHorizontal ( ) && horizontalArrows ) ||
95
+ ( ! this . isHorizontal ( ) && ! horizontalArrows )
132
96
) {
133
97
event . preventDefault ( ) ;
134
98
@@ -157,69 +121,27 @@ export class CdkMenuBar extends CdkMenuGroup implements Menu, AfterContentInit,
157
121
}
158
122
}
159
123
160
- /** Setup the FocusKeyManager with the correct orientation for the menu bar. */
161
- private _setKeyManager ( ) {
162
- this . _keyManager = new FocusKeyManager ( this . _allItems )
163
- . withWrap ( )
164
- . withTypeAhead ( )
165
- . withHomeAndEnd ( ) ;
166
-
167
- if ( this . _isHorizontal ( ) ) {
168
- this . _keyManager . withHorizontalOrientation ( this . _dir ?. value || 'ltr' ) ;
169
- } else {
170
- this . _keyManager . withVerticalOrientation ( ) ;
171
- }
172
- }
173
-
174
124
/**
175
125
* Set the PointerFocusTracker and ensure that when mouse focus changes the key manager is updated
176
126
* with the latest menu item under mouse focus.
177
127
*/
178
128
private _subscribeToMouseManager ( ) {
179
129
this . _ngZone . runOutsideAngular ( ( ) => {
180
- this . _pointerTracker = new PointerFocusTracker ( this . _allItems ) ;
181
- this . _pointerTracker . entered . pipe ( takeUntil ( this . _destroyed ) ) . subscribe ( item => {
182
- if ( this . _hasOpenSubmenu ( ) ) {
183
- this . _keyManager . setActiveItem ( item ) ;
130
+ this . pointerTracker = new PointerFocusTracker ( this . items ) ;
131
+ this . pointerTracker . entered . pipe ( takeUntil ( this . destroyed ) ) . subscribe ( item => {
132
+ if ( this . hasOpenSubmenu ( ) ) {
133
+ this . keyManager . setActiveItem ( item ) ;
184
134
}
185
135
} ) ;
186
136
} ) ;
187
137
}
188
138
189
- /** Subscribe to the MenuStack close and empty observables. */
190
- private _subscribeToMenuStack ( ) {
191
- this . _menuStack . closed
192
- . pipe ( takeUntil ( this . _destroyed ) )
193
- . subscribe ( item => this . _closeOpenMenu ( item ) ) ;
194
-
195
- this . _menuStack . emptied
196
- . pipe ( takeUntil ( this . _destroyed ) )
197
- . subscribe ( event => this . _toggleOpenMenu ( event ) ) ;
198
- }
199
-
200
- /**
201
- * Close the open menu if the current active item opened the requested MenuStackItem.
202
- * @param item the MenuStackItem requested to be closed.
203
- */
204
- private _closeOpenMenu ( menu : MenuStackItem | undefined ) {
205
- const trigger = this . _openItem ;
206
- const keyManager = this . _keyManager ;
207
- if ( menu === trigger ?. getMenuTrigger ( ) ?. getMenu ( ) ) {
208
- trigger ?. getMenuTrigger ( ) ?. closeMenu ( ) ;
209
- // If the user has moused over a sibling item we want to focus the element under mouse focus
210
- // not the trigger which previously opened the now closed menu.
211
- if ( trigger ) {
212
- keyManager . setActiveItem ( this . _pointerTracker ?. activeElement || trigger ) ;
213
- }
214
- }
215
- }
216
-
217
139
/**
218
140
* Set focus to either the current, previous or next item based on the FocusNext event, then
219
141
* open the previous or next item.
220
142
*/
221
143
private _toggleOpenMenu ( event : FocusNext | undefined ) {
222
- const keyManager = this . _keyManager ;
144
+ const keyManager = this . keyManager ;
223
145
switch ( event ) {
224
146
case FocusNext . nextItem :
225
147
keyManager . setFocusOrigin ( 'keyboard' ) ;
@@ -242,48 +164,9 @@ export class CdkMenuBar extends CdkMenuGroup implements Menu, AfterContentInit,
242
164
}
243
165
}
244
166
245
- /**
246
- * @return true if the menu bar is configured to be horizontal.
247
- */
248
- private _isHorizontal ( ) {
249
- return this . orientation === 'horizontal' ;
250
- }
251
-
252
- /**
253
- * Subscribe to the menu trigger's open events in order to track the trigger which opened the menu
254
- * and stop tracking it when the menu is closed.
255
- */
256
- private _subscribeToMenuOpen ( ) {
257
- const exitCondition = merge ( this . _allItems . changes , this . _destroyed ) ;
258
- this . _allItems . changes
259
- . pipe (
260
- startWith ( this . _allItems ) ,
261
- mergeMap ( ( list : QueryList < CdkMenuItem > ) =>
262
- list
263
- . filter ( item => item . hasMenu ( ) )
264
- . map ( item => item . getMenuTrigger ( ) ! . opened . pipe ( mapTo ( item ) , takeUntil ( exitCondition ) ) ) ,
265
- ) ,
266
- mergeAll ( ) ,
267
- switchMap ( ( item : CdkMenuItem ) => {
268
- this . _openItem = item ;
269
- return item . getMenuTrigger ( ) ! . closed ;
270
- } ) ,
271
- takeUntil ( this . _destroyed ) ,
272
- )
273
- . subscribe ( ( ) => ( this . _openItem = undefined ) ) ;
274
- }
275
-
276
- /** Return true if the MenuBar has an open submenu. */
277
- private _hasOpenSubmenu ( ) {
278
- return ! ! this . _openItem ;
279
- }
280
-
281
- override ngOnDestroy ( ) {
282
- super . ngOnDestroy ( ) ;
283
-
284
- this . _destroyed . next ( ) ;
285
- this . _destroyed . complete ( ) ;
286
-
287
- this . _pointerTracker ?. destroy ( ) ;
167
+ private _subscribeToMenuStackEmptied ( ) {
168
+ this . menuStack ?. emptied
169
+ . pipe ( takeUntil ( this . destroyed ) )
170
+ . subscribe ( event => this . _toggleOpenMenu ( event ) ) ;
288
171
}
289
172
}
0 commit comments