From 0f3a55f479fabc56e361088510ba0a5f987163c3 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Mon, 20 Nov 2023 19:20:58 +0000 Subject: [PATCH] feat(material-experimental/theming): add M3 select, option, and optgroup support (#28148) * feat(material-experimental/theming): add M3 select support * feat(material-experimental/theming): add M3 option & optgroup support --- src/dev-app/theme-m3.scss | 9 ++ .../theming/_custom-tokens.scss | 58 ++++++++++ .../theming/_m3-density.scss | 3 + .../theming/_m3-tokens.scss | 15 +++ src/material/core/option/_optgroup-theme.scss | 63 +++++++--- src/material/core/option/_option-theme.scss | 77 +++++++++---- src/material/core/tokens/m2/_index.scss | 6 + src/material/select/_select-theme.scss | 109 ++++++++++++------ 8 files changed, 263 insertions(+), 77 deletions(-) diff --git a/src/dev-app/theme-m3.scss b/src/dev-app/theme-m3.scss index 7ed533bbbc77..db5f48009ccd 100644 --- a/src/dev-app/theme-m3.scss +++ b/src/dev-app/theme-m3.scss @@ -45,10 +45,13 @@ html { @include mat.input-theme($light-theme); @include mat.list-theme($light-theme); @include mat.menu-theme($light-theme); + @include mat.optgroup-theme($light-theme); + @include mat.option-theme($light-theme); @include mat.progress-bar-theme($light-theme); @include mat.progress-spinner-theme($light-theme); @include mat.radio-theme($light-theme); @include mat.ripple-theme($light-theme); + @include mat.select-theme($light-theme); @include mat.sidenav-theme($light-theme); @include mat.slide-toggle-theme($light-theme); @include mat.slider-theme($light-theme); @@ -77,10 +80,13 @@ html { @include mat.input-color($dark-theme); @include mat.list-color($dark-theme); @include mat.menu-color($dark-theme); + @include mat.optgroup-color($dark-theme); + @include mat.option-color($dark-theme); @include mat.progress-bar-color($dark-theme); @include mat.progress-spinner-color($dark-theme); @include mat.radio-color($dark-theme); @include mat.ripple-color($dark-theme); + @include mat.select-color($dark-theme); @include mat.sidenav-color($dark-theme); @include mat.slide-toggle-color($dark-theme); @include mat.slider-color($dark-theme); @@ -108,9 +114,12 @@ html { @include mat.input-density($scale-theme); @include mat.list-density($scale-theme); @include mat.menu-density($scale-theme); + @include mat.optgroup-density($scale-theme); + @include mat.option-density($scale-theme); @include mat.progress-bar-density($scale-theme); @include mat.progress-spinner-density($scale-theme); @include mat.radio-density($scale-theme); + @include mat.select-density($scale-theme); @include mat.sidenav-density($scale-theme); @include mat.slide-toggle-density($scale-theme); @include mat.slider-density($scale-theme); diff --git a/src/material-experimental/theming/_custom-tokens.scss b/src/material-experimental/theming/_custom-tokens.scss index 51650ca66e7e..247552c5c37c 100644 --- a/src/material-experimental/theming/_custom-tokens.scss +++ b/src/material-experimental/theming/_custom-tokens.scss @@ -120,6 +120,42 @@ ); } +/// Generates custom tokens for the mat-optgroup. +/// @param {Map} $systems The MDC system tokens +/// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values +/// @return {Map} A set of custom tokens for the mat-optgroup +@function optgroup($systems, $exclude-hardcoded) { + @return mat.private-merge-all( + _generate-typography-tokens($systems, label-text, title-small), + ( + label-text-color: map.get($systems, md-sys-color, on-surface-variant), + ) + ); +} + +/// Generates custom tokens for the mat-option. +/// @param {Map} $systems The MDC system tokens +/// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values +/// @return {Map} A set of custom tokens for the mat-option +@function option($systems, $exclude-hardcoded) { + @return mat.private-merge-all( + _generate-typography-tokens($systems, label-text, label-large), + ( + selected-state-label-text-color: map.get($systems, md-sys-color, on-secondary-container), + label-text-color: map.get($systems, md-sys-color, on-surface), + hover-state-layer-color: mat.private-safe-color-change( + map.get($systems, md-sys-color, on-surface), + $alpha: map.get($systems, md-sys-state, hover-state-layer-opacity) + ), + focus-state-layer-color: mat.private-safe-color-change( + map.get($systems, md-sys-color, on-surface), + $alpha: map.get($systems, md-sys-state, focus-state-layer-opacity) + ), + selected-state-layer-color: map.get($systems, md-sys-color, secondary-container), + ), + ); +} + /// Generates custom tokens for the mat-radio. /// @param {Map} $systems The MDC system tokens /// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values @@ -142,6 +178,28 @@ ); } +/// Generates custom tokens for the mat-select. +/// @param {Map} $systems The MDC system tokens +/// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values +/// @return {Map} A set of custom tokens for the mat-select +@function select($systems, $exclude-hardcoded) { + @return mat.private-merge-all( + _generate-typography-tokens($systems, trigger-text, body-large), + ( + panel-background-color: map.get($systems, md-sys-color, surface-container), + enabled-trigger-text-color: map.get($systems, md-sys-color, on-surface), + disabled-trigger-text-color: mat.private-safe-color-change( + map.get($systems, md-sys-color, on-surface), $alpha: 0.38), + placeholder-text-color: map.get($systems, md-sys-color, on-surface-variant), + enabled-arrow-color: map.get($systems, md-sys-color, on-surface-variant), + disabled-arrow-color: mat.private-safe-color-change( + map.get($systems, md-sys-color, on-surface), $alpha: 0.38), + focused-arrow-color: map.get($systems, md-sys-color, primary), + invalid-arrow-color: map.get($systems, md-sys-color, error), + ) + ); +} + /// Generates custom tokens for the mat-sidenav. /// @param {Map} $systems The MDC system tokens /// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values diff --git a/src/material-experimental/theming/_m3-density.scss b/src/material-experimental/theming/_m3-density.scss index c3e0406aec8d..5ba148483a38 100644 --- a/src/material-experimental/theming/_m3-density.scss +++ b/src/material-experimental/theming/_m3-density.scss @@ -49,8 +49,11 @@ $_density-tokens: ( (mat, grid-list): (), (mat, icon): (), (mat, menu): (), + (mat, optgroup): (), + (mat, option): (), (mat, radio): (), (mat, ripple): (), + (mat, select): (), (mat, sidenav): (), (mat, slide-toggle): (), (mat, slider): (), diff --git a/src/material-experimental/theming/_m3-tokens.scss b/src/material-experimental/theming/_m3-tokens.scss index c50182976cee..114293f88529 100644 --- a/src/material-experimental/theming/_m3-tokens.scss +++ b/src/material-experimental/theming/_m3-tokens.scss @@ -280,6 +280,16 @@ custom-tokens.menu($systems, $exclude-hardcoded), $token-slots ), + _namespace-tokens( + (mat, optgroup), + custom-tokens.optgroup($systems, $exclude-hardcoded), + $token-slots + ), + _namespace-tokens( + (mat, option), + custom-tokens.option($systems, $exclude-hardcoded), + $token-slots + ), _namespace-tokens( (mat, radio), custom-tokens.radio($systems, $exclude-hardcoded), @@ -290,6 +300,11 @@ custom-tokens.ripple($systems, $exclude-hardcoded), $token-slots ), + _namespace-tokens( + (mat, select), + custom-tokens.select($systems, $exclude-hardcoded), + $token-slots + ), _namespace-tokens( (mat, sidenav), custom-tokens.sidenav($systems, $exclude-hardcoded), diff --git a/src/material/core/option/_optgroup-theme.scss b/src/material/core/option/_optgroup-theme.scss index 275906bb0ab7..5cd02dad20b3 100644 --- a/src/material/core/option/_optgroup-theme.scss +++ b/src/material/core/option/_optgroup-theme.scss @@ -1,41 +1,72 @@ +@use 'sass:map'; @use '../tokens/m2/mat/optgroup' as tokens-mat-optgroup; @use '../tokens/token-utils'; @use '../style/sass-utils'; - @use '../theming/theming'; @use '../theming/inspection'; @use '../typography/typography'; -@mixin base($theme) {} +@mixin base($theme) { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, base)); + } + @else {} +} @mixin color($theme) { - @include sass-utils.current-selector-or-root() { - @include token-utils.create-token-values(tokens-mat-optgroup.$prefix, - tokens-mat-optgroup.get-color-tokens($theme)); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, color)); + } + @else { + @include sass-utils.current-selector-or-root() { + @include token-utils.create-token-values(tokens-mat-optgroup.$prefix, + tokens-mat-optgroup.get-color-tokens($theme)); + } } } @mixin typography($theme) { - @include sass-utils.current-selector-or-root() { - @include token-utils.create-token-values(tokens-mat-optgroup.$prefix, - tokens-mat-optgroup.get-typography-tokens($theme)); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, typography)); + } + @else { + @include sass-utils.current-selector-or-root() { + @include token-utils.create-token-values(tokens-mat-optgroup.$prefix, + tokens-mat-optgroup.get-typography-tokens($theme)); + } } } @mixin density($theme) { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, density)); + } + @else {} } @mixin theme($theme) { @include theming.private-check-duplicate-theme-styles($theme, 'mat-optgroup') { - @include base($theme); - @if inspection.theme-has($theme, color) { - @include color($theme); - } - @if inspection.theme-has($theme, density) { - @include density($theme); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme)); } - @if inspection.theme-has($theme, typography) { - @include typography($theme); + @else { + @include base($theme); + @if inspection.theme-has($theme, color) { + @include color($theme); + } + @if inspection.theme-has($theme, density) { + @include density($theme); + } + @if inspection.theme-has($theme, typography) { + @include typography($theme); + } } } } + +@mixin _theme-from-tokens($tokens) { + @if ($tokens != ()) { + @include token-utils.create-token-values( + tokens-mat-optgroup.$prefix, map.get($tokens, tokens-mat-optgroup.$prefix)); + } +} diff --git a/src/material/core/option/_option-theme.scss b/src/material/core/option/_option-theme.scss index 662ff5a574eb..ca02734adcd9 100644 --- a/src/material/core/option/_option-theme.scss +++ b/src/material/core/option/_option-theme.scss @@ -1,51 +1,82 @@ +@use 'sass:map'; @use '../tokens/m2/mat/option' as tokens-mat-option; @use '../tokens/token-utils'; @use '../style/sass-utils'; - @use '../theming/theming'; @use '../theming/inspection'; @use '../typography/typography'; -@mixin base($theme) {} +@mixin base($theme) { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, base)); + } + @else {} +} @mixin color($theme) { - @include sass-utils.current-selector-or-root() { - @include token-utils.create-token-values(tokens-mat-option.$prefix, - tokens-mat-option.get-color-tokens($theme)); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, color)); } + @else { + @include sass-utils.current-selector-or-root() { + @include token-utils.create-token-values(tokens-mat-option.$prefix, + tokens-mat-option.get-color-tokens($theme)); + } - .mat-accent { - @include token-utils.create-token-values(tokens-mat-option.$prefix, - tokens-mat-option.get-color-tokens($theme, accent)); - } + .mat-accent { + @include token-utils.create-token-values(tokens-mat-option.$prefix, + tokens-mat-option.get-color-tokens($theme, accent)); + } - .mat-warn { - @include token-utils.create-token-values(tokens-mat-option.$prefix, - tokens-mat-option.get-color-tokens($theme, warn)); + .mat-warn { + @include token-utils.create-token-values(tokens-mat-option.$prefix, + tokens-mat-option.get-color-tokens($theme, warn)); + } } } @mixin typography($theme) { - @include sass-utils.current-selector-or-root() { - @include token-utils.create-token-values(tokens-mat-option.$prefix, - tokens-mat-option.get-typography-tokens($theme)); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, typography)); + } + @else { + @include sass-utils.current-selector-or-root() { + @include token-utils.create-token-values(tokens-mat-option.$prefix, + tokens-mat-option.get-typography-tokens($theme)); + } } } @mixin density($theme) { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, density)); + } + @else {} } @mixin theme($theme) { @include theming.private-check-duplicate-theme-styles($theme, 'mat-option') { - @include base($theme); - @if inspection.theme-has($theme, color) { - @include color($theme); - } - @if inspection.theme-has($theme, density) { - @include density($theme); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme)); } - @if inspection.theme-has($theme, typography) { - @include typography($theme); + @else { + @include base($theme); + @if inspection.theme-has($theme, color) { + @include color($theme); + } + @if inspection.theme-has($theme, density) { + @include density($theme); + } + @if inspection.theme-has($theme, typography) { + @include typography($theme); + } } } } + +@mixin _theme-from-tokens($tokens) { + @if ($tokens != ()) { + @include token-utils.create-token-values( + tokens-mat-option.$prefix, map.get($tokens, tokens-mat-option.$prefix)); + } +} diff --git a/src/material/core/tokens/m2/_index.scss b/src/material/core/tokens/m2/_index.scss index 6f34d45b7e7a..bab478ea17ee 100644 --- a/src/material/core/tokens/m2/_index.scss +++ b/src/material/core/tokens/m2/_index.scss @@ -7,8 +7,11 @@ @use './mat/grid-list' as tokens-mat-grid-list; @use './mat/icon' as tokens-mat-icon; @use './mat/menu' as tokens-mat-menu; +@use './mat/option' as tokens-mat-option; +@use './mat/optgroup' as tokens-mat-optgroup; @use './mat/radio' as tokens-mat-radio; @use './mat/ripple' as tokens-mat-ripple; +@use './mat/select' as tokens-mat-select; @use './mat/sidenav' as tokens-mat-sidenav; @use './mat/slide-toggle' as tokens-mat-slide-toggle; @use './mat/slider' as tokens-mat-slider; @@ -87,8 +90,11 @@ _get-tokens-for-module($theme, tokens-mat-grid-list), _get-tokens-for-module($theme, tokens-mat-icon), _get-tokens-for-module($theme, tokens-mat-menu), + _get-tokens-for-module($theme, tokens-mat-optgroup), + _get-tokens-for-module($theme, tokens-mat-option), _get-tokens-for-module($theme, tokens-mat-radio), _get-tokens-for-module($theme, tokens-mat-ripple), + _get-tokens-for-module($theme, tokens-mat-select), _get-tokens-for-module($theme, tokens-mat-sidenav), _get-tokens-for-module($theme, tokens-mat-slide-toggle), _get-tokens-for-module($theme, tokens-mat-slider), diff --git a/src/material/select/_select-theme.scss b/src/material/select/_select-theme.scss index 40d22a6edaf0..c39c938d3e74 100644 --- a/src/material/select/_select-theme.scss +++ b/src/material/select/_select-theme.scss @@ -1,3 +1,4 @@ +@use 'sass:map'; @use '@material/density' as mdc-density; @use '@material/textfield' as mdc-textfield; @use '../core/tokens/m2/mat/select' as tokens-mat-select; @@ -8,67 +9,99 @@ @use '../core/theming/inspection'; @use '../core/typography/typography'; -@mixin base($theme) {} +@mixin base($theme) { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, base)); + } + @else {} +} @mixin color($theme) { - @include sass-utils.current-selector-or-root() { - @include token-utils.create-token-values(tokens-mat-select.$prefix, - tokens-mat-select.get-color-tokens($theme)); - - .mat-mdc-form-field.mat-accent { + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, color)); + } + @else { + @include sass-utils.current-selector-or-root() { @include token-utils.create-token-values(tokens-mat-select.$prefix, - tokens-mat-select.get-color-tokens($theme, accent)); - } + tokens-mat-select.get-color-tokens($theme)); - .mat-mdc-form-field.mat-warn { - @include token-utils.create-token-values(tokens-mat-select.$prefix, - tokens-mat-select.get-color-tokens($theme, warn)); + .mat-mdc-form-field.mat-accent { + @include token-utils.create-token-values(tokens-mat-select.$prefix, + tokens-mat-select.get-color-tokens($theme, accent)); + } + + .mat-mdc-form-field.mat-warn { + @include token-utils.create-token-values(tokens-mat-select.$prefix, + tokens-mat-select.get-color-tokens($theme, warn)); + } } } } @mixin typography($theme) { - @include sass-utils.current-selector-or-root() { - @include token-utils.create-token-values(tokens-mat-select.$prefix, - tokens-mat-select.get-typography-tokens($theme)); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, typography)); + } + @else { + @include sass-utils.current-selector-or-root() { + @include token-utils.create-token-values(tokens-mat-select.$prefix, + tokens-mat-select.get-typography-tokens($theme)); + } } } @mixin density($theme) { - $density-scale: inspection.get-theme-density($theme); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme, density)); + } + @else { + $density-scale: inspection.get-theme-density($theme); - // Density is clamped at -5 here, because MDC's form field throws an error for anything lower. - $form-field-height: mdc-density.prop-value( - $density-config: mdc-textfield.$density-config, - $density-scale: theming.clamp-density($density-scale, -5), - $property-name: height, - ); + // Density is clamped at -5 here, because MDC's form field throws an error for anything lower. + $form-field-height: mdc-density.prop-value( + $density-config: mdc-textfield.$density-config, + $density-scale: theming.clamp-density($density-scale, -5), + $property-name: height, + ); - // On lower densities the filled form field hides its label which causes the label to - // be misaligned. Remove the additional offset that was added because of the label. - @if ($form-field-height < mdc-textfield.$minimum-height-for-filled-label) { - .mat-form-field-appearance-fill .mat-mdc-select-arrow-wrapper { - transform: none; + // On lower densities the filled form field hides its label which causes the label to + // be misaligned. Remove the additional offset that was added because of the label. + @if ($form-field-height < mdc-textfield.$minimum-height-for-filled-label) { + .mat-form-field-appearance-fill .mat-mdc-select-arrow-wrapper { + transform: none; + } } - } - @include sass-utils.current-selector-or-root() { - @include token-utils.create-token-values(tokens-mat-select.$prefix, - tokens-mat-select.get-density-tokens($theme)); + @include sass-utils.current-selector-or-root() { + @include token-utils.create-token-values(tokens-mat-select.$prefix, + tokens-mat-select.get-density-tokens($theme)); + } } } @mixin theme($theme) { @include theming.private-check-duplicate-theme-styles($theme, 'mat-select') { - @include base($theme); - @if inspection.theme-has($theme, color) { - @include color($theme); + @if inspection.get-theme-version($theme) == 1 { + @include _theme-from-tokens(inspection.get-theme-tokens($theme)); } - @if inspection.theme-has($theme, density) { - @include density($theme); - } - @if inspection.theme-has($theme, typography) { - @include typography($theme); + @else { + @include base($theme); + @if inspection.theme-has($theme, color) { + @include color($theme); + } + @if inspection.theme-has($theme, density) { + @include density($theme); + } + @if inspection.theme-has($theme, typography) { + @include typography($theme); + } } } } + +@mixin _theme-from-tokens($tokens) { + @if ($tokens != ()) { + @include token-utils.create-token-values( + tokens-mat-select.$prefix, map.get($tokens, tokens-mat-select.$prefix)); + } +}