Skip to content

Commit d758ad1

Browse files
committed
refactor(material/menu): switch to tokens API
Switches the menu to use the tokens API for theming.
1 parent 06f8544 commit d758ad1

File tree

5 files changed

+124
-73
lines changed

5 files changed

+124
-73
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
@use 'sass:map';
2+
@use '../../token-utils';
3+
@use '../../../theming/theming';
4+
@use '../../../style/sass-utils';
5+
@use '../../../typography/typography-utils';
6+
7+
// The prefix used to generate the fully qualified name for tokens in this file.
8+
$prefix: (mat, menu);
9+
10+
// Tokens that can't be configured through Angular Material's current theming API,
11+
// but may be in a future version of the theming API.
12+
@function get-unthemable-tokens() {
13+
@return (
14+
container-shape: 4px,
15+
);
16+
}
17+
18+
// Tokens that can be configured through Angular Material's color theming API.
19+
@function get-color-tokens($config) {
20+
$is-dark: map.get($config, is-dark);
21+
$foreground: map.get($config, foreground);
22+
$background: map.get($config, background);
23+
$on-surface: if($is-dark, #fff, #000);
24+
$active-state-layer-color: rgba($on-surface, if($is-dark, 0.08, 0.04));
25+
$text-color: theming.get-color-from-palette($foreground, text);
26+
27+
@return (
28+
item-label-text-color: $text-color,
29+
item-icon-color: $text-color,
30+
item-hover-state-layer-color: $active-state-layer-color,
31+
item-focus-state-layer-color: $active-state-layer-color,
32+
container-color: theming.get-color-from-palette($background, card),
33+
);
34+
}
35+
36+
// Tokens that can be configured through Angular Material's typography theming API.
37+
@function get-typography-tokens($config) {
38+
@return (
39+
item-label-text-font:
40+
typography-utils.font-family($config, body-1) or typography-utils.font-family($config),
41+
item-label-text-size: typography-utils.font-size($config, body-1),
42+
item-label-text-tracking: typography-utils.letter-spacing($config, body-1),
43+
item-label-text-line-height: typography-utils.line-height($config, body-1),
44+
item-label-text-weight: typography-utils.font-weight($config, body-1),
45+
);
46+
}
47+
48+
// Tokens that can be configured through Angular Material's density theming API.
49+
@function get-density-tokens($config) {
50+
@return ();
51+
}
52+
53+
// Combines the tokens generated by the above functions into a single map with placeholder values.
54+
// This is used to create token slots.
55+
@function get-token-slots() {
56+
@return sass-utils.deep-merge-all(
57+
get-unthemable-tokens(),
58+
get-color-tokens(token-utils.$placeholder-color-config),
59+
get-typography-tokens(token-utils.$placeholder-typography-config),
60+
get-density-tokens(token-utils.$placeholder-density-config)
61+
);
62+
}

src/material/menu/_menu-theme.scss

Lines changed: 9 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,25 @@
1-
@use '@material/theme/theme-color' as mdc-theme-color;
2-
@use '@material/theme/theme' as mdc-theme;
3-
@use '@material/menu-surface' as mdc-menu-surface;
4-
@use '@material/list/evolution-mixins' as mdc-list;
5-
@use '@material/typography' as mdc-typography;
6-
@use '@material/ripple' as mdc-ripple;
7-
@use '../core/mdc-helpers/mdc-helpers';
1+
@use '../core/tokens/m2/mat/menu' as tokens-mat-menu;
2+
@use '../core/style/sass-utils';
3+
@use '../core/tokens/token-utils';
84
@use '../core/theming/theming';
95
@use '../core/typography/typography';
106

117

128
@mixin color($config-or-theme) {
139
$config: theming.get-color-config($config-or-theme);
14-
@include mdc-helpers.using-mdc-theme($config) {
15-
@include mdc-menu-surface.core-styles(mdc-helpers.$mdc-theme-styles-query);
1610

17-
.mat-mdc-menu-panel-wrapper {
18-
@include mdc-list.without-ripple(mdc-helpers.$mdc-theme-styles-query);
19-
}
20-
21-
// MDC doesn't appear to have disabled styling for menu
22-
// items so we have to grey them out ourselves.
23-
.mat-mdc-menu-item[disabled] {
24-
&,
25-
.mat-mdc-menu-submenu-icon,
26-
.mat-icon-no-color {
27-
@include mdc-theme.prop(color, text-disabled-on-background);
28-
}
29-
}
30-
31-
// Since we're creating `mat-icon` and the submenu trigger
32-
// chevron ourselves, we have to handle the color as well.
33-
.mat-mdc-menu-item .mat-icon-no-color,
34-
.mat-mdc-menu-submenu-icon {
35-
@include mdc-theme.prop(color, text-primary-on-background);
36-
}
37-
38-
// MDC's hover and focus styles are tied to their ripples which we aren't using.
39-
.mat-mdc-menu-item:hover,
40-
.mat-mdc-menu-item.cdk-program-focused,
41-
.mat-mdc-menu-item.cdk-keyboard-focused,
42-
.mat-mdc-menu-item-highlighted {
43-
&:not([disabled]) {
44-
$color: mdc-theme-color.$on-surface;
45-
background: rgba($color, mdc-ripple.states-opacity($color, hover));
46-
}
47-
}
11+
@include sass-utils.current-selector-or-root() {
12+
@include token-utils.create-token-values(tokens-mat-menu.$prefix,
13+
tokens-mat-menu.get-color-tokens($config));
4814
}
4915
}
5016

5117
@mixin typography($config-or-theme) {
5218
$config: typography.private-typography-to-2018-config(
5319
theming.get-typography-config($config-or-theme));
54-
@include mdc-helpers.using-mdc-typography($config) {
55-
@include mdc-menu-surface.core-styles(mdc-helpers.$mdc-typography-styles-query);
56-
57-
.mat-mdc-menu-content {
58-
// Note that we include this private mixin, because the public
59-
// one adds a bunch of styles that we aren't using for the menu.
60-
@include mdc-list.list-base(mdc-helpers.$mdc-typography-styles-query);
61-
62-
// MDC uses the `subtitle1` level for list items, but the spec shows `body1` as the correct
63-
// level.
64-
&,
65-
.mat-mdc-menu-item .mdc-list-item__primary-text {
66-
@include mdc-typography.typography(body1, $query: mdc-helpers.$mdc-typography-styles-query);
67-
}
68-
}
20+
@include sass-utils.current-selector-or-root() {
21+
@include token-utils.create-token-values(tokens-mat-menu.$prefix,
22+
tokens-mat-menu.get-typography-tokens($config));
6923
}
7024
}
7125

src/material/menu/menu-trigger.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
9696
private _menuCloseSubscription = Subscription.EMPTY;
9797
private _scrollStrategy: () => ScrollStrategy;
9898
private _changeDetectorRef = inject(ChangeDetectorRef);
99-
protected _panelClass: string | null;
10099

101100
/**
102101
* We're specifically looking for a `MatMenu` here since the generic `MatMenuPanel`
@@ -330,10 +329,6 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
330329
this._closingActionsSubscription = this._menuClosingActions().subscribe(() => this.closeMenu());
331330
this._initMenu(menu);
332331

333-
if (this._panelClass) {
334-
overlayRef.overlayElement.classList.add(this._panelClass);
335-
}
336-
337332
if (menu instanceof _MatMenuBase) {
338333
menu._startAnimation();
339334
menu._directDescendantItems.changes.pipe(takeUntil(menu.close)).subscribe(() => {
@@ -691,6 +686,4 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
691686
},
692687
exportAs: 'matMenuTrigger',
693688
})
694-
export class MatMenuTrigger extends _MatMenuTriggerBase {
695-
protected override _panelClass = 'mat-mdc-menu-panel-wrapper';
696-
}
689+
export class MatMenuTrigger extends _MatMenuTriggerBase {}

src/material/menu/menu.scss

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,50 @@
11
@use 'sass:map';
22
@use '@angular/cdk';
3-
@use '@material/menu-surface' as mdc-menu-surface;
43
@use '@material/list/evolution-mixins' as mdc-list-mixins;
54
@use '@material/list/evolution-variables' as mdc-list-variables;
5+
@use '@material/typography/typography' as mdc-typography;
6+
@use '../core/tokens/m2/mat/menu' as tokens-mat-menu;
7+
@use '../core/tokens/token-utils';
68
@use '../core/mdc-helpers/mdc-helpers';
79
@use '../core/style/menu-common';
810
@use '../core/style/button-common';
911

10-
11-
@include mdc-helpers.disable-mdc-fallback-declarations {
12-
@include mdc-menu-surface.core-styles($query: structure);
13-
}
14-
1512
// Prevent rendering mat-menu as it can affect the flex layout.
1613
mat-menu {
1714
display: none;
1815
}
1916

2017
.mat-mdc-menu-content {
2118
@include mdc-list-mixins.list-base($query: structure);
19+
20+
&,
21+
.mat-mdc-menu-item .mdc-list-item__primary-text {
22+
@include mdc-typography.smooth-font();
23+
@include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) {
24+
@include token-utils.create-token-slot(font-family, item-label-text-font);
25+
@include token-utils.create-token-slot(line-height, item-label-text-line-height);
26+
@include token-utils.create-token-slot(font-size, item-label-text-size);
27+
@include token-utils.create-token-slot(letter-spacing, item-label-text-tracking);
28+
@include token-utils.create-token-slot(font-weight, item-label-text-weight);
29+
}
30+
}
2231
}
2332

2433
.mat-mdc-menu-panel {
34+
@include token-utils.create-token-values(tokens-mat-menu.$prefix,
35+
tokens-mat-menu.get-unthemable-tokens());
36+
37+
@include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) {
38+
@include token-utils.create-token-slot(border-radius, container-shape);
39+
@include token-utils.create-token-slot(background-color, container-color);
40+
}
41+
42+
// TODO(crisbeto): we don't need this for anything, but it was there when
43+
// we used MDC's structural styles and removing it leads to sub-pixels
44+
// differences in text rendering which break a lot of screenshots internally.
45+
// We should clean it up eventually and re-approve all the screenshots.
46+
will-change: transform, opacity;
47+
2548
// Prevent users from interacting with the panel while it's animating. Note that
2649
// people won't be able to click through it, because the overlay pane will catch the click.
2750
// This fixes the following issues:
@@ -78,6 +101,15 @@ mat-menu {
78101
$height-config: map.get(mdc-list-variables.$one-line-item-density-config, height);
79102
min-height: map.get($height-config, default);
80103

104+
@include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) {
105+
@include token-utils.create-token-slot(color, item-label-text-color);
106+
107+
.mat-icon-no-color,
108+
.mat-mdc-menu-submenu-icon {
109+
@include token-utils.create-token-slot(color, item-icon-color);
110+
}
111+
}
112+
81113
// If the MDC list is loaded after the menu, this gets overwritten which breaks the text
82114
// alignment. Ideally we'd wrap all the MDC mixins above with this selector, but the increased
83115
// specificity breaks some internal overrides.
@@ -130,6 +162,20 @@ mat-menu {
130162
@include menu-common.item-submenu-trigger(mdc-list-variables.$side-padding);
131163
}
132164

165+
&:not([disabled]) {
166+
@include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) {
167+
&:hover {
168+
@include token-utils.create-token-slot(background-color, item-hover-state-layer-color);
169+
}
170+
171+
&.cdk-program-focused,
172+
&.cdk-keyboard-focused,
173+
&.mat-mdc-menu-item-highlighted {
174+
@include token-utils.create-token-slot(background-color, item-focus-state-layer-color);
175+
}
176+
}
177+
}
178+
133179
@include cdk.high-contrast(active, off) {
134180
$outline-width: 1px;
135181

tools/public_api_guard/material/menu.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,6 @@ export interface MatMenuPanel<T = any> {
274274

275275
// @public
276276
export class MatMenuTrigger extends _MatMenuTriggerBase {
277-
// (undocumented)
278-
protected _panelClass: string;
279277
// (undocumented)
280278
static ɵdir: i0.ɵɵDirectiveDeclaration<MatMenuTrigger, "[mat-menu-trigger-for], [matMenuTriggerFor]", ["matMenuTrigger"], {}, {}, never, never, false, never>;
281279
// (undocumented)
@@ -315,8 +313,6 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
315313
// (undocumented)
316314
_openedBy: Exclude<FocusOrigin, 'program' | null> | undefined;
317315
openMenu(): void;
318-
// (undocumented)
319-
protected _panelClass: string | null;
320316
restoreFocus: boolean;
321317
toggleMenu(): void;
322318
triggersSubmenu(): boolean;

0 commit comments

Comments
 (0)