@@ -36,7 +36,7 @@ import {
36
36
import { normalizePassiveListenerOptions } from '@angular/cdk/platform' ;
37
37
import { asapScheduler , merge , of as observableOf , Subscription } from 'rxjs' ;
38
38
import { delay , filter , take , takeUntil } from 'rxjs/operators' ;
39
- import { _MatMenuBase } from './menu' ;
39
+ import { MenuCloseReason , _MatMenuBase } from './menu' ;
40
40
import { throwMatMenuMissingError , throwMatMenuRecursiveError } from './menu-errors' ;
41
41
import { MatMenuItem } from './menu-item' ;
42
42
import { MatMenuPanel , MAT_MENU_PANEL } from './menu-panel' ;
@@ -103,7 +103,7 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
103
103
104
104
// Tracking input type is necessary so it's possible to only auto-focus
105
105
// the first item of the list when the menu is opened via the keyboard
106
- _openedBy : Exclude < FocusOrigin , 'program' > = null ;
106
+ _openedBy : Exclude < FocusOrigin , 'program' | null > | undefined = undefined ;
107
107
108
108
/**
109
109
* @deprecated
@@ -131,15 +131,14 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
131
131
throwMatMenuRecursiveError ( ) ;
132
132
}
133
133
134
- this . _menuCloseSubscription = menu . close . subscribe (
135
- ( reason : 'click' | 'tab' | 'keydown' | undefined ) => {
136
- this . _destroyMenu ( ) ;
134
+ this . _menuCloseSubscription = menu . close . subscribe ( ( reason : MenuCloseReason ) => {
135
+ this . _destroyMenu ( reason ) ;
137
136
138
- // If a click closed the menu, we should close the entire chain of nested menus.
139
- if ( ( reason === 'click' || reason === 'tab' ) && this . _parentMaterialMenu ) {
140
- this . _parentMaterialMenu . closed . emit ( reason ) ;
141
- }
142
- } ) ;
137
+ // If a click closed the menu, we should close the entire chain of nested menus.
138
+ if ( ( reason === 'click' || reason === 'tab' ) && this . _parentMaterialMenu ) {
139
+ this . _parentMaterialMenu . closed . emit ( reason ) ;
140
+ }
141
+ } ) ;
143
142
}
144
143
}
145
144
private _menu : MatMenuPanel ;
@@ -284,15 +283,24 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
284
283
}
285
284
286
285
/** Closes the menu and does the necessary cleanup. */
287
- private _destroyMenu ( ) {
286
+ private _destroyMenu ( reason : MenuCloseReason ) {
288
287
if ( ! this . _overlayRef || ! this . menuOpen ) {
289
288
return ;
290
289
}
291
290
292
291
const menu = this . menu ;
293
292
this . _closingActionsSubscription . unsubscribe ( ) ;
294
293
this . _overlayRef . detach ( ) ;
295
- this . _restoreFocus ( ) ;
294
+
295
+ // Always restore focus if the user is navigating using the keyboard or the menu was opened
296
+ // programmatically. We don't restore for non-root triggers, because it can prevent focus
297
+ // from making it back to the root trigger when closing a long chain of menus by clicking
298
+ // on the backdrop.
299
+ if ( this . restoreFocus && ( reason === 'keydown' || ! this . _openedBy || ! this . triggersSubmenu ( ) ) ) {
300
+ this . focus ( this . _openedBy ) ;
301
+ }
302
+
303
+ this . _openedBy = undefined ;
296
304
297
305
if ( menu instanceof _MatMenuBase ) {
298
306
menu . _resetAnimation ( ) ;
@@ -350,24 +358,6 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
350
358
}
351
359
}
352
360
353
- /** Restores focus to the element that was focused before the menu was open. */
354
- private _restoreFocus ( ) {
355
- // We should reset focus if the user is navigating using a keyboard or
356
- // if we have a top-level trigger which might cause focus to be lost
357
- // when clicking on the backdrop.
358
- if ( this . restoreFocus ) {
359
- if ( ! this . _openedBy ) {
360
- // Note that the focus style will show up both for `program` and
361
- // `keyboard` so we don't have to specify which one it is.
362
- this . focus ( ) ;
363
- } else if ( ! this . triggersSubmenu ( ) ) {
364
- this . focus ( this . _openedBy ) ;
365
- }
366
- }
367
-
368
- this . _openedBy = null ;
369
- }
370
-
371
361
// set state rather than toggle to support triggers sharing a menu
372
362
private _setIsMenuOpen ( isOpen : boolean ) : void {
373
363
this . _menuOpen = isOpen ;
@@ -506,7 +496,7 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
506
496
if ( ! isFakeMousedownFromScreenReader ( event ) ) {
507
497
// Since right or middle button clicks won't trigger the `click` event,
508
498
// we shouldn't consider the menu as opened by mouse in those cases.
509
- this . _openedBy = event . button === 0 ? 'mouse' : null ;
499
+ this . _openedBy = event . button === 0 ? 'mouse' : undefined ;
510
500
511
501
// Since clicking on the trigger won't close the menu if it opens a sub-menu,
512
502
// we should prevent focus from moving onto it via click to avoid the
0 commit comments