Skip to content

Commit a51f82f

Browse files
crisbetoandrewseguin
authored andcommitted
feat(menu): add indicator to menu items that trigger a sub-menu (#5995)
Adds an arrow indicator to the `md-menu-item` instances that trigger a sub-menu.
1 parent eaa6099 commit a51f82f

File tree

5 files changed

+70
-13
lines changed

5 files changed

+70
-13
lines changed

src/lib/menu/_menu-theme.scss

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@
1818
&[disabled] {
1919
color: mat-color($foreground, 'disabled');
2020
}
21+
}
2122

22-
.mat-icon {
23-
color: mat-color($foreground, 'icon');
24-
vertical-align: middle;
25-
}
26-
23+
.mat-menu-item .mat-icon,
24+
.mat-menu-item-submenu-trigger::after {
25+
color: mat-color($foreground, 'icon');
2726
}
2827

2928
.mat-menu-item:hover,

src/lib/menu/menu-item.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const _MdMenuItemMixinBase = mixinDisabled(MdMenuItemBase);
2828
'role': 'menuitem',
2929
'class': 'mat-menu-item',
3030
'[class.mat-menu-item-highlighted]': '_highlighted',
31+
'[class.mat-menu-item-submenu-trigger]': '_triggersSubmenu',
3132
'[attr.tabindex]': '_getTabIndex()',
3233
'[attr.aria-disabled]': 'disabled.toString()',
3334
'[attr.disabled]': 'disabled || null',
@@ -45,6 +46,9 @@ export class MdMenuItem extends _MdMenuItemMixinBase implements Focusable, CanDi
4546
/** Whether the menu item is highlighted. */
4647
_highlighted: boolean = false;
4748

49+
/** Whether the menu item acts as a trigger for a sub-menu. */
50+
_triggersSubmenu: boolean = false;
51+
4852
constructor(private _elementRef: ElementRef) {
4953
super();
5054
}

src/lib/menu/menu-trigger.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,19 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
122122
/** Event emitted when the associated menu is closed. */
123123
@Output() onMenuClose = new EventEmitter<void>();
124124

125-
constructor(private _overlay: Overlay,
126-
private _element: ElementRef,
127-
private _viewContainerRef: ViewContainerRef,
128-
@Inject(MD_MENU_SCROLL_STRATEGY) private _scrollStrategy,
129-
@Optional() private _parentMenu: MdMenu,
130-
@Optional() @Self() private _menuItemInstance: MdMenuItem,
131-
@Optional() private _dir: Directionality) { }
125+
constructor(
126+
private _overlay: Overlay,
127+
private _element: ElementRef,
128+
private _viewContainerRef: ViewContainerRef,
129+
@Inject(MD_MENU_SCROLL_STRATEGY) private _scrollStrategy,
130+
@Optional() private _parentMenu: MdMenu,
131+
@Optional() @Self() private _menuItemInstance: MdMenuItem,
132+
@Optional() private _dir: Directionality) {
133+
134+
if (_menuItemInstance) {
135+
_menuItemInstance._triggersSubmenu = this.triggersSubmenu();
136+
}
137+
}
132138

133139
ngAfterViewInit() {
134140
this._checkMenu();

src/lib/menu/menu.scss

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// TODO(kara): update vars for desktop when MD team responds
2-
// TODO(kara): animation for menu opening
32

43
@import '../core/style/button-common';
54
@import '../core/style/layout-common';
@@ -8,6 +7,7 @@
87

98
$mat-menu-vertical-padding: 8px !default;
109
$mat-menu-border-radius: 2px !default;
10+
$mat-menu-submenu-indicator-size: 10px !default;
1111

1212
.mat-menu-panel {
1313
@include mat-menu-base(2);
@@ -36,6 +36,43 @@ $mat-menu-border-radius: 2px !default;
3636
@include mat-button-reset();
3737
@include mat-menu-item-base();
3838
position: relative;
39+
40+
.mat-icon {
41+
vertical-align: middle;
42+
}
43+
}
44+
45+
.mat-menu-item-submenu-trigger {
46+
// Increase the side padding to prevent the indicator from overlapping the text.
47+
padding-right: $mat-menu-side-padding * 2;
48+
49+
// Renders a triangle to indicate that the menu item will open a sub-menu.
50+
&::after {
51+
$size: $mat-menu-submenu-indicator-size / 2;
52+
53+
width: 0;
54+
height: 0;
55+
border-style: solid;
56+
border-width: $size 0 $size $size;
57+
border-color: transparent transparent transparent currentColor;
58+
content: '';
59+
display: inline-block;
60+
position: absolute;
61+
top: 50%;
62+
right: $mat-menu-side-padding;
63+
transform: translateY(-50%);
64+
}
65+
66+
[dir='rtl'] & {
67+
padding-right: $mat-menu-side-padding / 2;
68+
padding-left: $mat-menu-side-padding * 2;
69+
70+
&::after {
71+
right: auto;
72+
left: $mat-menu-side-padding;
73+
transform: rotateY(180deg) translateY(-50%);
74+
}
75+
}
3976
}
4077

4178
button.mat-menu-item {

src/lib/menu/menu.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,17 @@ describe('MdMenu', () => {
872872
expect(overlay.querySelectorAll('.mat-menu-panel').length).toBe(0, 'Expected no open menus');
873873
});
874874

875+
it('should set a class on the menu items that trigger a sub-menu', () => {
876+
compileTestComponent();
877+
instance.rootTrigger.openMenu();
878+
fixture.detectChanges();
879+
880+
const menuItems = overlay.querySelectorAll('[md-menu-item]');
881+
882+
expect(menuItems[0].classList).toContain('mat-menu-item-submenu-trigger');
883+
expect(menuItems[1].classList).not.toContain('mat-menu-item-submenu-trigger');
884+
});
885+
875886
});
876887

877888
});

0 commit comments

Comments
 (0)