Skip to content

Commit

Permalink
refactor(menu)!: refactor menu-item to use md-item and not rely on md…
Browse files Browse the repository at this point in the history
…-list-item

BREAKING CHANGE: This change refactors menu-item to no longer subclass or import from list-item. It also refactors it to use md-item directly which means that the API of menu item has moved from properties to slots. `start-*` and `end-*` slots are now just `start` and `end`, many tokens are now gone in favor of slotting. `headline` property is now a `slot="headline"` slot. Typeahead search text can now be set via `typeaheadText` which defaults to the slotted headline `textContent`. `select-option` now has the `displayText` which is used to display text in the `md-select` when the option is selected; defaults to the slotted headline `textContent`.

PiperOrigin-RevId: 567719483
  • Loading branch information
Elliott Marquez authored and copybara-github committed Sep 22, 2023
1 parent 61b382a commit 2a1d877
Show file tree
Hide file tree
Showing 15 changed files with 594 additions and 91 deletions.
41 changes: 23 additions & 18 deletions menu/demo/stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ const sharedStyle = css`
[dir=rtl] md-icon {
transform: scaleX(-1);
}
[slot="headline"] {
white-space: nowrap;
}
`;

const standard: MaterialStoryInit<StoryKnobs> = {
Expand Down Expand Up @@ -127,10 +130,10 @@ const standard: MaterialStoryInit<StoryKnobs> = {
@closed=${setButtonAriaExpandedFalse}>
${fruitNames.map((name, index) => html`
<md-menu-item
headline=${name}
id=${index}
.keepOpen=${knobs.keepOpen}
.disabled=${knobs.disabled}>
<div slot="headline">${name}</div>
</md-menu-item>`)}
</md-menu>
</div>
Expand All @@ -150,13 +153,13 @@ const linkable: MaterialStoryInit<StoryKnobs> = {
const isLastItem = index === fruitNames.length - 1;
return html`
<md-menu-item
headline=${name}
id=${index}
.disabled=${knobs.disabled}
.target=${
knobs.target as '' | '_blank' | '_parent' | '_self' | '_top'}
.href=${knobs.href}>
<md-icon slot="end-icon">
<div slot="headline">${name}</div>
<md-icon slot="end">
${knobs['link icon']}
</md-icon>
</md-menu-item>
Expand Down Expand Up @@ -214,10 +217,9 @@ const submenu: MaterialStoryInit<StoryKnobs> = {

return html`
<md-menu-item
headline=${name}
id=${currentIndex}
.keepOpen=${knobs.keepOpen}
.disabled=${knobs.disabled}>
.keepOpen=${knobs.keepOpen}
.disabled=${knobs.disabled}>
<div slot="headline">${name}</div>
</md-menu-item>`;
});

Expand All @@ -228,16 +230,17 @@ const submenu: MaterialStoryInit<StoryKnobs> = {

return html`
<md-sub-menu
id=${currentIndex}
.anchorCorner=${knobs['submenu.anchorCorner']!}
.menuCorner=${knobs['submenu.menuCorner']!}
.hoverOpenDelay=${knobs.hoverOpenDelay}
.hoverCloseDelay=${knobs.hoverCloseDelay}>
<md-menu-item
slot="item"
headline=${name}
id=${currentIndex}
id=${++currentIndex}
.disabled=${knobs.disabled}>
<md-icon slot="end-icon">
<div slot="headline">${name}</div>
<md-icon slot="end">
${knobs['submenu item icon']}
</md-icon>
</md-menu-item>
Expand All @@ -259,10 +262,10 @@ const submenu: MaterialStoryInit<StoryKnobs> = {

return html`
<md-menu-item
headline=${name}
id=${currentIndex}
.keepOpen=${knobs.keepOpen}
.disabled=${knobs.disabled}>
<div slot="headline">${name}</div>
</md-menu-item>`;
}),
];
Expand All @@ -273,16 +276,17 @@ const submenu: MaterialStoryInit<StoryKnobs> = {

return html`
<md-sub-menu
id=${currentIndex}
.anchorCorner=${knobs['submenu.anchorCorner']!}
.menuCorner=${knobs['submenu.menuCorner']!}
.hoverOpenDelay=${knobs.hoverOpenDelay}
.hoverCloseDelay=${knobs.hoverCloseDelay}>
<md-menu-item
slot="item"
headline=${name}
id=${currentIndex}
id=${++currentIndex}
.disabled=${knobs.disabled}>
<md-icon slot="end-icon">
<div slot="headline">${name}</div>
<md-icon slot="end">
${knobs['submenu item icon']}
</md-icon>
</md-menu-item>
Expand Down Expand Up @@ -382,10 +386,10 @@ const menuWithoutButton: MaterialStoryInit<StoryKnobs> = {
@close-menu=${displayCloseEvent}>
${fruitNames.map((name, index) => html`
<md-menu-item
headline=${name}
id=${index}
.keepOpen=${knobs.keepOpen}
.disabled=${knobs.disabled}>
<div slot="headline">${name}</div>
</md-menu-item>
`)}
</md-menu>
Expand Down Expand Up @@ -438,9 +442,10 @@ function displayCloseEvent(event: CloseMenuEvent) {

const stringifyItem = (menuItem: MenuItem&HTMLElement) => {
const tagName = menuItem.tagName.toLowerCase();
const headline = menuItem.headline;
return `${tagName}${menuItem.id ? `[id="${menuItem.id}"]` : ''}[headline="${
headline}"]`;
const headline = menuItem.typeaheadText;
return `${tagName}${
menuItem.id ? `[id="${menuItem.id}"]` :
''} > [slot="headline"] > ${headline}`;
};

// display the event's details in the inner text of that output element
Expand Down
143 changes: 124 additions & 19 deletions menu/internal/menuitem/_menu-item.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,57 +9,162 @@
@use 'sass:string';
// go/keep-sorted end
// go/keep-sorted start
@use '../../../focus/focus-ring';
@use '../../../icon/icon';
@use '../../../list/list-item';
@use '../../../ripple/ripple';
@use '../../../tokens';
// go/keep-sorted end

@mixin theme($tokens) {
$list-item-supported-tokens: tokens.$md-comp-menu-list-item-supported-tokens;
$supported-tokens: tokens.$md-comp-menu-item-supported-tokens;

@each $token, $value in $tokens {
@if list.index($supported-tokens, $token) == null {
@if list.index($supported-tokens, $token) ==
null and
list.index($list-item-supported-tokens, $token) ==
null
{
@error 'Token `#{$token}` is not a supported token.';
}

@if $value {
@if $value and list.index($supported-tokens, $token) == null {
--md-menu-item-#{$token}: #{$value};
}

@if $value and list.index($list-item-supported-tokens, $token) == null {
--md-list-item-#{$token}: #{$value};
}
}
}

@mixin styles() {
$list-item-tokens: tokens.md-comp-menu-list-item-values();
$tokens: tokens.md-comp-menu-item-values();

:host {
@each $token, $value in $tokens {
--_#{$token}: var(--md-menu-item-#{$token}, #{$value});
}
border-radius: map.get($list-item-tokens, 'container-shape');
display: flex;

@include list-item.theme(
@include ripple.theme(
(
'container-color': var(--_container-color),
hover-color: map.get($list-item-tokens, 'hover-state-layer-color'),
hover-opacity: map.get($list-item-tokens, 'hover-state-layer-opacity'),
pressed-color: map.get($list-item-tokens, 'pressed-state-layer-color'),
pressed-opacity:
map.get($list-item-tokens, 'pressed-state-layer-opacity'),
)
);
}

.list-item.selected {
background-color: var(--_selected-container-color);
:host([disabled]) {
opacity: map.get($list-item-tokens, 'disabled-opacity');
pointer-events: none;
}

.selected:not(.disabled) .label-text {
color: var(--_selected-label-text-color);
}
md-focus-ring {
z-index: 1;

// Set the ripple opacity to 0 if there is a submenu that is hovered.
.submenu-hover {
// Have to use ripple theme directly because :has selector in this case does
// not work in this case with the :has selector, thus we cannot override the
// custom props set in :host
@include ripple.theme(
@include focus-ring.theme(
(
hover-opacity: 0,
'shape': 8px,
)
);
}

a,
button,
li {
// Resets. These can be removed once we're no longer use these tags
background: none;
border: none;
padding: 0;
margin: 0;
text-align: unset;
text-decoration: none;
}

.list-item {
border-radius: inherit;
display: flex;
flex: 1;
outline: none;
// hide android tap color since we have ripple
-webkit-tap-highlight-color: transparent;

&:not(.disabled) {
cursor: pointer;
}
}

[slot='container'] {
pointer-events: none;
}

md-ripple {
border-radius: inherit;
}

md-item {
border-radius: inherit;
flex: 1;
color: map.get($list-item-tokens, 'label-text-color');
font-family: map.get($list-item-tokens, 'label-text-font');
font-size: map.get($list-item-tokens, 'label-text-size');
line-height: map.get($list-item-tokens, 'label-text-line-height');
font-weight: map.get($list-item-tokens, 'label-text-weight');
min-height: map.get($list-item-tokens, 'one-line-container-height');
padding-top: map.get($list-item-tokens, 'top-space');
padding-bottom: map.get($list-item-tokens, 'bottom-space');
padding-inline-start: map.get($list-item-tokens, 'leading-space');
padding-inline-end: map.get($list-item-tokens, 'trailing-space');
}

md-item[multiline] {
min-height: map.get($list-item-tokens, 'two-line-container-height');
}

[slot='supporting-text'] {
color: map.get($list-item-tokens, 'supporting-text-color');
font-family: map.get($list-item-tokens, 'supporting-text-font');
font-size: map.get($list-item-tokens, 'supporting-text-size');
line-height: map.get($list-item-tokens, 'supporting-text-line-height');
font-weight: map.get($list-item-tokens, 'supporting-text-weight');
}

[slot='trailing-supporting-text'] {
color: map.get($list-item-tokens, 'trailing-supporting-text-color');
font-family: map.get($list-item-tokens, 'trailing-supporting-text-font');
font-size: map.get($list-item-tokens, 'trailing-supporting-text-size');
line-height: map.get(
$list-item-tokens,
'trailing-supporting-text-line-height'
);
font-weight: map.get($list-item-tokens, 'trailing-supporting-text-weight');
}

:is([slot='start'], [slot='end'])::slotted(*) {
fill: currentColor;
}

[slot='start'] {
color: map.get($list-item-tokens, 'leading-icon-color');
}

[slot='end'] {
color: map.get($list-item-tokens, 'trailing-icon-color');
}

.list-item {
background-color: map.get($list-item-tokens, 'container-color');
}

.list-item.selected {
background-color: map.get($tokens, 'selected-container-color');
}

.selected:not(.disabled) .label-text {
color: map.get($tokens, 'selected-label-text-color');
}
}
10 changes: 7 additions & 3 deletions menu/internal/menuitem/forced-colors-styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
//

@media (forced-colors: active) {
:host([disabled]),
:host([disabled]) slot {
color: GrayText;
opacity: 1;
}

.list-item {
position: relative;
}

// Show double border only when selected, and the current list item does not
// have a focus ring on it.
.list-item.selected:not(.has-focus-ring)::before {
.list-item.selected::before {
content: '';
position: absolute;
inset: 0;
Expand Down
Loading

0 comments on commit 2a1d877

Please sign in to comment.