From eb2a906700e32a0d5bf8671c556566d8e7ef1ca0 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 30 Jun 2023 12:38:21 +0200 Subject: [PATCH] refactor(material/expansion): switch to tokens API Reworks the expansion panel to use the tokens theming API. --- src/material/core/tokens/_token-utils.scss | 1 + .../core/tokens/m2/mat/_expansion.scss | 90 +++++++++++++++++++ .../expansion/_expansion-legacy-index.scss | 1 - .../expansion/_expansion-mixins.import.scss | 1 - src/material/expansion/_expansion-mixins.scss | 11 --- .../expansion/_expansion-theme.import.scss | 2 - src/material/expansion/_expansion-theme.scss | 80 +++-------------- .../expansion/expansion-panel-header.scss | 65 +++++++++++++- src/material/expansion/expansion-panel.scss | 53 ++++++++--- 9 files changed, 203 insertions(+), 101 deletions(-) create mode 100644 src/material/core/tokens/m2/mat/_expansion.scss delete mode 100644 src/material/expansion/_expansion-mixins.import.scss delete mode 100644 src/material/expansion/_expansion-mixins.scss diff --git a/src/material/core/tokens/_token-utils.scss b/src/material/core/tokens/_token-utils.scss index b9fc21118b48..01a14b974135 100644 --- a/src/material/core/tokens/_token-utils.scss +++ b/src/material/core/tokens/_token-utils.scss @@ -39,6 +39,7 @@ $placeholder-typography-config: ( caption: $_placeholder-typography-level-config, button: $_placeholder-typography-level-config, overline: $_placeholder-typography-level-config, + subheading-1: $_placeholder-typography-level-config, ); // Placeholder density config that can be passed to token getter functions when generating token diff --git a/src/material/core/tokens/m2/mat/_expansion.scss b/src/material/core/tokens/m2/mat/_expansion.scss new file mode 100644 index 000000000000..2d850df9ba53 --- /dev/null +++ b/src/material/core/tokens/m2/mat/_expansion.scss @@ -0,0 +1,90 @@ +@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, expansion); + +// 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) { + $foreground: map.get($config, foreground); + $background: map.get($config, background); + + @return ( + container-background-color: theming.get-color-from-palette($background, card), + container-text-color: theming.get-color-from-palette($foreground, text), + actions-divider-color: theming.get-color-from-palette($foreground, divider), + header-hover-state-layer-color: theming.get-color-from-palette($background, hover), + header-focus-state-layer-color: theming.get-color-from-palette($background, hover), + header-disabled-state-text-color: theming.get-color-from-palette($foreground, disabled-button), + header-text-color: theming.get-color-from-palette($foreground, text), + header-description-color: theming.get-color-from-palette($foreground, secondary-text), + header-indicator-color: theming.get-color-from-palette($foreground, secondary-text), + ); +} + +// Tokens that can be configured through Angular Material's typography theming API. +@function get-typography-tokens($config) { + $fallback-font-family: typography-utils.font-family($config); + + @return ( + header-text-font: typography-utils.font-family($config, subheading-1) or $fallback-font-family, + header-text-size: typography-utils.font-size($config, subheading-1), + header-text-weight: typography-utils.font-weight($config, subheading-1), + + // TODO(crisbeto): these two properties weren't set at all before the introduction of tokens, + // but it's inconsistent not to provide them since the container sets all of them. Eventually + // we should change the values to use come from `subheading-1`. + header-text-line-height: inherit, + header-text-tracking: inherit, + + container-text-font: typography-utils.font-family($config, body-1) or $fallback-font-family, + container-text-line-height: typography-utils.line-height($config, body-1), + container-text-size: typography-utils.font-size($config, body-1), + container-text-tracking: typography-utils.letter-spacing($config, body-1), + container-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) { + $scale: theming.clamp-density($config, -3); + $collapsed-scale: ( + 0: 48px, + -1: 44px, + -2: 40px, + -3: 36px, + ); + $expanded-scale: ( + 0: 64px, + -1: 60px, + -2: 56px, + -3: 48px, + ); + + @return ( + header-collapsed-state-height: map.get($collapsed-scale, $scale), + header-expanded-state-height: map.get($expanded-scale, $scale), + ); +} + +// 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/expansion/_expansion-legacy-index.scss b/src/material/expansion/_expansion-legacy-index.scss index 4023472a1a76..8d9cc4fc13db 100644 --- a/src/material/expansion/_expansion-legacy-index.scss +++ b/src/material/expansion/_expansion-legacy-index.scss @@ -1,3 +1,2 @@ @forward 'expansion-variables' as mat-expansion-panel-*; -@forward 'expansion-mixins' as mat-*; @forward 'expansion-theme' as mat-expansion-panel-*; diff --git a/src/material/expansion/_expansion-mixins.import.scss b/src/material/expansion/_expansion-mixins.import.scss deleted file mode 100644 index ce27c4a0ff30..000000000000 --- a/src/material/expansion/_expansion-mixins.import.scss +++ /dev/null @@ -1 +0,0 @@ -@forward 'expansion-mixins' as mat-*; diff --git a/src/material/expansion/_expansion-mixins.scss b/src/material/expansion/_expansion-mixins.scss deleted file mode 100644 index fc79d09633f8..000000000000 --- a/src/material/expansion/_expansion-mixins.scss +++ /dev/null @@ -1,11 +0,0 @@ -@mixin private-expansion-focus { - .mat-expansion-panel { - & .mat-expansion-panel-header.cdk-keyboard-focused, - & .mat-expansion-panel-header.cdk-program-focused, - &:not(.mat-expanded) .mat-expansion-panel-header:hover { - &:not([aria-disabled='true']) { - @content; - } - } - } -} diff --git a/src/material/expansion/_expansion-theme.import.scss b/src/material/expansion/_expansion-theme.import.scss index 77b02cd64727..9990c607f906 100644 --- a/src/material/expansion/_expansion-theme.import.scss +++ b/src/material/expansion/_expansion-theme.import.scss @@ -2,7 +2,6 @@ @forward '../core/style/private.import'; @forward 'expansion-variables' as mat-expansion-panel-*; @forward '../core/typography/typography-utils.import'; -@forward 'expansion-mixins' as mat-*; @forward 'expansion-theme' as mat-expansion-panel-*; @import '../core/density/private/compatibility'; @@ -11,4 +10,3 @@ @import '../core/style/private'; @import '../core/typography/typography-utils'; @import './expansion-variables'; -@import './expansion-mixins'; diff --git a/src/material/expansion/_expansion-theme.scss b/src/material/expansion/_expansion-theme.scss index 14bf3814e91f..4d14da51c9da 100644 --- a/src/material/expansion/_expansion-theme.scss +++ b/src/material/expansion/_expansion-theme.scss @@ -1,90 +1,34 @@ -@use 'sass:map'; -@use '../core/density/private/compatibility'; @use '../core/theming/theming'; -@use '../core/style/private'; @use '../core/typography/typography'; -@use '../core/typography/typography-utils'; -@use './expansion-variables'; -@use './expansion-mixins'; +@use '../core/style/sass-utils'; +@use '../core/tokens/token-utils'; +@use '../core/tokens/m2/mat/expansion' as tokens-mat-expansion; @mixin color($config-or-theme) { $config: theming.get-color-config($config-or-theme); - $background: map.get($config, background); - $foreground: map.get($config, foreground); - .mat-expansion-panel { - @include private.private-theme-overridable-elevation(2, $config); - background: theming.get-color-from-palette($background, card); - color: theming.get-color-from-palette($foreground, text); - } - - .mat-action-row { - border-top-color: theming.get-color-from-palette($foreground, divider); - } - - @include expansion-mixins.private-expansion-focus { - background: theming.get-color-from-palette($background, hover); - } - - // Disable the hover on touch devices since it can appear like it is stuck. We can't use - // `@media (hover)` above, because the desktop support browser support isn't great. - @media (hover: none) { - .mat-expansion-panel:not(.mat-expanded):not([aria-disabled='true']) - .mat-expansion-panel-header:hover { - background: theming.get-color-from-palette($background, card); - } - } - - .mat-expansion-panel-header-title { - color: theming.get-color-from-palette($foreground, text); - } - - .mat-expansion-panel-header-description, - .mat-expansion-indicator::after { - color: theming.get-color-from-palette($foreground, secondary-text); - } - - .mat-expansion-panel-header[aria-disabled='true'] { - color: theming.get-color-from-palette($foreground, disabled-button); - - .mat-expansion-panel-header-title, - .mat-expansion-panel-header-description { - color: inherit; - } + @include sass-utils.current-selector-or-root() { + @include token-utils.create-token-values(tokens-mat-expansion.$prefix, + tokens-mat-expansion.get-color-tokens($config)); } } @mixin typography($config-or-theme) { $config: typography.private-typography-to-2014-config( theming.get-typography-config($config-or-theme)); - .mat-expansion-panel-header { - font: { - family: typography-utils.font-family($config, subheading-1); - size: typography-utils.font-size($config, subheading-1); - weight: typography-utils.font-weight($config, subheading-1); - } - } - .mat-expansion-panel-content { - @include typography-utils.typography-level($config, body-1); + @include sass-utils.current-selector-or-root() { + @include token-utils.create-token-values(tokens-mat-expansion.$prefix, + tokens-mat-expansion.get-typography-tokens($config)); } } @mixin density($config-or-theme) { $density-scale: theming.get-density-config($config-or-theme); - $expanded-height: compatibility.private-density-prop-value( - expansion-variables.$header-density-config, $density-scale, expanded-height); - $collapsed-height: compatibility.private-density-prop-value( - expansion-variables.$header-density-config, $density-scale, collapsed-height); - @include compatibility.private-density-legacy-compatibility() { - .mat-expansion-panel-header { - height: $collapsed-height; - - &.mat-expanded { - height: $expanded-height; - } - } + @include sass-utils.current-selector-or-root() { + @include token-utils.create-token-values(tokens-mat-expansion.$prefix, + tokens-mat-expansion.get-density-tokens($density-scale)); } } diff --git a/src/material/expansion/expansion-panel-header.scss b/src/material/expansion/expansion-panel-header.scss index c434939c2d59..2380200e0d07 100644 --- a/src/material/expansion/expansion-panel-header.scss +++ b/src/material/expansion/expansion-panel-header.scss @@ -1,4 +1,6 @@ @use '@angular/cdk'; +@use '../core/tokens/m2/mat/expansion' as tokens-mat-expansion; +@use '../core/tokens/token-utils'; @use './expansion-variables'; .mat-expansion-panel-header { @@ -9,6 +11,44 @@ border-radius: inherit; transition: height expansion-variables.$header-transition; + @include token-utils.use-tokens( + tokens-mat-expansion.$prefix, tokens-mat-expansion.get-token-slots()) { + @include token-utils.create-token-slot(height, header-collapsed-state-height); + @include token-utils.create-token-slot(font-family, header-text-font); + @include token-utils.create-token-slot(font-size, header-text-size); + @include token-utils.create-token-slot(font-weight, header-text-weight); + @include token-utils.create-token-slot(line-height, header-text-line-height); + @include token-utils.create-token-slot(letter-spacing, header-text-tracking); + + &.mat-expanded { + @include token-utils.create-token-slot(height, header-expanded-state-height); + } + + &[aria-disabled='true'] { + @include token-utils.create-token-slot(color, header-disabled-state-text-color); + } + + &:not([aria-disabled='true']) { + cursor: pointer; + + .mat-expansion-panel:not(.mat-expanded) &:hover { + @include token-utils.create-token-slot(background, header-hover-state-layer-color); + + // Disable the hover on touch devices since it can appear like it is stuck. We can't use + // `@media (hover)` above, because the desktop support browser support isn't great. + @media (hover: none) { + @include token-utils.create-token-slot(background, container-background-color); + } + } + + // The `.mat-expansion-panel` here is redundant, but we need the additional specificity. + .mat-expansion-panel &.cdk-keyboard-focused, + .mat-expansion-panel &.cdk-program-focused { + @include token-utils.create-token-slot(background, header-focus-state-layer-color); + } + } + } + // If the `NoopAnimationsModule` is used, disable the height transition. &._mat-animation-noopable { transition: none; @@ -24,10 +64,6 @@ background: inherit; } - &:not([aria-disabled='true']) { - cursor: pointer; - } - &.mat-expansion-toggle-indicator-before { flex-direction: row-reverse; @@ -68,6 +104,13 @@ } } +.mat-expansion-panel-header-title { + @include token-utils.use-tokens( + tokens-mat-expansion.$prefix, tokens-mat-expansion.get-token-slots()) { + @include token-utils.create-token-slot(color, header-text-color); + } +} + .mat-expansion-panel-header-title, .mat-expansion-panel-header-description { display: flex; @@ -80,10 +123,19 @@ margin-right: 0; margin-left: 16px; } + + .mat-expansion-panel-header[aria-disabled='true'] & { + color: inherit; + } } .mat-expansion-panel-header-description { flex-grow: 2; + + @include token-utils.use-tokens( + tokens-mat-expansion.$prefix, tokens-mat-expansion.get-token-slots()) { + @include token-utils.create-token-slot(color, header-description-color); + } } // Creates the expansion indicator arrow. Done using ::after @@ -96,6 +148,11 @@ padding: 3px; transform: rotate(45deg); vertical-align: middle; + + @include token-utils.use-tokens( + tokens-mat-expansion.$prefix, tokens-mat-expansion.get-token-slots()) { + @include token-utils.create-token-slot(color, header-indicator-color); + } } @include cdk.high-contrast(active, off) { diff --git a/src/material/expansion/expansion-panel.scss b/src/material/expansion/expansion-panel.scss index 7af49c54126b..ce870070b051 100644 --- a/src/material/expansion/expansion-panel.scss +++ b/src/material/expansion/expansion-panel.scss @@ -1,37 +1,48 @@ @use '@angular/cdk'; - +@use '../core/tokens/m2/mat/expansion' as tokens-mat-expansion; +@use '../core/tokens/token-utils'; @use '../core/style/variables'; @use '../core/style/elevation'; .mat-expansion-panel { - $border-radius: 4px; - + @include token-utils.create-token-values( + tokens-mat-expansion.$prefix, tokens-mat-expansion.get-unthemable-tokens()); + @include elevation.overridable-elevation(2); box-sizing: content-box; display: block; margin: 0; - border-radius: $border-radius; overflow: hidden; transition: margin 225ms variables.$fast-out-slow-in-timing-function, elevation.private-transition-property-value(); - // Required so that the `box-shadow` works after the focus indicator relatively positions the - // header. + // Required so that the `box-shadow` works after the + // focus indicator relatively positions the header. position: relative; + @include token-utils.use-tokens( + tokens-mat-expansion.$prefix, tokens-mat-expansion.get-token-slots()) { + @include token-utils.create-token-slot(background, container-background-color); + @include token-utils.create-token-slot(color, container-text-color); + @include token-utils.create-token-slot(border-radius, container-shape); + } + .mat-accordion & { &:not(.mat-expanded), &:not(.mat-expansion-panel-spacing) { border-radius: 0; } - &:first-of-type { - border-top-right-radius: $border-radius; - border-top-left-radius: $border-radius; - } - - &:last-of-type { - border-bottom-right-radius: $border-radius; - border-bottom-left-radius: $border-radius; + @include token-utils.use-tokens( + tokens-mat-expansion.$prefix, tokens-mat-expansion.get-token-slots()) { + &:first-of-type { + @include token-utils.create-token-slot(border-top-right-radius, container-shape); + @include token-utils.create-token-slot(border-top-left-radius, container-shape); + } + + &:last-of-type { + @include token-utils.create-token-slot(border-bottom-right-radius, container-shape); + @include token-utils.create-token-slot(border-bottom-left-radius, container-shape); + } } } @@ -51,6 +62,15 @@ flex-direction: column; overflow: visible; + @include token-utils.use-tokens( + tokens-mat-expansion.$prefix, tokens-mat-expansion.get-token-slots()) { + @include token-utils.create-token-slot(font-family, container-text-font); + @include token-utils.create-token-slot(font-size, container-text-size); + @include token-utils.create-token-slot(font-weight, container-text-weight); + @include token-utils.create-token-slot(line-height, container-text-line-height); + @include token-utils.create-token-slot(letter-spacing, container-text-tracking); + } + // Usually the `visibility: hidden` added by the animation is enough to prevent focus from // entering the collapsed content, but children with their own `visibility` can override it. // In other components we set a `display: none` at the root to stop focus from reaching the @@ -88,6 +108,11 @@ justify-content: flex-end; padding: 16px 8px 16px 24px; + @include token-utils.use-tokens( + tokens-mat-expansion.$prefix, tokens-mat-expansion.get-token-slots()) { + @include token-utils.create-token-slot(border-top-color, actions-divider-color); + } + .mat-button-base, .mat-mdc-button-base { margin-left: 8px;