-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(menu): add nested menu functionality #5493
Conversation
|
We need to make sure that when using a mouse and running the cursor diagnally that it doesn't change to another nested menu item. I haven't seen this mentioned in the design document, so I'm not sure if this has been overlooked at the moment. The google documents menu does this but i beleive its done using a timer instead of detecting direction, a good example would be the amazon menu even if you move slowly it still has the correct nested menu open and moving the cursor up and down the menu shows the nested items without delay. This seems to be a lot more user friendly. This page here documents it. |
The article @joejordanbrown posted makes a pretty good point; we should consider adding a feature like that to the cdk and using in the menu as a follow-up. |
Makes sense, I'll add it to my backlog since this PR is big enough as it is. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you plan on doing anything for adding an indicator for nested menu triggers in a follow-up?
src/lib/menu/menu-directive.ts
Outdated
/** Stream that emits whenever the hovered menu item changes. */ | ||
get hover(): Observable<MdMenuItem> { | ||
return merge(...this.items.map(item => item.hover)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this a function to avoid the compiled getter boilerplate?
export class MdMenuItem extends _MdMenuItemMixinBase implements Focusable, CanDisable { | ||
export class MdMenuItem extends _MdMenuItemMixinBase implements Focusable, CanDisable, OnDestroy { | ||
/** Stream that emits when the menu item is hovered. */ | ||
hover: Subject<MdMenuItem> = new Subject(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this meant to be an internal api?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is mainly to facilitate the nested menu, but I can see it being useful in other cases as well.
src/lib/menu/menu-trigger.ts
Outdated
/** Whether the menu triggers a sub-menu or a top-level one. */ | ||
get triggersSubmenu(): boolean { | ||
return !!(this._menuItemInstance && this._parentMenu); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this a function?
src/lib/menu/menu-trigger.ts
Outdated
|
||
if (!this.menu.overlapTrigger) { | ||
if (this.triggersSubmenu) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some comments around how the positioning is different when triggering a submenu?
I'll add some UX improvements in a follow-up. This PR is mainly for figuring out the positioning, accessibility and opening/closing logic. |
Addressed the feedback @jelbourn. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, but I want @kara to take a look when she gets back next week
I agree with @m98 that the animation speed should be slowed down from what the demo has. It should match what the first level menu uses. |
wrt adding an indicator, could be related to #5067 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, a couple comments.
Also, a few things to track for future improvement PRs:
-
Agree that the sub-menu triggers need arrow icons for usability. It's not clear which menu items are submenus otherwise. Create an issue for this?
-
Moving the mouse quickly to the left/right after opening the top level menu can mess up the positioning. Look into this in a future PR?
src/lib/menu/menu.md
Outdated
|
||
### Nested menu | ||
|
||
Material supports the ability for a `md-menu-item` to open a sub-menu. To do so, you have to define |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: a md-menu-item
-> an md-menu-item
src/lib/menu/menu-trigger.ts
Outdated
this._parentMenuSubscriptions.push(this._parentMenu.close.subscribe(() => { | ||
this.menu.close.emit(); | ||
})); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it at all possible to avoid having a list of subscriptions? I think the logic might be neater / easier to follow if we merge the streams that result in the menu closing into one subscription, rather than having a few separate subscriptions that do the same thing.
get _menuClosingActions() {
return merge(
this._overlayRef.backdropClick,
this._parentMenu.close,
this._parentMenu.hover().filter(active => active !== this._menuItemInstance);
}
Then instead of only subscribing to the backdrop when the menu opens, we subscribe to the menu closing events when the menu opens, kind of like the autocomplete.
_subscribeToMenuClose() {
this._closingSubscription = this._menuClosingActions.subscribe(() => this.menu.close.emit());
}
Ultimately, we'd still be adding another subscription onInit for the parent menu hovering (to open the menu when the active item is this menu item), but at least we wouldn't be adding an array of subscriptions that we'd need to manage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did it initially, because they subscribe at different times in the lifecycle. Looking at it again, they don't really need to do that. Refactored.
Adds the ability for an `md-menu-item` inside of a `md-menu` to trigger another `md-menu`. This is a first step towards a `md-toolbar` component. Fixes angular#1429.
Rebased and addressed the feedback. Also I agree on the UX improvements, but I'll add them in a follow-up. The main purpose of this PR was to sort out the opening/closing logic, accessibility and positioning. Can you take another look @kara? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost there; just noticed one more thing. When you click on a menu item, the parent menus don't seem to close (only its own parent menu). Seems like this should be fixed in this PR.
src/lib/menu/menu-trigger.ts
Outdated
/** Returns a stream that emits whenever an action that should close the menu occurs. */ | ||
private _menuClosingActions() { | ||
const backdrop = this._overlayRef!.backdropClick(); | ||
const parentClose = !this._parentMenu ? observableOf(null) : this._parentMenu.close; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I'd switch the logic here and on line 364 because it's harder to scan "if not, else" statements (the else becomes "if not not").
this._parentMenu ? this._parentMenu.close : observableOf(null);
Good point WRT to closing all of the menu on click @kara. I've addressed it together with the other comment. Also redeployed the demo to https://material-cb7ec.firebaseapp.com/menu |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Note: Addition of |
I suppose the same can be said about the |
@crisbeto No changes needed on your end; just a note to myself so I don't accidentally merge prematurely. |
Done. |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Adds the ability for an
md-menu-item
inside of amd-menu
to trigger anothermd-menu
. This is a first step towards amd-toolbar
component.Demo: https://material-cb7ec.firebaseapp.com/menu
Fixes #1429.