diff --git a/src/material/core/tokens/m2/mat/_menu.scss b/src/material/core/tokens/m2/mat/_menu.scss new file mode 100644 index 000000000000..f06c35575030 --- /dev/null +++ b/src/material/core/tokens/m2/mat/_menu.scss @@ -0,0 +1,62 @@ +@use 'sass:map'; +@use '../../token-utils'; +@use '../../../theming/theming'; +@use '../../../style/sass-utils'; +@use '../../../typography/typography-utils'; + +// The prefix used to generate the fully qualified name for tokens in this file. +$prefix: (mat, menu); + +// Tokens that can't be configured through Angular Material's current theming API, +// but may be in a future version of the theming API. +@function get-unthemable-tokens() { + @return ( + container-shape: 4px, + ); +} + +// Tokens that can be configured through Angular Material's color theming API. +@function get-color-tokens($config) { + $is-dark: map.get($config, is-dark); + $foreground: map.get($config, foreground); + $background: map.get($config, background); + $on-surface: if($is-dark, #fff, #000); + $active-state-layer-color: rgba($on-surface, if($is-dark, 0.08, 0.04)); + $text-color: theming.get-color-from-palette($foreground, text); + + @return ( + item-label-text-color: $text-color, + item-icon-color: $text-color, + item-hover-state-layer-color: $active-state-layer-color, + item-focus-state-layer-color: $active-state-layer-color, + container-color: theming.get-color-from-palette($background, card), + ); +} + +// Tokens that can be configured through Angular Material's typography theming API. +@function get-typography-tokens($config) { + @return ( + item-label-text-font: + typography-utils.font-family($config, body-1) or typography-utils.font-family($config), + item-label-text-size: typography-utils.font-size($config, body-1), + item-label-text-tracking: typography-utils.letter-spacing($config, body-1), + item-label-text-line-height: typography-utils.line-height($config, body-1), + item-label-text-weight: typography-utils.font-weight($config, body-1), + ); +} + +// Tokens that can be configured through Angular Material's density theming API. +@function get-density-tokens($config) { + @return (); +} + +// Combines the tokens generated by the above functions into a single map with placeholder values. +// This is used to create token slots. +@function get-token-slots() { + @return sass-utils.deep-merge-all( + get-unthemable-tokens(), + get-color-tokens(token-utils.$placeholder-color-config), + get-typography-tokens(token-utils.$placeholder-typography-config), + get-density-tokens(token-utils.$placeholder-density-config) + ); +} diff --git a/src/material/menu/_menu-theme.scss b/src/material/menu/_menu-theme.scss index 3b993b2c7d4c..bc0d4fe382f5 100644 --- a/src/material/menu/_menu-theme.scss +++ b/src/material/menu/_menu-theme.scss @@ -1,71 +1,25 @@ -@use '@material/theme/theme-color' as mdc-theme-color; -@use '@material/theme/theme' as mdc-theme; -@use '@material/menu-surface' as mdc-menu-surface; -@use '@material/list/evolution-mixins' as mdc-list; -@use '@material/typography' as mdc-typography; -@use '@material/ripple' as mdc-ripple; -@use '../core/mdc-helpers/mdc-helpers'; +@use '../core/tokens/m2/mat/menu' as tokens-mat-menu; +@use '../core/style/sass-utils'; +@use '../core/tokens/token-utils'; @use '../core/theming/theming'; @use '../core/typography/typography'; @mixin color($config-or-theme) { $config: theming.get-color-config($config-or-theme); - @include mdc-helpers.using-mdc-theme($config) { - @include mdc-menu-surface.core-styles(mdc-helpers.$mdc-theme-styles-query); - .mat-mdc-menu-panel-wrapper { - @include mdc-list.without-ripple(mdc-helpers.$mdc-theme-styles-query); - } - - // MDC doesn't appear to have disabled styling for menu - // items so we have to grey them out ourselves. - .mat-mdc-menu-item[disabled] { - &, - .mat-mdc-menu-submenu-icon, - .mat-icon-no-color { - @include mdc-theme.prop(color, text-disabled-on-background); - } - } - - // Since we're creating `mat-icon` and the submenu trigger - // chevron ourselves, we have to handle the color as well. - .mat-mdc-menu-item .mat-icon-no-color, - .mat-mdc-menu-submenu-icon { - @include mdc-theme.prop(color, text-primary-on-background); - } - - // MDC's hover and focus styles are tied to their ripples which we aren't using. - .mat-mdc-menu-item:hover, - .mat-mdc-menu-item.cdk-program-focused, - .mat-mdc-menu-item.cdk-keyboard-focused, - .mat-mdc-menu-item-highlighted { - &:not([disabled]) { - $color: mdc-theme-color.$on-surface; - background: rgba($color, mdc-ripple.states-opacity($color, hover)); - } - } + @include sass-utils.current-selector-or-root() { + @include token-utils.create-token-values(tokens-mat-menu.$prefix, + tokens-mat-menu.get-color-tokens($config)); } } @mixin typography($config-or-theme) { $config: typography.private-typography-to-2018-config( theming.get-typography-config($config-or-theme)); - @include mdc-helpers.using-mdc-typography($config) { - @include mdc-menu-surface.core-styles(mdc-helpers.$mdc-typography-styles-query); - - .mat-mdc-menu-content { - // Note that we include this private mixin, because the public - // one adds a bunch of styles that we aren't using for the menu. - @include mdc-list.list-base(mdc-helpers.$mdc-typography-styles-query); - - // MDC uses the `subtitle1` level for list items, but the spec shows `body1` as the correct - // level. - &, - .mat-mdc-menu-item .mdc-list-item__primary-text { - @include mdc-typography.typography(body1, $query: mdc-helpers.$mdc-typography-styles-query); - } - } + @include sass-utils.current-selector-or-root() { + @include token-utils.create-token-values(tokens-mat-menu.$prefix, + tokens-mat-menu.get-typography-tokens($config)); } } diff --git a/src/material/menu/menu-trigger.ts b/src/material/menu/menu-trigger.ts index 52b5693610ca..3eb78521c49b 100644 --- a/src/material/menu/menu-trigger.ts +++ b/src/material/menu/menu-trigger.ts @@ -96,7 +96,6 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy private _menuCloseSubscription = Subscription.EMPTY; private _scrollStrategy: () => ScrollStrategy; private _changeDetectorRef = inject(ChangeDetectorRef); - protected _panelClass: string | null; /** * We're specifically looking for a `MatMenu` here since the generic `MatMenuPanel` @@ -330,10 +329,6 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy this._closingActionsSubscription = this._menuClosingActions().subscribe(() => this.closeMenu()); this._initMenu(menu); - if (this._panelClass) { - overlayRef.overlayElement.classList.add(this._panelClass); - } - if (menu instanceof _MatMenuBase) { menu._startAnimation(); menu._directDescendantItems.changes.pipe(takeUntil(menu.close)).subscribe(() => { @@ -691,6 +686,4 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy }, exportAs: 'matMenuTrigger', }) -export class MatMenuTrigger extends _MatMenuTriggerBase { - protected override _panelClass = 'mat-mdc-menu-panel-wrapper'; -} +export class MatMenuTrigger extends _MatMenuTriggerBase {} diff --git a/src/material/menu/menu.scss b/src/material/menu/menu.scss index deb341bf9391..124fa61ba9ab 100644 --- a/src/material/menu/menu.scss +++ b/src/material/menu/menu.scss @@ -1,17 +1,14 @@ @use 'sass:map'; @use '@angular/cdk'; -@use '@material/menu-surface' as mdc-menu-surface; @use '@material/list/evolution-mixins' as mdc-list-mixins; @use '@material/list/evolution-variables' as mdc-list-variables; +@use '@material/typography/typography' as mdc-typography; +@use '../core/tokens/m2/mat/menu' as tokens-mat-menu; +@use '../core/tokens/token-utils'; @use '../core/mdc-helpers/mdc-helpers'; @use '../core/style/menu-common'; @use '../core/style/button-common'; - -@include mdc-helpers.disable-mdc-fallback-declarations { - @include mdc-menu-surface.core-styles($query: structure); -} - // Prevent rendering mat-menu as it can affect the flex layout. mat-menu { display: none; @@ -19,9 +16,37 @@ mat-menu { .mat-mdc-menu-content { @include mdc-list-mixins.list-base($query: structure); + + &, + .mat-mdc-menu-item .mdc-list-item__primary-text { + @include mdc-typography.smooth-font(); + @include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) { + @include token-utils.create-token-slot(font-family, item-label-text-font); + @include token-utils.create-token-slot(line-height, item-label-text-line-height); + @include token-utils.create-token-slot(font-size, item-label-text-size); + @include token-utils.create-token-slot(letter-spacing, item-label-text-tracking); + @include token-utils.create-token-slot(font-weight, item-label-text-weight); + } + } } .mat-mdc-menu-panel { + @include token-utils.create-token-values(tokens-mat-menu.$prefix, + tokens-mat-menu.get-unthemable-tokens()); + box-sizing: border-box; + outline: 0; + + @include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) { + @include token-utils.create-token-slot(border-radius, container-shape); + @include token-utils.create-token-slot(background-color, container-color); + } + + // TODO(crisbeto): we don't need this for anything, but it was there when + // we used MDC's structural styles and removing it leads to sub-pixels + // differences in text rendering which break a lot of screenshots internally. + // We should clean it up eventually and re-approve all the screenshots. + will-change: transform, opacity; + // Prevent users from interacting with the panel while it's animating. Note that // people won't be able to click through it, because the overlay pane will catch the click. // This fixes the following issues: @@ -78,6 +103,20 @@ mat-menu { $height-config: map.get(mdc-list-variables.$one-line-item-density-config, height); min-height: map.get($height-config, default); + @include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) { + // The class selector isn't specific enough to overide the link pseudo selectors so we need + // to target them specifically, otherwise the item color might be overwritten by the user + // agent resets of the app. + &, &:visited, &:link { + @include token-utils.create-token-slot(color, item-label-text-color); + } + + .mat-icon-no-color, + .mat-mdc-menu-submenu-icon { + @include token-utils.create-token-slot(color, item-icon-color); + } + } + // If the MDC list is loaded after the menu, this gets overwritten which breaks the text // alignment. Ideally we'd wrap all the MDC mixins above with this selector, but the increased // specificity breaks some internal overrides. @@ -130,6 +169,20 @@ mat-menu { @include menu-common.item-submenu-trigger(mdc-list-variables.$side-padding); } + &:not([disabled]) { + @include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) { + &:hover { + @include token-utils.create-token-slot(background-color, item-hover-state-layer-color); + } + + &.cdk-program-focused, + &.cdk-keyboard-focused, + &.mat-mdc-menu-item-highlighted { + @include token-utils.create-token-slot(background-color, item-focus-state-layer-color); + } + } + } + @include cdk.high-contrast(active, off) { $outline-width: 1px; diff --git a/tools/public_api_guard/material/menu.md b/tools/public_api_guard/material/menu.md index b065ef284cd1..9c0b5bdbf9b3 100644 --- a/tools/public_api_guard/material/menu.md +++ b/tools/public_api_guard/material/menu.md @@ -274,8 +274,6 @@ export interface MatMenuPanel { // @public export class MatMenuTrigger extends _MatMenuTriggerBase { - // (undocumented) - protected _panelClass: string; // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) @@ -315,8 +313,6 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy // (undocumented) _openedBy: Exclude | undefined; openMenu(): void; - // (undocumented) - protected _panelClass: string | null; restoreFocus: boolean; toggleMenu(): void; triggersSubmenu(): boolean;