Skip to content

Commit

Permalink
fix(menu): fix submenu closing when already opened and all menus clos…
Browse files Browse the repository at this point in the history
…ing when hovering over menuitem

PiperOrigin-RevId: 527611715
  • Loading branch information
e111077 authored and Copybara-Service committed Apr 27, 2023
1 parent a0efe1e commit f6d72f9
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 2 deletions.
14 changes: 13 additions & 1 deletion menu/lib/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,9 @@ export abstract class Menu extends LitElement {
@close-menu=${this.onCloseMenu}
@deactivate-items=${this.onDeactivateItems}
@deactivate-typeahead=${this.handleDeactivateTypeahead}
@activate-typeahead=${this.handleActivateTypeahead}></slot>`;
@activate-typeahead=${this.handleActivateTypeahead}
@stay-open-on-focusout=${this.handleStayOpenOnFocusout}
@close-on-focusout=${this.handleCloseOnFocusout}></slot>`;
}

/**
Expand Down Expand Up @@ -681,6 +683,16 @@ export abstract class Menu extends LitElement {
this.typeaheadActive = true;
}

private handleStayOpenOnFocusout(e:Event) {
e.stopPropagation();
this.stayOpenOnFocusout = true;
}

private handleCloseOnFocusout(e:Event) {
e.stopPropagation();
this.stayOpenOnFocusout = false;
}

override focus() {
this.listElement?.focus();
}
Expand Down
20 changes: 20 additions & 0 deletions menu/lib/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,26 @@ export class CloseMenuEvent<T extends Reason = DefaultReasons> extends Event {
}
}

/**
* The event that signals to the menu that it should stay open on the focusout
* event.
*/
export class StayOpenOnFocusoutEvent extends Event {
constructor() {
super('stay-open-on-focusout', {bubbles: true, composed: true});
}
}

/**
* The event that signals to the menu that it should close open on the focusout
* event.
*/
export class CloseOnFocusoutEvent extends Event {
constructor() {
super('close-on-focusout', {bubbles: true, composed: true});
}
}

/**
* The default close menu event used by md-menu. To create your own `close-menu`
* event, you should subclass the `CloseMenuEvent` instead.
Expand Down
19 changes: 18 additions & 1 deletion menu/lib/submenuitem/sub-menu-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {property, queryAssignedElements} from 'lit/decorators.js';
import {List} from '../../../list/lib/list.js';
import {Corner, Menu} from '../menu.js';
import {MenuItemEl} from '../menuitem/menu-item.js';
import {ActivateTypeaheadEvent, CLOSE_REASON, CloseMenuEvent, DeactivateItemsEvent, DeactivateTypeaheadEvent, KEYDOWN_CLOSE_KEYS, NAVIGABLE_KEY, SELECTION_KEY} from '../shared.js';
import {ActivateTypeaheadEvent, CLOSE_REASON, CloseMenuEvent, CloseOnFocusoutEvent, DeactivateItemsEvent, DeactivateTypeaheadEvent, KEYDOWN_CLOSE_KEYS, NAVIGABLE_KEY, SELECTION_KEY, StayOpenOnFocusoutEvent} from '../shared.js';

function stopPropagation(e: Event) {
e.stopPropagation();
Expand All @@ -23,6 +23,12 @@ function stopPropagation(e: Event) {
* to deactivate the typeahead functionality when a submenu opens
* @fires activate-typeahead {DeactivateItemsEvent} Requests the parent menu to
* activate the typeahead functionality when a submenu closes
* @fires stay-open-on-focusout {StayOpenOnFocusoutEvent} Requests the parent
* menu to stay open when focusout event is fired or has a `null`
* `relatedTarget` when submenu is opened.
* @fires close-on-focusout {CloseOnFocusoutEvent} Requests the parent
* menu to close when focusout event is fired or has a `null`
* `relatedTarget` When submenu is closed.
*/
export class SubMenuItem extends MenuItemEl {
/**
Expand Down Expand Up @@ -152,6 +158,8 @@ export class SubMenuItem extends MenuItemEl {

private onCloseSubmenu(e: CloseMenuEvent) {
e.itemPath.push(this);
// Restore focusout behavior
this.dispatchEvent(new CloseOnFocusoutEvent());
this.dispatchEvent(new ActivateTypeaheadEvent());
// Escape should only close one menu not all of the menus unlike space or
// click selection which should close all menus.
Expand Down Expand Up @@ -206,9 +214,16 @@ export class SubMenuItem extends MenuItemEl {
// keyboard after hover.
menu.defaultFocus = 'LIST_ROOT';
menu.skipRestoreFocus = true;
menu.stayOpenOnOutsideClick = true;
menu.stayOpenOnFocusout = true;

// Menu could already be opened because of mouse interaction
const menuAlreadyOpen = menu.open;
// We want the parent to stay open in the case such that a middle submenu
// has a submenuitem hovered which opens a third submenut. Then if you hover
// on yet another middle menu-item (not submenuitem) then focusout Event's
// relatedTarget will be `null` thus, causing all the menus to close
this.dispatchEvent(new StayOpenOnFocusoutEvent());
menu.show();

// Deactivate other items. This can be the case if the user has tabbed
Expand Down Expand Up @@ -238,6 +253,8 @@ export class SubMenuItem extends MenuItemEl {
this.dispatchEvent(new ActivateTypeaheadEvent());
menu.quick = true;
menu.close();
// Restore focusout behavior.
this.dispatchEvent(new CloseOnFocusoutEvent());
this.active = false;
this.selected = false;
menu.addEventListener('closed', onClosed, {once: true});
Expand Down

0 comments on commit f6d72f9

Please sign in to comment.