From e2f578a3b0586ffd7c57a720c6d109b450f3d326 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 2 Jul 2024 17:06:49 +0000 Subject: [PATCH] refactor(material/chips): simplify structural styles (#29357) Simplifies the structural styles for the chips to make them simpler and easier to maintain. (cherry picked from commit f8d9d167e90d2d57c955842fe07b15d94263ce8a) --- src/material/chips/_chips-theme.scss | 46 +- src/material/chips/chip-set.scss | 35 +- src/material/chips/chip.scss | 691 +++++++++++++----- src/material/core/tokens/m2/mdc/_chip.scss | 16 +- src/material/core/tokens/m3/mdc/_chip.scss | 5 +- .../tokens/tests/test-validate-tokens.scss | 6 - 6 files changed, 559 insertions(+), 240 deletions(-) diff --git a/src/material/chips/_chips-theme.scss b/src/material/chips/_chips-theme.scss index 736b195ec173..2528a8685bdb 100644 --- a/src/material/chips/_chips-theme.scss +++ b/src/material/chips/_chips-theme.scss @@ -1,5 +1,4 @@ @use 'sass:color'; -@use '@material/chips/chip-theme' as mdc-chip-theme; @use '../core/tokens/m2/mdc/chip' as tokens-mdc-chip; @use '../core/tokens/m2/mat/chip' as tokens-mat-chip; @use '../core/tokens/token-utils'; @@ -17,9 +16,10 @@ } @else { .mat-mdc-standard-chip { - @include mdc-chip-theme.theme(tokens-mdc-chip.get-unthemable-tokens()); @include token-utils.create-token-values( - tokens-mat-chip.$prefix, tokens-mat-chip.get-unthemable-tokens()); + tokens-mdc-chip.$prefix, tokens-mdc-chip.get-unthemable-tokens()); + @include token-utils.create-token-values( + tokens-mat-chip.$prefix, tokens-mat-chip.get-unthemable-tokens()); } } } @@ -35,32 +35,32 @@ } @else { .mat-mdc-standard-chip { - $default-color-tokens: tokens-mdc-chip.get-color-tokens($theme); - @include mdc-chip-theme.theme($default-color-tokens); @include token-utils.create-token-values( - tokens-mat-chip.$prefix, tokens-mat-chip.get-color-tokens($theme)); + tokens-mdc-chip.$prefix, tokens-mdc-chip.get-color-tokens($theme)); + @include token-utils.create-token-values( + tokens-mat-chip.$prefix, tokens-mat-chip.get-color-tokens($theme)); &.mat-mdc-chip-selected, &.mat-mdc-chip-highlighted { &.mat-primary { - $primary-color-tokens: tokens-mdc-chip.get-color-tokens($theme, primary); - @include mdc-chip-theme.theme($primary-color-tokens); @include token-utils.create-token-values( - tokens-mat-chip.$prefix, tokens-mat-chip.get-color-tokens($theme, primary)); + tokens-mdc-chip.$prefix, tokens-mdc-chip.get-color-tokens($theme, primary)); + @include token-utils.create-token-values( + tokens-mat-chip.$prefix, tokens-mat-chip.get-color-tokens($theme, primary)); } &.mat-accent { - $accent-color-tokens: tokens-mdc-chip.get-color-tokens($theme, accent); - @include mdc-chip-theme.theme($accent-color-tokens); @include token-utils.create-token-values( - tokens-mat-chip.$prefix, tokens-mat-chip.get-color-tokens($theme, accent)); + tokens-mdc-chip.$prefix, tokens-mdc-chip.get-color-tokens($theme, accent)); + @include token-utils.create-token-values( + tokens-mat-chip.$prefix, tokens-mat-chip.get-color-tokens($theme, accent)); } &.mat-warn { - $warn-color-tokens: tokens-mdc-chip.get-color-tokens($theme, warn); - @include mdc-chip-theme.theme($warn-color-tokens); @include token-utils.create-token-values( - tokens-mat-chip.$prefix, tokens-mat-chip.get-color-tokens($theme, warn)); + tokens-mdc-chip.$prefix, tokens-mdc-chip.get-color-tokens($theme, warn)); + @include token-utils.create-token-values( + tokens-mat-chip.$prefix, tokens-mat-chip.get-color-tokens($theme, warn)); } } } @@ -74,12 +74,11 @@ @include _theme-from-tokens(inspection.get-theme-tokens($theme, typography)); } @else { - $typography-tokens: tokens-mdc-chip.get-typography-tokens($theme); - .mat-mdc-standard-chip { - @include mdc-chip-theme.theme($typography-tokens); @include token-utils.create-token-values( - tokens-mat-chip.$prefix, tokens-mat-chip.get-typography-tokens($theme)); + tokens-mdc-chip.$prefix, tokens-mdc-chip.get-typography-tokens($theme)); + @include token-utils.create-token-values( + tokens-mat-chip.$prefix, tokens-mat-chip.get-typography-tokens($theme)); } } } @@ -91,12 +90,11 @@ @include _theme-from-tokens(inspection.get-theme-tokens($theme, density)); } @else { - $density-tokens: tokens-mdc-chip.get-density-tokens($theme); - .mat-mdc-chip.mat-mdc-standard-chip { - @include mdc-chip-theme.theme($density-tokens); @include token-utils.create-token-values( - tokens-mat-chip.$prefix, tokens-mat-chip.get-density-tokens($theme)); + tokens-mdc-chip.$prefix, tokens-mdc-chip.get-density-tokens($theme)); + @include token-utils.create-token-values( + tokens-mat-chip.$prefix, tokens-mat-chip.get-density-tokens($theme)); } } } @@ -141,6 +139,6 @@ 'Calls to Angular Material theme mixins with an M3 theme must be wrapped in a selector'); $mdc-chip-tokens: token-utils.get-tokens-for($tokens, tokens-mdc-chip.$prefix, $options...); $mat-chip-tokens: token-utils.get-tokens-for($tokens, tokens-mat-chip.$prefix, $options...); - @include mdc-chip-theme.theme($mdc-chip-tokens); + @include token-utils.create-token-values(tokens-mdc-chip.$prefix, $mdc-chip-tokens); @include token-utils.create-token-values(tokens-mat-chip.$prefix, $mat-chip-tokens); } diff --git a/src/material/chips/chip-set.scss b/src/material/chips/chip-set.scss index 3ff0bb4d8a37..a4631248db7d 100644 --- a/src/material/chips/chip-set.scss +++ b/src/material/chips/chip-set.scss @@ -1,14 +1,39 @@ -@use '@material/chips/chip-set' as mdc-chip-set; -@use '../core/mdc-helpers/mdc-helpers'; - -@include mdc-chip-set.core-styles($query: mdc-helpers.$mdc-base-styles-query); - // Ensures that the internal chip container spans the entire outer container width, if the // outer container width is customized. This is used by some wrapper components in g3. .mat-mdc-chip-set { + display: flex; + + &:focus { + outline: none; + } + .mdc-evolution-chip-set__chips { min-width: 100%; + margin-left: -8px; + margin-right: 0; + } + + .mdc-evolution-chip { + margin: 4px 0 4px 8px; } + + [dir='rtl'] & { + .mdc-evolution-chip-set__chips { + margin-left: 0; + margin-right: -8px; + } + + .mdc-evolution-chip { + margin-left: 0; + margin-right: 8px; + } + } +} + +.mdc-evolution-chip-set__chips { + display: flex; + flex-flow: wrap; + min-width: 0; } // Angular Material supports vertically-stacked chips, which MDC does not. diff --git a/src/material/chips/chip.scss b/src/material/chips/chip.scss index b40993846fe2..cb7ab142ef12 100644 --- a/src/material/chips/chip.scss +++ b/src/material/chips/chip.scss @@ -1,228 +1,564 @@ @use '@angular/cdk'; -@use '@material/chips/chip' as mdc-chip; -@use '@material/chips/chip-theme' as mdc-chip-theme; -@use '../core/mdc-helpers/mdc-helpers'; @use '../core/style/layout-common'; @use '../core/focus-indicators/private' as focus-indicators-private; @use '../core/tokens/m2/mdc/chip' as tokens-mdc-chip; @use '../core/tokens/m2/mat/chip' as tokens-mat-chip; +@use '../core/style/vendor-prefixes'; @use '../core/tokens/token-utils'; -@use '@material/theme/custom-properties' as mdc-custom-properties; -// The slots for tokens that will be configured in the theme can be emitted with no fallback. -@include mdc-custom-properties.configure($emit-fallback-values: false, $emit-fallback-vars: false) { - $mdc-chip-token-slots: tokens-mdc-chip.get-token-slots(); - $mat-chip-token-slots: tokens-mat-chip.get-token-slots(); +$_checkmark-size: 20px; +$_trailing-icon-size: 18px; +$_action-padding: 12px; +$_graphic-padding: 6px; +$_trailing-action-padding: 8px; +$_avatar-leading-padding: 4px; +$_avatar-trailing-padding: 8px; + +.mdc-evolution-chip, +.mdc-evolution-chip__cell, +.mdc-evolution-chip__action { + display: inline-flex; + align-items: center; +} - // Add the MDC chip static styles. - @include mdc-chip.static-styles(); +.mdc-evolution-chip { + position: relative; + max-width: 100%; +} - .mat-mdc-standard-chip { - // Add the official slots for the MDC chip. - @include mdc-chip-theme.theme-styles($mdc-chip-token-slots); - } - - // The highlighted attribute is used to make the chip appear as selected on-demand, - // aside from showing the selected indicator. We achieve this by re-mapping the base - // tokens to the highlighted ones. Note that we only need to do this for the tokens - // that we don't re-implement ourselves below. - // TODO(crisbeto): with some future refactors we may be able to clean this up. - .mat-mdc-chip-highlighted { - @include token-utils.use-tokens(tokens-mdc-chip.$prefix, $mdc-chip-token-slots) { - $highlighted-remapped-tokens: ( - with-icon-icon-color: with-icon-selected-icon-color, - elevated-container-color: elevated-selected-container-color, - label-text-color: selected-label-text-color, - outline-width: flat-selected-outline-width, - ); +.mdc-evolution-chip__cell, +.mdc-evolution-chip__action { + height: 100%; +} - @each $selected, $base in $highlighted-remapped-tokens { - #{token-utils.get-token-variable($selected)}: var(token-utils.get-token-variable($base)); - } +.mdc-evolution-chip__cell--primary { + // Ensures that the trailing icon is pushed to the end if the chip has a set width. + flex-basis: 100%; + overflow-x: hidden; +} + +.mdc-evolution-chip__cell--trailing { + flex: 1 0 auto; +} + +.mdc-evolution-chip__action { + align-items: center; + background: none; + border: none; + box-sizing: content-box; + cursor: pointer; + display: inline-flex; + justify-content: center; + outline: none; + padding: 0; + text-decoration: none; + color: inherit; +} + +.mdc-evolution-chip__action--presentational { + cursor: auto; +} + +.mdc-evolution-chip--disabled, +.mdc-evolution-chip__action:disabled { + pointer-events: none; +} + +.mdc-evolution-chip__action--primary { + // This element can be placed on a `button` node which usually has some user agent styles. + // Reset the font so that the typography from the root element can propagate down. + font: inherit; + letter-spacing: inherit; + white-space: inherit; + overflow-x: hidden; + + @include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) { + .mat-mdc-standard-chip &::before { + @include token-utils.create-token-slot(border-width, outline-width); + @include token-utils.create-token-slot(border-radius, container-shape-radius); + box-sizing: border-box; + content: ''; + height: 100%; + left: 0; + position: absolute; + pointer-events: none; + top: 0; + width: 100%; + z-index: 1; + border-style: solid; + } + + .mat-mdc-standard-chip & { + padding-left: $_action-padding; + padding-right: $_action-padding; + } + + .mat-mdc-standard-chip.mdc-evolution-chip--with-primary-graphic & { + padding-left: 0; + padding-right: $_action-padding; + } + + [dir='rtl'] .mat-mdc-standard-chip.mdc-evolution-chip--with-primary-graphic & { + padding-left: $_action-padding; + padding-right: 0; + } + + .mat-mdc-standard-chip:not(.mdc-evolution-chip--disabled) &::before { + @include token-utils.create-token-slot(border-color, outline-color); + } + + &:not(.mdc-evolution-chip__action--presentational):not(.mdc-ripple-upgraded):focus::before { + @include token-utils.create-token-slot(border-color, focus-outline-color); + } + + .mat-mdc-standard-chip.mdc-evolution-chip--disabled &::before { + @include token-utils.create-token-slot(border-color, disabled-outline-color); + } + + .mat-mdc-standard-chip.mdc-evolution-chip--selected &::before { + @include token-utils.create-token-slot(border-width, flat-selected-outline-width); } } - // Add additional slots for the MDC chip tokens, needed in Angular Material. - $disabled-trailing-icon-opacity: 1; + // Keeps basic listbox chips looking consistent with the other variations. Listbox chips don't + // inherit the font size, because they wrap the label in a `button` that has user agent styles. + .mat-mdc-basic-chip & { + font: inherit; + } - @include token-utils.use-tokens(tokens-mdc-chip.$prefix, $mdc-chip-token-slots) { - .mat-mdc-chip-focus-overlay { - @include token-utils.create-token-slot(background, focus-state-layer-color); + // Moved out into variables, because the selectors are too long. + $with-graphic: '.mdc-evolution-chip--with-primary-graphic'; + $with-trailing: '.mdc-evolution-chip--with-trailing-action'; - .mat-mdc-chip-selected &, - .mat-mdc-chip-highlighted & { - @include token-utils.create-token-slot(background, selected-focus-state-layer-color); - } + .mat-mdc-standard-chip#{$with-trailing} & { + padding-left: $_action-padding; + padding-right: 0; + } - .mat-mdc-chip:hover & { - @include token-utils.create-token-slot(background, hover-state-layer-color); - @include token-utils.create-token-slot(opacity, hover-state-layer-opacity); - } + [dir='rtl'] .mat-mdc-standard-chip#{$with-trailing} & { + padding-left: 0; + padding-right: $_action-padding; + } - .mat-mdc-chip-selected:hover, - .mat-mdc-chip-highlighted:hover & { - @include token-utils.create-token-slot(background, selected-hover-state-layer-color); - @include token-utils.create-token-slot(opacity, selected-hover-state-layer-opacity); - } + .mat-mdc-standard-chip#{$with-graphic}#{$with-trailing} & { + padding-left: 0; + padding-right: 0; + } - .mat-mdc-chip.cdk-focused & { - @include token-utils.create-token-slot(background, focus-state-layer-color); - @include token-utils.create-token-slot(opacity, focus-state-layer-opacity); - } + [dir='rtl'] .mat-mdc-standard-chip#{$with-graphic}#{$with-trailing} & { + padding-left: 0; + padding-right: 0; + } - .mat-mdc-chip-selected.cdk-focused &, - .mat-mdc-chip-highlighted.cdk-focused & { - @include token-utils.create-token-slot(background, selected-focus-state-layer-color); - @include token-utils.create-token-slot(opacity, selected-focus-state-layer-opacity); - } + .mdc-evolution-chip--with-avatar#{$with-graphic} & { + padding-left: 0; + padding-right: $_action-padding; + } + + [dir='rtl'] .mdc-evolution-chip--with-avatar#{$with-graphic} & { + padding-left: $_action-padding; + padding-right: 0; + } + + .mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & { + padding-left: 0; + padding-right: 0; + } + + [dir='rtl'] .mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & { + padding-left: 0; + padding-right: 0; + } +} + +.mdc-evolution-chip__action--trailing { + position: relative; + overflow: visible; + + @include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) { + .mat-mdc-standard-chip:not(.mdc-evolution-chip--disabled) & { + @include token-utils.create-token-slot(color, with-trailing-icon-trailing-icon-color); } - .mdc-evolution-chip--disabled:not(.mdc-evolution-chip--selected) .mat-mdc-chip-avatar { - @include token-utils.create-token-slot(opacity, with-avatar-disabled-avatar-opacity); + .mat-mdc-standard-chip.mdc-evolution-chip--disabled & { + @include token-utils.create-token-slot(color, + with-trailing-icon-disabled-trailing-icon-color); } + } + + // Moved out into variables, because the selectors are too long. + $with-graphic: '.mdc-evolution-chip--with-primary-graphic'; + $with-trailing: '.mdc-evolution-chip--with-trailing-action'; - .mdc-evolution-chip--disabled .mdc-evolution-chip__icon--trailing { - $disabled-trailing-icon-opacity: - token-utils.get-token-variable(with-trailing-icon-disabled-trailing-icon-opacity); + .mat-mdc-standard-chip#{$with-trailing} & { + padding-left: $_trailing-action-padding; + padding-right: $_trailing-action-padding; + } - @include token-utils.create-token-slot( - opacity, with-trailing-icon-disabled-trailing-icon-opacity); + .mat-mdc-standard-chip#{$with-graphic}#{$with-trailing} & { + padding-left: $_trailing-action-padding; + padding-right: $_trailing-action-padding; + } + + + .mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & { + padding-left: $_avatar-trailing-padding; + padding-right: $_avatar-trailing-padding; + } + + [dir='rtl'] .mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & { + padding-left: $_avatar-trailing-padding; + padding-right: $_avatar-trailing-padding; + } + +} + +.mdc-evolution-chip__text-label { + @include vendor-prefixes.user-select(none); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + + @include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) { + .mat-mdc-standard-chip & { + @include token-utils.create-token-slot(font-family, label-text-font); + @include token-utils.create-token-slot(line-height, label-text-line-height); + @include token-utils.create-token-slot(font-size, label-text-size); + @include token-utils.create-token-slot(font-weight, label-text-weight); + @include token-utils.create-token-slot(letter-spacing, label-text-tracking); } - .mdc-evolution-chip--disabled.mdc-evolution-chip--selected .mdc-evolution-chip__checkmark { - @include token-utils.create-token-slot(opacity, with-icon-disabled-icon-opacity); + .mat-mdc-standard-chip:not(.mdc-evolution-chip--disabled) & { + @include token-utils.create-token-slot(color, label-text-color); } - } - @include token-utils.use-tokens(tokens-mat-chip.$prefix, $mat-chip-token-slots) { - // Historically, MDC did not support disabled chips, so we needed our own disabled styles. - // Now that MDC supports disabled styles, we should switch to using theirs. - .mat-mdc-standard-chip { - &.mdc-evolution-chip--disabled { - @include token-utils.create-token-slot(opacity, disabled-container-opacity); - } + .mat-mdc-standard-chip.mdc-evolution-chip--selected:not(.mdc-evolution-chip--disabled) & { + @include token-utils.create-token-slot(color, selected-label-text-color); + } - &.mdc-evolution-chip--selected, - &.mat-mdc-chip-highlighted { - .mdc-evolution-chip__icon--trailing { - @include token-utils.create-token-slot(color, selected-trailing-icon-color); - } + .mat-mdc-standard-chip.mdc-evolution-chip--disabled &, + .mat-mdc-standard-chip.mdc-evolution-chip--selected.mdc-evolution-chip--disabled & { + @include token-utils.create-token-slot(color, disabled-label-text-color); + } + } +} - &.mdc-evolution-chip--disabled .mdc-evolution-chip__icon--trailing { - @include token-utils.create-token-slot(color, selected-disabled-trailing-icon-color); - } - } +.mdc-evolution-chip__graphic { + align-items: center; + display: inline-flex; + justify-content: center; + overflow: hidden; + pointer-events: none; + position: relative; + flex: 1 0 auto; + + @include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) { + .mat-mdc-standard-chip & { + @include token-utils.create-token-slot(width, with-avatar-avatar-size); + @include token-utils.create-token-slot(height, with-avatar-avatar-size); + @include token-utils.create-token-slot(font-size, with-avatar-avatar-size); } + } - .mat-mdc-chip-remove { - @include token-utils.create-token-slot(opacity, trailing-action-opacity); + .mdc-evolution-chip--selecting & { + transition: width 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1); + } - &:focus { - @include token-utils.create-token-slot(opacity, trailing-action-focus-opacity); - } + // Moved out into variables, because the selectors are too long. + $with-icon: '.mdc-evolution-chip--with-primary-icon'; + $with-graphic: '.mdc-evolution-chip--with-primary-graphic'; + $with-trailing: '.mdc-evolution-chip--with-trailing-action'; - &::after { - @include token-utils.create-token-slot(background, trailing-action-state-layer-color); - } + .mdc-evolution-chip--selectable:not(.mdc-evolution-chip--selected):not(#{$with-icon}) & { + width: 0; + } - &:hover::after { - @include token-utils.create-token-slot(opacity, trailing-action-hover-state-layer-opacity); - } + .mat-mdc-standard-chip#{$with-graphic} & { + padding-left: $_graphic-padding; + padding-right: $_graphic-padding; + } - &:focus::after { - @include token-utils.create-token-slot(opacity, trailing-action-focus-state-layer-opacity); - } + .mdc-evolution-chip--with-avatar#{$with-graphic} & { + padding-left: $_avatar-leading-padding; + padding-right: $_avatar-trailing-padding; + } + + [dir='rtl'] .mdc-evolution-chip--with-avatar#{$with-graphic} & { + padding-left: $_avatar-trailing-padding; + padding-right: $_avatar-leading-padding; + } + + .mat-mdc-standard-chip#{$with-graphic}#{$with-trailing} & { + padding-left: $_graphic-padding; + padding-right: $_graphic-padding; + } + + .mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & { + padding-left: $_avatar-leading-padding; + padding-right: $_avatar-trailing-padding; + } + + [dir='rtl'] .mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & { + padding-left: $_avatar-trailing-padding; + padding-right: $_avatar-leading-padding; + } +} + +.mdc-evolution-chip__checkmark { + position: absolute; + opacity: 0; + top: 50%; + left: 50%; + height: $_checkmark-size; + width: $_checkmark-size; + + @include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) { + .mat-mdc-standard-chip:not(.mdc-evolution-chip--disabled) & { + @include token-utils.create-token-slot(color, with-icon-selected-icon-color); } - .mat-mdc-chip-selected .mat-mdc-chip-remove::after, - .mat-mdc-chip-highlighted .mat-mdc-chip-remove::after { - @include token-utils.create-token-slot( - background, selected-trailing-action-state-layer-color); + .mat-mdc-standard-chip.mdc-evolution-chip--disabled & { + @include token-utils.create-token-slot(color, with-icon-disabled-icon-color); } + } - // If the trailing icon is a chip-remove button, we have to factor in the trailing action - // opacity as well as the disabled opacity. - .mdc-evolution-chip--disabled .mdc-evolution-chip__icon--trailing { - &.mat-mdc-chip-remove { + .mdc-evolution-chip--selecting & { + transition: transform 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1); + transform: translate(-75%, -50%); + } + + .mdc-evolution-chip--selected & { + transform: translate(-50%, -50%); + opacity: 1; + } +} + +.mdc-evolution-chip__checkmark-svg { + display: block; +} + +.mdc-evolution-chip__checkmark-path { + stroke-width: 2px; + stroke-dasharray: 29.7833385; + stroke-dashoffset: 29.7833385; + stroke: currentColor; + + .mdc-evolution-chip--selecting & { + transition: stroke-dashoffset 150ms 45ms cubic-bezier(0.4, 0, 0.2, 1); + } + + .mdc-evolution-chip--selected & { + stroke-dashoffset: 0; + } + + @include cdk.high-contrast(active, off) { + // SVG colors won't be changed in high contrast mode and since the checkmark is white + // by default, it'll blend in with the background in black-on-white mode. Override the + // color to ensure that it's visible. We need !important, because the theme styles are + // very specific. + stroke: CanvasText !important; + } +} + +.mdc-evolution-chip__icon--trailing { + .mat-mdc-standard-chip & { + height: $_trailing-icon-size; + width: $_trailing-icon-size; + font-size: $_trailing-icon-size; + } + + $disabled-icon-opacity: null; + @include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) { + $disabled-icon-opacity: + #{token-utils.get-token-variable(with-trailing-icon-disabled-trailing-icon-opacity)}; + } + + // If the trailing icon is a chip-remove button, we have to factor in the trailing action + // opacity as well as the disabled opacity. + .mdc-evolution-chip--disabled &.mat-mdc-chip-remove { + @include token-utils.use-tokens(tokens-mat-chip.$prefix, tokens-mat-chip.get-token-slots()) { + opacity: calc( + var(#{token-utils.get-token-variable(trailing-action-opacity)}) * + var(#{$disabled-icon-opacity}) + ); + + &:focus { opacity: calc( - var(#{token-utils.get-token-variable(trailing-action-opacity)}) * - var(#{$disabled-trailing-icon-opacity}) + var(#{token-utils.get-token-variable(trailing-action-focus-opacity)}) * + var(#{$disabled-icon-opacity}) ); - - &:focus { - opacity: calc( - var(#{token-utils.get-token-variable(trailing-action-focus-opacity)}) * - var(#{$disabled-trailing-icon-opacity}) - ); - } } } } } -// We *should* be able to include these styles through MDC's -// `theme-styles` mixin, but we can't at the time of writing. -@mixin _missing-mdc-theme-styles() { - .mat-mdc-standard-chip { - @include mdc-chip-theme.outline-style(solid); - @include mdc-chip-theme.checkmark-size(mdc-chip-theme.$checkmark-size); - @include mdc-chip-theme.trailing-action-size(mdc-chip-theme.$trailing-action-size); - @include mdc-chip-theme.horizontal-padding( - mdc-chip-theme.$leading-padding, - mdc-chip-theme.$trailing-padding - ); - @include mdc-chip-theme.with-graphic-horizontal-padding( - mdc-chip-theme.$graphic-leading-padding, - mdc-chip-theme.$graphic-trailing-padding, - mdc-chip-theme.$trailing-padding - ); - @include mdc-chip-theme.with-trailing-action-horizontal-padding( - mdc-chip-theme.$leading-padding, - mdc-chip-theme.$trailing-action-leading-padding, - mdc-chip-theme.$trailing-action-trailing-padding - ); - @include mdc-chip-theme.with-graphic-and-trailing-action-horizontal-padding( - mdc-chip-theme.$graphic-leading-padding, - mdc-chip-theme.$graphic-trailing-padding, - mdc-chip-theme.$trailing-action-leading-padding, - mdc-chip-theme.$trailing-action-trailing-padding - ); +.mat-mdc-standard-chip { + @include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) { + @include token-utils.create-token-slot(border-radius, container-shape-radius); + @include token-utils.create-token-slot(height, container-height); + + &:not(.mdc-evolution-chip--disabled) { + @include token-utils.create-token-slot(background-color, elevated-container-color); + } + + &.mdc-evolution-chip--disabled { + @include token-utils.create-token-slot(background-color, elevated-disabled-container-color); + } + + &.mdc-evolution-chip--selected:not(.mdc-evolution-chip--disabled) { + @include token-utils.create-token-slot(background-color, elevated-selected-container-color); + } + + &.mdc-evolution-chip--selected.mdc-evolution-chip--disabled { + @include token-utils.create-token-slot(background-color, + flat-disabled-selected-container-color); + } } - .mdc-evolution-chip--with-avatar { - @include mdc-chip-theme.with-graphic-horizontal-padding( - mdc-chip-theme.$avatar-leading-padding, - mdc-chip-theme.$avatar-trailing-padding, - mdc-chip-theme.$trailing-padding - ); - @include mdc-chip-theme.with-graphic-and-trailing-action-horizontal-padding( - mdc-chip-theme.$avatar-leading-padding, - mdc-chip-theme.$avatar-trailing-padding, - mdc-chip-theme.$trailing-action-leading-padding, - mdc-chip-theme.$trailing-action-trailing-padding + @include cdk.high-contrast(active, off) { + outline: solid 1px; + } +} + +.mdc-evolution-chip__icon--primary { + @include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) { + .mat-mdc-standard-chip & { + @include token-utils.create-token-slot(border-radius, with-avatar-avatar-shape-radius); + @include token-utils.create-token-slot(width, with-icon-icon-size); + @include token-utils.create-token-slot(height, with-icon-icon-size); + @include token-utils.create-token-slot(font-size, with-icon-icon-size); + } + + .mdc-evolution-chip--selected & { + opacity: 0; + } + + .mat-mdc-standard-chip:not(.mdc-evolution-chip--disabled) & { + @include token-utils.create-token-slot(color, with-icon-icon-color); + } + + .mat-mdc-standard-chip.mdc-evolution-chip--disabled & { + @include token-utils.create-token-slot(color, with-icon-disabled-icon-color); + } + } +} + +// The highlighted attribute is used to make the chip appear as selected on-demand, +// aside from showing the selected indicator. We achieve this by re-mapping the base +// tokens to the highlighted ones. Note that we only need to do this for the tokens +// that we don't re-implement ourselves below. +// TODO(crisbeto): with some future refactors we may be able to clean this up. +.mat-mdc-chip-highlighted { + @include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) { + $highlighted-remapped-tokens: ( + with-icon-icon-color: with-icon-selected-icon-color, + elevated-container-color: elevated-selected-container-color, + label-text-color: selected-label-text-color, + outline-width: flat-selected-outline-width, ); + + @each $selected, $base in $highlighted-remapped-tokens { + #{token-utils.get-token-variable($selected)}: var(token-utils.get-token-variable($base)); + } } } -@include mdc-helpers.disable-mdc-fallback-declarations { - @include _missing-mdc-theme-styles(); +// Add additional slots for the MDC chip tokens, needed in Angular Material. +@include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) { + .mat-mdc-chip-focus-overlay { + @include token-utils.create-token-slot(background, focus-state-layer-color); + + .mat-mdc-chip-selected &, + .mat-mdc-chip-highlighted & { + @include token-utils.create-token-slot(background, selected-focus-state-layer-color); + } + + .mat-mdc-chip:hover & { + @include token-utils.create-token-slot(background, hover-state-layer-color); + @include token-utils.create-token-slot(opacity, hover-state-layer-opacity); + } + + .mat-mdc-chip-selected:hover, + .mat-mdc-chip-highlighted:hover & { + @include token-utils.create-token-slot(background, selected-hover-state-layer-color); + @include token-utils.create-token-slot(opacity, selected-hover-state-layer-opacity); + } + + .mat-mdc-chip.cdk-focused & { + @include token-utils.create-token-slot(background, focus-state-layer-color); + @include token-utils.create-token-slot(opacity, focus-state-layer-opacity); + } + + .mat-mdc-chip-selected.cdk-focused &, + .mat-mdc-chip-highlighted.cdk-focused & { + @include token-utils.create-token-slot(background, selected-focus-state-layer-color); + @include token-utils.create-token-slot(opacity, selected-focus-state-layer-opacity); + } + } + + .mdc-evolution-chip--disabled:not(.mdc-evolution-chip--selected) .mat-mdc-chip-avatar { + @include token-utils.create-token-slot(opacity, with-avatar-disabled-avatar-opacity); + } + + .mdc-evolution-chip--disabled .mdc-evolution-chip__icon--trailing { + @include token-utils.create-token-slot( + opacity, with-trailing-icon-disabled-trailing-icon-opacity); + } + + .mdc-evolution-chip--disabled.mdc-evolution-chip--selected .mdc-evolution-chip__checkmark { + @include token-utils.create-token-slot(opacity, with-icon-disabled-icon-opacity); + } } -.mat-mdc-standard-chip { - -webkit-tap-highlight-color: transparent; +@include token-utils.use-tokens(tokens-mat-chip.$prefix, tokens-mat-chip.get-token-slots()) { + // Historically, MDC did not support disabled chips, so we needed our own disabled styles. + // Now that MDC supports disabled styles, we should switch to using theirs. + .mat-mdc-standard-chip { + &.mdc-evolution-chip--disabled { + @include token-utils.create-token-slot(opacity, disabled-container-opacity); + } - @include cdk.high-contrast(active, off) { - outline: solid 1px; + &.mdc-evolution-chip--selected, + &.mat-mdc-chip-highlighted { + .mdc-evolution-chip__icon--trailing { + @include token-utils.create-token-slot(color, selected-trailing-icon-color); + } - .mdc-evolution-chip__checkmark-path { - // SVG colors won't be changed in high contrast mode and since the checkmark is white - // by default, it'll blend in with the background in black-on-white mode. Override the - // color to ensure that it's visible. We need !important, because the theme styles are - // very specific. - stroke: CanvasText !important; + &.mdc-evolution-chip--disabled .mdc-evolution-chip__icon--trailing { + @include token-utils.create-token-slot(color, selected-disabled-trailing-icon-color); + } } } + .mat-mdc-chip-remove { + @include token-utils.create-token-slot(opacity, trailing-action-opacity); + + &:focus { + @include token-utils.create-token-slot(opacity, trailing-action-focus-opacity); + } + + &::after { + @include token-utils.create-token-slot(background, trailing-action-state-layer-color); + } + + &:hover::after { + @include token-utils.create-token-slot(opacity, trailing-action-hover-state-layer-opacity); + } + + &:focus::after { + @include token-utils.create-token-slot(opacity, trailing-action-focus-state-layer-opacity); + } + } + + .mat-mdc-chip-selected .mat-mdc-chip-remove::after, + .mat-mdc-chip-highlighted .mat-mdc-chip-remove::after { + @include token-utils.create-token-slot(background, selected-trailing-action-state-layer-color); + } +} + +.mat-mdc-standard-chip { + -webkit-tap-highlight-color: transparent; + // MDC sets `overflow: hidden` on these elements in order to truncate the text. This is // unnecessary since our chips don't truncate their text and it makes it difficult to style // the strong focus indicators so we need to override it. @@ -232,19 +568,6 @@ overflow: visible; } - // Ensures that the trailing icon is pushed to the end if the chip has a set width. - .mdc-evolution-chip__cell--primary { - flex-basis: 100%; - } - - // This element can be placed on a `button` node which usually has some user agent styles. - // Reset the font so that the typography from the root element can propagate down. - .mdc-evolution-chip__action--primary { - font: inherit; - letter-spacing: inherit; - white-space: inherit; - } - // MDC sizes and positions this element using `width`, `height` and `padding`. // This usually works, but it's common for apps to add `box-sizing: border-box` // to all elements on the page which can cause the graphic to be clipped. @@ -267,12 +590,6 @@ } } -// Keeps basic listbox chips looking consistent with the other variations. Listbox chips don't -// inherit the font size, because they wrap the label in a `button` that has user agent styles. -.mat-mdc-basic-chip .mdc-evolution-chip__action--primary { - font: inherit; -} - // MDC's focus and hover indication is handled through their ripple which we currently // don't use due to size concerns so we have to re-implement it ourselves. .mat-mdc-chip-focus-overlay { @@ -391,9 +708,9 @@ } .mat-icon { - width: mdc-chip-theme.$trailing-action-size; - height: mdc-chip-theme.$trailing-action-size; - font-size: mdc-chip-theme.$trailing-action-size; + width: $_trailing-icon-size; + height: $_trailing-icon-size; + font-size: $_trailing-icon-size; box-sizing: content-box; } } diff --git a/src/material/core/tokens/m2/mdc/_chip.scss b/src/material/core/tokens/m2/mdc/_chip.scss index 9a5548bef329..7ecc9451c9a2 100644 --- a/src/material/core/tokens/m2/mdc/_chip.scss +++ b/src/material/core/tokens/m2/mdc/_chip.scss @@ -19,21 +19,9 @@ $prefix: (mdc, chip); @function get-unthemable-tokens() { @return ( // The shape & radius of the chip. - container-shape: - ( - family: 'rounded', - radius: ( - 16px 16px 16px 16px, - ) - ), + container-shape-radius: 16px, // The shape & radius of the avatar. - with-avatar-avatar-shape: - ( - family: 'rounded', - radius: ( - 14px 14px 14px 14px, - ) - ), + with-avatar-avatar-shape-radius: 14px, // The width & height of the chip avatar. with-avatar-avatar-size: 28px, // The width & height of the chip icon. diff --git a/src/material/core/tokens/m3/mdc/_chip.scss b/src/material/core/tokens/m3/mdc/_chip.scss index 943ede0c943e..7ba9188def22 100644 --- a/src/material/core/tokens/m3/mdc/_chip.scss +++ b/src/material/core/tokens/m3/mdc/_chip.scss @@ -16,10 +16,7 @@ $prefix: (mdc, chip); $tokens: sass-utils.merge-all( token-utils.generate-typography-tokens($systems, label-text, label-large), ( - container-shape: token-utils.hardcode(( - family: rounded, - radius: 8px, - ), $exclude-hardcoded), + container-shape-radius: token-utils.hardcode(8px, $exclude-hardcoded), with-avatar-avatar-size: token-utils.hardcode(24px, $exclude-hardcoded), label-text-color: map.get($systems, md-sys-color, on-surface-variant), disabled-label-text-color: sass-utils.safe-color-change( diff --git a/src/material/core/tokens/tests/test-validate-tokens.scss b/src/material/core/tokens/tests/test-validate-tokens.scss index 9af8a6fb23ec..b2e4ba548dd6 100644 --- a/src/material/core/tokens/tests/test-validate-tokens.scss +++ b/src/material/core/tokens/tests/test-validate-tokens.scss @@ -18,7 +18,6 @@ @use '@material/tab-indicator/tab-indicator-theme' as mdc-tab-indicator-theme; @use '@material/snackbar/snackbar-theme' as mdc-snackbar-theme; @use '@material/slider/slider-theme' as mdc-slider-theme; -@use '@material/chips/chip-theme' as mdc-chips-theme; @use '@material/dialog/dialog-theme' as mdc-dialog-theme; @use '@material/textfield/filled-text-field-theme' as mdc-filled-text-field-theme; @use '@material/textfield/outlined-text-field-theme' as mdc-outlined-text-field-theme; @@ -123,11 +122,6 @@ $slots: tokens-mdc-slider.get-token-slots(), $reference: mdc-slider-theme.$light-theme ); -@include validate-slots( - $component: 'm2.mdc.chips', - $slots: tokens-mdc-chip.get-token-slots(), - $reference: mdc-chips-theme.$light-theme -); @include validate-slots( $component: 'm2.mdc.dialog', $slots: tokens-mdc-dialog.get-token-slots(),