@use '@angular/cdk'; @use '../core/tokens/m2/mat/menu' as tokens-mat-menu; @use '../core/tokens/token-utils'; @use '../core/style/menu-common'; @use '../core/style/button-common'; @use '../core/style/vendor-prefixes'; // Prevent rendering mat-menu as it can affect the flex layout. mat-menu { display: none; } .mat-mdc-menu-content { margin: 0; padding: 8px 0; outline: 0; &, .mat-mdc-menu-item .mat-mdc-menu-item-text { @include vendor-prefixes.smooth-font(); flex: 1; white-space: normal; @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 menu-common.base; 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); @include token-utils.create-token-slot(box-shadow, container-elevation-shadow); } // 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: // * Users accidentally opening sub-menus when the `overlapTrigger` option is enabled. // * Users accidentally tapping on content inside the sub-menu on touch devices, if the // sub-menu overlaps the trigger. The issue is due to touch devices emulating the // `mouseenter` event by dispatching it on tap. &.ng-animating { pointer-events: none; // If the same menu is assigned to multiple triggers and the user moves quickly between them // (e.g. in a nested menu), the panel for the old menu may show up as empty while it's // animating away. Hide such cases since they can look off to users. &:has(.mat-mdc-menu-content:empty) { display: none; } } @include cdk.high-contrast { outline: solid 1px; } .mat-divider { // Use margin instead of padding since divider uses border-top to render out the line. @include token-utils.use-tokens( tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots() ) { color: token-utils.get-token-variable(divider-color); margin-bottom: token-utils.get-token-variable(divider-bottom-spacing); margin-top: token-utils.get-token-variable(divider-top-spacing); } } } .mat-mdc-menu-item { display: flex; position: relative; align-items: center; justify-content: flex-start; overflow: hidden; padding: 0; // MDC's menu items are `
  • ` nodes which don't need resets, however ours // can be anything, including buttons, so we need to do the reset ourselves. cursor: pointer; width: 100%; text-align: left; box-sizing: border-box; color: inherit; font-size: inherit; background: none; text-decoration: none; margin: 0; // Resolves an issue where buttons have an extra 2px margin on Safari. min-height: 48px; @include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) { @include token-utils.create-token-slot(padding-left, item-leading-spacing); @include token-utils.create-token-slot(padding-right, item-trailing-spacing); } @include button-common.reset; @include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) { $icons-selector: '.material-icons, mat-icon, [matButtonIcon]'; [dir='rtl'] & { @include token-utils.create-token-slot(padding-left, item-trailing-spacing); @include token-utils.create-token-slot(padding-right, item-leading-spacing); } &:has(#{$icons-selector}) { @include token-utils.create-token-slot(padding-left, item-with-icon-leading-spacing); @include token-utils.create-token-slot(padding-right, item-with-icon-trailing-spacing); } [dir='rtl'] &:has(#{$icons-selector}) { @include token-utils.create-token-slot(padding-left, item-with-icon-trailing-spacing); @include token-utils.create-token-slot(padding-right, item-with-icon-leading-spacing); } } @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); } } &[disabled] { cursor: default; opacity: 0.38; // The browser prevents clicks on disabled buttons from propagating which prevents the menu // from closing, but clicks on child nodes still propagate which is inconsistent (see #16694). // In order to keep the behavior consistent and prevent the menu from closing, we add an overlay // on top of the content that will catch all the clicks while disabled. &::after { display: block; position: absolute; content: ''; top: 0; left: 0; bottom: 0; right: 0; } } // Inherited from MDC and necessary for some internal tests. &:focus { outline: 0; } .mat-icon { flex-shrink: 0; @include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) { margin-right: token-utils.get-token-variable(item-spacing); height: token-utils.get-token-variable(item-icon-size); width: token-utils.get-token-variable(item-icon-size); } } [dir='rtl'] & { text-align: right; .mat-icon { margin-right: 0; @include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) { margin-left: token-utils.get-token-variable(item-spacing); } } } &: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 { $outline-width: 1px; // We need to move the item 1px down, because Firefox seems to have // an issue rendering the top part of the outline (see #21524). margin-top: $outline-width; } } .mat-mdc-menu-submenu-icon { @include token-utils.use-tokens(tokens-mat-menu.$prefix, tokens-mat-menu.get-token-slots()) { @include menu-common.item-submenu-icon( token-utils.get-token-variable(item-spacing), token-utils.get-token-variable(item-icon-size) ); } } // Increase specificity because ripple styles are part of the `mat-core` mixin and can // potentially overwrite the absolute position of the container. .mat-mdc-menu-item .mat-mdc-menu-ripple { @include menu-common.item-ripple; }