From 231f198b687ca4334ef84be21c14bed9db62d0db Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 2 Jul 2024 13:18:12 +0200 Subject: [PATCH 01/22] refactor(material/core): consolidate elevation usages Consolidates all the usages of `@material/elevation` under one utility function so the eventual removal is easier. --- src/material/core/style/_elevation.scss | 5 +++++ src/material/core/tokens/_token-utils.scss | 4 ++-- src/material/core/tokens/m2/mat/_app.scss | 3 +-- src/material/core/tokens/m2/mat/_autocomplete.scss | 4 ++-- src/material/core/tokens/m2/mat/_datepicker.scss | 6 +++--- src/material/core/tokens/m2/mat/_dialog.scss | 4 ++-- src/material/core/tokens/m2/mat/_select.scss | 4 ++-- src/material/core/tokens/m2/mat/_sidenav.scss | 4 ++-- src/material/core/tokens/m2/mdc/_elevated-card.scss | 4 ++-- src/material/core/tokens/m2/mdc/_extended-fab.scss | 13 ++++++------- src/material/core/tokens/m2/mdc/_fab-small.scss | 13 ++++++------- src/material/core/tokens/m2/mdc/_fab.scss | 13 ++++++------- src/material/core/tokens/m2/mdc/_outlined-card.scss | 3 +-- src/material/core/tokens/m3/mat/_app.scss | 4 ++-- src/material/core/tokens/m3/mat/_autocomplete.scss | 4 ++-- src/material/core/tokens/m3/mat/_datepicker.scss | 6 +++--- src/material/core/tokens/m3/mat/_select.scss | 4 ++-- src/material/core/tokens/m3/mdc/_elevated-card.scss | 4 ++-- src/material/core/tokens/m3/mdc/_extended-fab.scss | 4 ++-- src/material/core/tokens/m3/mdc/_fab-small.scss | 4 ++-- src/material/core/tokens/m3/mdc/_fab.scss | 4 ++-- src/material/core/tokens/m3/mdc/_outlined-card.scss | 4 ++-- 22 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/material/core/style/_elevation.scss b/src/material/core/style/_elevation.scss index fa4ed60cc1a3..a8e57becb19d 100644 --- a/src/material/core/style/_elevation.scss +++ b/src/material/core/style/_elevation.scss @@ -70,6 +70,11 @@ $prefix: 'mat-elevation-z'; } } +// Gets the box shadow value for a specific elevation. +@function get-box-shadow($z-value, $shadow-color: black) { + @return mdc-elevation.elevation-box-shadow($z-value, $shadow-color); +} + // Returns a string that can be used as the value for a transition property for elevation. // Calling this function directly is useful in situations where a component needs to transition // more than one property. diff --git a/src/material/core/tokens/_token-utils.scss b/src/material/core/tokens/_token-utils.scss index d537d3667ce0..571c634a7ba5 100644 --- a/src/material/core/tokens/_token-utils.scss +++ b/src/material/core/tokens/_token-utils.scss @@ -1,11 +1,11 @@ @use 'sass:list'; @use 'sass:map'; @use 'sass:meta'; -@use '@material/elevation/elevation-theme' as mdc-elevation-theme; @use '@material/theme/custom-properties' as mdc-custom-properties; @use '@material/theme/theme' as mdc-theme; @use '@material/theme/keys' as mdc-keys; @use '@material/tokens/v0_161' as mdc-tokens; +@use '../style/elevation'; @use '../style/sass-utils'; @use '../m2/palette' as m2-palette; @use '../m2/theming' as m2-theming; @@ -161,7 +161,7 @@ $_component-prefix: null; $elevation: map.get($tokens, $elevation-token); $shadow-color: map.get($tokens, $shadow-color-token); @return map.merge($tokens, ( - $elevation-token: mdc-elevation-theme.elevation-box-shadow($elevation, $shadow-color), + $elevation-token: elevation.get-box-shadow($elevation, $shadow-color), $shadow-color-token: null, )); } diff --git a/src/material/core/tokens/m2/mat/_app.scss b/src/material/core/tokens/m2/mat/_app.scss index c1bde7db58fc..39a588f4f74b 100644 --- a/src/material/core/tokens/m2/mat/_app.scss +++ b/src/material/core/tokens/m2/mat/_app.scss @@ -1,4 +1,3 @@ -@use '@material/elevation/elevation-theme' as mdc-elevation; @use 'sass:map'; @use '../../token-utils'; @use '../../../theming/inspection'; @@ -23,7 +22,7 @@ $prefix: (mat, app); @for $zValue from 0 through 24 { $elevation-color: inspection.get-theme-color($theme, foreground, elevation); - $shadow: mdc-elevation.elevation-box-shadow($zValue, + $shadow: elevation.get-box-shadow($zValue, if($elevation-color == null, elevation.$color, $elevation-color)); $tokens: map.set($tokens, 'elevation-shadow-level-#{$zValue}', $shadow); } diff --git a/src/material/core/tokens/m2/mat/_autocomplete.scss b/src/material/core/tokens/m2/mat/_autocomplete.scss index a1a06caced3a..9e17ad4ec687 100644 --- a/src/material/core/tokens/m2/mat/_autocomplete.scss +++ b/src/material/core/tokens/m2/mat/_autocomplete.scss @@ -1,6 +1,6 @@ -@use '@material/elevation/elevation-theme' as mdc-elevation; @use '../../token-utils'; @use '../../../theming/inspection'; +@use '../../../style/elevation'; @use '../../../style/sass-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -11,7 +11,7 @@ $prefix: (mat, autocomplete); @function get-unthemable-tokens() { @return ( container-shape: 4px, - container-elevation-shadow: mdc-elevation.elevation-box-shadow(8), + container-elevation-shadow: elevation.get-box-shadow(8), ); } diff --git a/src/material/core/tokens/m2/mat/_datepicker.scss b/src/material/core/tokens/m2/mat/_datepicker.scss index ef00a98bf717..10731abfeeee 100644 --- a/src/material/core/tokens/m2/mat/_datepicker.scss +++ b/src/material/core/tokens/m2/mat/_datepicker.scss @@ -1,9 +1,9 @@ -@use '@material/elevation/elevation-theme' as mdc-elevation; @use 'sass:color'; @use 'sass:meta'; @use 'sass:math'; @use '../../token-utils'; @use '../../../theming/inspection'; +@use '../../../style/elevation'; @use '../../../style/sass-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -34,8 +34,8 @@ $private-default-overlap-color: #a8dab5; @return ( calendar-container-shape: 4px, calendar-container-touch-shape: 4px, - calendar-container-elevation-shadow: mdc-elevation.elevation-box-shadow(4), - calendar-container-touch-elevation-shadow: mdc-elevation.elevation-box-shadow(24), + calendar-container-elevation-shadow: elevation.get-box-shadow(4), + calendar-container-touch-elevation-shadow: elevation.get-box-shadow(24), ); } diff --git a/src/material/core/tokens/m2/mat/_dialog.scss b/src/material/core/tokens/m2/mat/_dialog.scss index ec1e888f4888..fb65d3863f4f 100644 --- a/src/material/core/tokens/m2/mat/_dialog.scss +++ b/src/material/core/tokens/m2/mat/_dialog.scss @@ -1,5 +1,5 @@ -@use '@material/elevation/elevation-theme' as mdc-elevation; @use '../../token-utils'; +@use '../../../style/elevation'; @use '../../../style/sass-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -9,7 +9,7 @@ $prefix: (mat, dialog); // but may be in a future version of the theming API. @function get-unthemable-tokens() { @return ( - container-elevation-shadow: mdc-elevation.elevation-box-shadow(24), + container-elevation-shadow: elevation.get-box-shadow(24), container-max-width: 80vw, container-small-max-width: 80vw, container-min-width: 0, diff --git a/src/material/core/tokens/m2/mat/_select.scss b/src/material/core/tokens/m2/mat/_select.scss index 5dbfa01aa2a1..d912d688b850 100644 --- a/src/material/core/tokens/m2/mat/_select.scss +++ b/src/material/core/tokens/m2/mat/_select.scss @@ -2,8 +2,8 @@ @use '../../token-utils'; @use '../../../theming/inspection'; @use '../../../theming/theming'; +@use '../../../style/elevation'; @use '../../../style/sass-utils'; -@use '@material/elevation/elevation-theme' as mdc-elevation; // The prefix used to generate the fully qualified name for tokens in this file. $prefix: (mat, select); @@ -12,7 +12,7 @@ $prefix: (mat, select); // but may be in a future version of the theming API. @function get-unthemable-tokens() { @return ( - container-elevation-shadow: mdc-elevation.elevation-box-shadow(8), + container-elevation-shadow: elevation.get-box-shadow(8), ); } diff --git a/src/material/core/tokens/m2/mat/_sidenav.scss b/src/material/core/tokens/m2/mat/_sidenav.scss index 080c3c121007..bef76fa1a250 100644 --- a/src/material/core/tokens/m2/mat/_sidenav.scss +++ b/src/material/core/tokens/m2/mat/_sidenav.scss @@ -1,8 +1,8 @@ -@use '@material/elevation/elevation-theme' as mdc-elevation; @use 'sass:color'; @use 'sass:meta'; @use '../../token-utils'; @use '../../../theming/inspection'; +@use '../../../style/elevation'; @use '../../../style/sass-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -15,7 +15,7 @@ $prefix: (mat, sidenav); // Currently zero, but it appears to be relevant for M3. // See: https://m3.material.io/components/navigation-drawer/overview container-shape: 0, - container-elevation-shadow: mdc-elevation.elevation-box-shadow(16), + container-elevation-shadow: elevation.get-box-shadow(16), container-width: auto, ); } diff --git a/src/material/core/tokens/m2/mdc/_elevated-card.scss b/src/material/core/tokens/m2/mdc/_elevated-card.scss index fe5a649c9bf1..d5018d78f26f 100644 --- a/src/material/core/tokens/m2/mdc/_elevated-card.scss +++ b/src/material/core/tokens/m2/mdc/_elevated-card.scss @@ -1,5 +1,5 @@ -@use '@material/elevation/elevation-theme' as mdc-elevation; @use '../../../theming/inspection'; +@use '../../../style/elevation'; @use '../../../style/sass-utils'; @use '../../token-utils'; @@ -51,7 +51,7 @@ $prefix: (mdc, elevated-card); @return ( // The background color of the card. container-color: inspection.get-theme-color($theme, background, card), - container-elevation: mdc-elevation.elevation-box-shadow(1), + container-elevation: elevation.get-box-shadow(1), ); } diff --git a/src/material/core/tokens/m2/mdc/_extended-fab.scss b/src/material/core/tokens/m2/mdc/_extended-fab.scss index d4f5bcb9b938..97fbbafb5c75 100644 --- a/src/material/core/tokens/m2/mdc/_extended-fab.scss +++ b/src/material/core/tokens/m2/mdc/_extended-fab.scss @@ -1,9 +1,8 @@ -@use '@material/elevation/elevation-theme' as mdc-elevation; +@use 'sass:map'; @use '../../token-utils'; +@use '../../../style/elevation'; @use '../../../theming/inspection'; -@use 'sass:map'; - // The prefix used to generate the fully qualified name for tokens in this file. $prefix: (mdc, extended-fab); @@ -51,10 +50,10 @@ $prefix: (mdc, extended-fab); // Tokens that can be configured through Angular Material's color theming API. @function get-color-tokens($theme) { @return ( - container-elevation-shadow: mdc-elevation.elevation-box-shadow(6), - focus-container-elevation-shadow: mdc-elevation.elevation-box-shadow(8), - hover-container-elevation-shadow: mdc-elevation.elevation-box-shadow(8), - pressed-container-elevation-shadow: mdc-elevation.elevation-box-shadow(12), + container-elevation-shadow: elevation.get-box-shadow(6), + focus-container-elevation-shadow: elevation.get-box-shadow(8), + hover-container-elevation-shadow: elevation.get-box-shadow(8), + pressed-container-elevation-shadow: elevation.get-box-shadow(12), ); } diff --git a/src/material/core/tokens/m2/mdc/_fab-small.scss b/src/material/core/tokens/m2/mdc/_fab-small.scss index b703adf98a4c..1e540a97ebd0 100644 --- a/src/material/core/tokens/m2/mdc/_fab-small.scss +++ b/src/material/core/tokens/m2/mdc/_fab-small.scss @@ -1,9 +1,8 @@ -@use '@material/elevation/elevation-theme' as mdc-elevation; +@use 'sass:map'; @use '../../../theming/inspection'; +@use '../../../style/elevation'; @use '../../token-utils'; -@use 'sass:map'; - // The prefix used to generate the fully qualified name for tokens in this file. $prefix: (mdc, fab-small); @@ -53,10 +52,10 @@ $prefix: (mdc, fab-small); @return ( // Background color of the FAB. container-color: inspection.get-theme-color($theme, background, card), - container-elevation-shadow: mdc-elevation.elevation-box-shadow(6), - focus-container-elevation-shadow: mdc-elevation.elevation-box-shadow(8), - hover-container-elevation-shadow: mdc-elevation.elevation-box-shadow(8), - pressed-container-elevation-shadow: mdc-elevation.elevation-box-shadow(12), + container-elevation-shadow: elevation.get-box-shadow(6), + focus-container-elevation-shadow: elevation.get-box-shadow(8), + hover-container-elevation-shadow: elevation.get-box-shadow(8), + pressed-container-elevation-shadow: elevation.get-box-shadow(12), ); } diff --git a/src/material/core/tokens/m2/mdc/_fab.scss b/src/material/core/tokens/m2/mdc/_fab.scss index e16027f43142..559b1dc6e308 100644 --- a/src/material/core/tokens/m2/mdc/_fab.scss +++ b/src/material/core/tokens/m2/mdc/_fab.scss @@ -1,9 +1,8 @@ -@use '@material/elevation/elevation-theme' as mdc-elevation; +@use 'sass:map'; @use '../../../theming/inspection'; +@use '../../../style/elevation'; @use '../../token-utils'; -@use 'sass:map'; - // The prefix used to generate the fully qualified name for tokens in this file. $prefix: (mdc, fab); @@ -54,10 +53,10 @@ $prefix: (mdc, fab); @return ( // Background color of the FAB. container-color: inspection.get-theme-color($theme, background, card), - container-elevation-shadow: mdc-elevation.elevation-box-shadow(6), - focus-container-elevation-shadow: mdc-elevation.elevation-box-shadow(8), - hover-container-elevation-shadow: mdc-elevation.elevation-box-shadow(8), - pressed-container-elevation-shadow: mdc-elevation.elevation-box-shadow(12), + container-elevation-shadow: elevation.get-box-shadow(6), + focus-container-elevation-shadow: elevation.get-box-shadow(8), + hover-container-elevation-shadow: elevation.get-box-shadow(8), + pressed-container-elevation-shadow: elevation.get-box-shadow(12), ); } diff --git a/src/material/core/tokens/m2/mdc/_outlined-card.scss b/src/material/core/tokens/m2/mdc/_outlined-card.scss index 38e45fbc65ca..d9dea0ec0b15 100644 --- a/src/material/core/tokens/m2/mdc/_outlined-card.scss +++ b/src/material/core/tokens/m2/mdc/_outlined-card.scss @@ -1,4 +1,3 @@ -@use '@material/elevation/elevation-theme' as mdc-elevation; @use '../../../style/elevation'; @use '../../../theming/inspection'; @use '../../../style/sass-utils'; @@ -60,7 +59,7 @@ $prefix: (mdc, outlined-card); container-color: inspection.get-theme-color($theme, background, card), // The border color of the card. outline-color: rgba(inspection.get-theme-color($theme, foreground, base), 0.12), - container-elevation: mdc-elevation.elevation-box-shadow(0), + container-elevation: elevation.get-box-shadow(0), ); } diff --git a/src/material/core/tokens/m3/mat/_app.scss b/src/material/core/tokens/m3/mat/_app.scss index e54826db04c5..4144c6a29846 100644 --- a/src/material/core/tokens/m3/mat/_app.scss +++ b/src/material/core/tokens/m3/mat/_app.scss @@ -1,5 +1,5 @@ @use 'sass:map'; -@use '@material/elevation' as mdc-elevation; +@use '../../../style/elevation'; @use '../../token-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -19,7 +19,7 @@ $prefix: (mat, app); @if ($shadow-color) { @for $zValue from 0 through 24 { - $shadow: mdc-elevation.elevation-box-shadow($zValue, $shadow-color); + $shadow: elevation.get-box-shadow($zValue, $shadow-color); $tokens: map.set($tokens, 'elevation-shadow-level-#{$zValue}', $shadow); } } diff --git a/src/material/core/tokens/m3/mat/_autocomplete.scss b/src/material/core/tokens/m3/mat/_autocomplete.scss index 485e452f4275..749153db51f4 100644 --- a/src/material/core/tokens/m3/mat/_autocomplete.scss +++ b/src/material/core/tokens/m3/mat/_autocomplete.scss @@ -1,5 +1,5 @@ @use 'sass:map'; -@use '@material/elevation/elevation-theme' as mdc-elevation; +@use '../../../style/elevation'; @use '../../token-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -15,7 +15,7 @@ $prefix: (mat, autocomplete); background-color: map.get($systems, md-sys-color, surface-container), container-shape: map.get($systems, md-sys-shape, corner-extra-small), container-elevation-shadow: - token-utils.hardcode(mdc-elevation.elevation-box-shadow(2), $exclude-hardcoded), + token-utils.hardcode(elevation.get-box-shadow(2), $exclude-hardcoded), ); @return token-utils.namespace-tokens($prefix, $tokens, $token-slots); diff --git a/src/material/core/tokens/m3/mat/_datepicker.scss b/src/material/core/tokens/m3/mat/_datepicker.scss index 3f33859e3eef..25c106cc6b9b 100644 --- a/src/material/core/tokens/m3/mat/_datepicker.scss +++ b/src/material/core/tokens/m3/mat/_datepicker.scss @@ -1,7 +1,7 @@ @use 'sass:map'; +@use '../../../style/elevation'; @use '../../../style/sass-utils'; @use '../../token-utils'; -@use '@material/elevation' as mdc-elevation; // The prefix used to generate the fully qualified name for tokens in this file. $prefix: (mat, datepicker); @@ -67,10 +67,10 @@ $prefix: (mat, datepicker); ), calendar-container-background-color: map.get($systems, md-sys-color, surface-container-high), calendar-container-text-color: map.get($systems, md-sys-color, on-surface), - calendar-container-elevation-shadow: token-utils.hardcode(mdc-elevation.elevation-box-shadow(0), + calendar-container-elevation-shadow: token-utils.hardcode(elevation.get-box-shadow(0), $exclude-hardcoded), calendar-container-touch-elevation-shadow: - token-utils.hardcode(mdc-elevation.elevation-box-shadow(0), $exclude-hardcoded), + token-utils.hardcode(elevation.get-box-shadow(0), $exclude-hardcoded), calendar-container-shape: map.get($systems, md-sys-shape, corner-large), calendar-container-touch-shape: map.get($systems, md-sys-shape, corner-extra-large), calendar-text-font: map.get($systems, md-sys-typescale, body-large-font), diff --git a/src/material/core/tokens/m3/mat/_select.scss b/src/material/core/tokens/m3/mat/_select.scss index 0e928e015928..b97af9a6b7f5 100644 --- a/src/material/core/tokens/m3/mat/_select.scss +++ b/src/material/core/tokens/m3/mat/_select.scss @@ -1,7 +1,7 @@ @use 'sass:map'; @use '../../../style/sass-utils'; +@use '../../../style/elevation'; @use '../../token-utils'; -@use '@material/elevation' as mdc-elevation; // The prefix used to generate the fully qualified name for tokens in this file. $prefix: (mat, select); @@ -26,7 +26,7 @@ $prefix: (mat, select); focused-arrow-color: map.get($systems, md-sys-color, primary), invalid-arrow-color: map.get($systems, md-sys-color, error), container-elevation-shadow: - token-utils.hardcode(mdc-elevation.elevation-box-shadow(2), $exclude-hardcoded), + token-utils.hardcode(elevation.get-box-shadow(2), $exclude-hardcoded), ) ), ( // Color variants: diff --git a/src/material/core/tokens/m3/mdc/_elevated-card.scss b/src/material/core/tokens/m3/mdc/_elevated-card.scss index 628e3a5f56e7..81cc4edf288a 100644 --- a/src/material/core/tokens/m3/mdc/_elevated-card.scss +++ b/src/material/core/tokens/m3/mdc/_elevated-card.scss @@ -1,5 +1,5 @@ @use 'sass:map'; -@use '@material/elevation/elevation-theme' as mdc-elevation; +@use '../../../style/elevation'; @use '../../token-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -15,7 +15,7 @@ $prefix: (mdc, elevated-card); $elevation: map.get($tokens, container-elevation); @if ($elevation != null) { - $tokens: map.set($tokens, container-elevation, mdc-elevation.elevation-box-shadow($elevation)); + $tokens: map.set($tokens, container-elevation, elevation.get-box-shadow($elevation)); } @return token-utils.namespace-tokens($prefix, $tokens, $token-slots); diff --git a/src/material/core/tokens/m3/mdc/_extended-fab.scss b/src/material/core/tokens/m3/mdc/_extended-fab.scss index bfc50e5964a6..48544bb0c50c 100644 --- a/src/material/core/tokens/m3/mdc/_extended-fab.scss +++ b/src/material/core/tokens/m3/mdc/_extended-fab.scss @@ -1,5 +1,5 @@ @use 'sass:map'; -@use '@material/elevation/elevation-theme' as mdc-elevation; +@use '../../../style/elevation'; @use '../../token-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -23,7 +23,7 @@ $prefix: (mdc, extended-fab); $elevation: map.get($tokens, $token); @if ($elevation != null) { - $tokens: map.set($tokens, $token + '-shadow', mdc-elevation.elevation-box-shadow($elevation)); + $tokens: map.set($tokens, $token + '-shadow', elevation.get-box-shadow($elevation)); } } diff --git a/src/material/core/tokens/m3/mdc/_fab-small.scss b/src/material/core/tokens/m3/mdc/_fab-small.scss index 2049f22d4551..60477725148d 100644 --- a/src/material/core/tokens/m3/mdc/_fab-small.scss +++ b/src/material/core/tokens/m3/mdc/_fab-small.scss @@ -1,5 +1,5 @@ @use 'sass:map'; -@use '@material/elevation/elevation-theme' as mdc-elevation; +@use '../../../style/elevation'; @use '../../token-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -23,7 +23,7 @@ $prefix: (mdc, fab-small); $elevation: map.get($tokens, $token); @if ($elevation != null) { - $tokens: map.set($tokens, $token + '-shadow', mdc-elevation.elevation-box-shadow($elevation)); + $tokens: map.set($tokens, $token + '-shadow', elevation.get-box-shadow($elevation)); } } diff --git a/src/material/core/tokens/m3/mdc/_fab.scss b/src/material/core/tokens/m3/mdc/_fab.scss index 2695f6ce9207..acd7719072b7 100644 --- a/src/material/core/tokens/m3/mdc/_fab.scss +++ b/src/material/core/tokens/m3/mdc/_fab.scss @@ -1,5 +1,5 @@ @use 'sass:map'; -@use '@material/elevation/elevation-theme' as mdc-elevation; +@use '../../../style/elevation'; @use '../../token-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -23,7 +23,7 @@ $prefix: (mdc, fab); $elevation: map.get($tokens, $token); @if ($elevation != null) { - $tokens: map.set($tokens, $token + '-shadow', mdc-elevation.elevation-box-shadow($elevation)); + $tokens: map.set($tokens, $token + '-shadow', elevation.get-box-shadow($elevation)); } } diff --git a/src/material/core/tokens/m3/mdc/_outlined-card.scss b/src/material/core/tokens/m3/mdc/_outlined-card.scss index 54ce7a762d51..dd55b5004d3a 100644 --- a/src/material/core/tokens/m3/mdc/_outlined-card.scss +++ b/src/material/core/tokens/m3/mdc/_outlined-card.scss @@ -1,5 +1,5 @@ @use 'sass:map'; -@use '@material/elevation/elevation-theme' as mdc-elevation; +@use '../../../style/elevation'; @use '../../token-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -15,7 +15,7 @@ $prefix: (mdc, outlined-card); $elevation: map.get($tokens, container-elevation); @if ($elevation != null) { - $tokens: map.set($tokens, container-elevation, mdc-elevation.elevation-box-shadow($elevation)); + $tokens: map.set($tokens, container-elevation, elevation.get-box-shadow($elevation)); } @return token-utils.namespace-tokens($prefix, $tokens, $token-slots); From c09a0e438abe728438e5c234a2d9ea18f365a9dc Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 2 Jul 2024 13:21:04 +0200 Subject: [PATCH 02/22] build: allow rem units Updates the Stylelint config to allow `rem`. --- .stylelintrc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stylelintrc.json b/.stylelintrc.json index 7d82e06d8a30..a854a9c4f7a9 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -81,7 +81,7 @@ "unit-case": "lower", "unit-no-unknown": true, - "unit-allowed-list": ["px", "%", "deg", "s", "ms", "em", "vh", "vw", "vmin"], + "unit-allowed-list": ["px", "%", "deg", "s", "ms", "em", "rem", "vh", "vw", "vmin"], "value-list-comma-space-after": "always-single-line", "value-list-comma-space-before": "never", From 62b6263e104058d0a3fd3622537197a98b9f3627 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 2 Jul 2024 13:43:01 +0200 Subject: [PATCH 03/22] refactor(multiple): remove dependency on external typography utilities Reduces our reliance on external typography utilities. --- src/material/button/button.scss | 3 +- .../internal-form-field.scss | 4 +-- src/material/core/m2/_typography.scss | 7 +++-- .../core/mdc-helpers/_mdc-helpers.scss | 4 +-- .../tests/test-typography-font-family.scss | 10 ++++--- src/material/dialog/dialog.scss | 14 ++++++++-- .../form-field/_form-field-subscript.scss | 4 +-- .../_mdc-text-field-structure-overrides.scss | 5 ++-- src/material/form-field/form-field.scss | 3 +- .../_list-option-trailing-avatar-compat.scss | 28 +++++++++++++++++-- src/material/paginator/paginator.scss | 4 +-- src/material/select/select.scss | 3 +- 12 files changed, 62 insertions(+), 27 deletions(-) diff --git a/src/material/button/button.scss b/src/material/button/button.scss index aed1f32c07e5..c05e3c600b61 100644 --- a/src/material/button/button.scss +++ b/src/material/button/button.scss @@ -5,7 +5,6 @@ @use '@material/button/button-filled-theme' as mdc-button-filled-theme; @use '@material/button/button-protected-theme' as mdc-button-protected-theme; @use '@material/button/button-outlined-theme' as mdc-button-outlined-theme; -@use '@material/typography/typography' as mdc-typography; @use '@material/theme/custom-properties' as mdc-custom-properties; @use './button-base'; @@ -155,7 +154,7 @@ // Similar to MDC's `_icon-structure`, apart from the margin which we // handle via custom tokens in `mat-private-button-horizontal-layout`. & > .mat-icon { - $icon-size: mdc-typography.px-to-rem(18px); + $icon-size: 1.125rem; display: inline-block; position: relative; vertical-align: top; diff --git a/src/material/core/internal-form-field/internal-form-field.scss b/src/material/core/internal-form-field/internal-form-field.scss index 70170adaac36..cb4f7e701cbb 100644 --- a/src/material/core/internal-form-field/internal-form-field.scss +++ b/src/material/core/internal-form-field/internal-form-field.scss @@ -1,6 +1,6 @@ @use '@material/form-field/form-field' as mdc-form-field; -@use '@material/typography/typography' as mdc-typography; @use '@material/theme/custom-properties' as mdc-custom-properties; +@use '../style/vendor-prefixes'; @use '../mdc-helpers/mdc-helpers'; @include mdc-custom-properties.configure($emit-fallback-values: false, $emit-fallback-vars: false) { @@ -8,5 +8,5 @@ } .mat-internal-form-field { - @include mdc-typography.smooth-font(); + @include vendor-prefixes.smooth-font(); } diff --git a/src/material/core/m2/_typography.scss b/src/material/core/m2/_typography.scss index 0d9238d5fada..2ad000cc860b 100644 --- a/src/material/core/m2/_typography.scss +++ b/src/material/core/m2/_typography.scss @@ -1,8 +1,11 @@ @use 'sass:map'; @use 'sass:math'; @use 'sass:meta'; +@use 'sass:string'; @use '@material/typography' as mdc-typography; +$_default-font-family: string.unquote('Roboto, sans-serif'); + /// Defines a typography level from the Material Design spec. /// @param {String} $font-size The font-size for this level. /// @param {String | Number} $line-height The line-height for this level. @@ -123,7 +126,7 @@ @function define-typography-config( // TODO(mmalerba): rename this function to define-typography-config, // and create a predefined px based config for people that need it. - $font-family: mdc-typography.$font-family, + $font-family: $_default-font-family, $headline-1: null, $headline-2: null, $headline-3: null, @@ -180,7 +183,7 @@ @function define-rem-typography-config( // TODO(mmalerba): rename this function to define-typography-config, // and create a predefined px based config for people that need it. - $font-family: mdc-typography.$font-family, + $font-family: $_default-font-family, $headline-1: null, $headline-2: null, $headline-3: null, diff --git a/src/material/core/mdc-helpers/_mdc-helpers.scss b/src/material/core/mdc-helpers/_mdc-helpers.scss index 28819d32b2d6..b963e10d1fbd 100644 --- a/src/material/core/mdc-helpers/_mdc-helpers.scss +++ b/src/material/core/mdc-helpers/_mdc-helpers.scss @@ -1,8 +1,8 @@ // TODO(mmalerba): this file should be split into separate cohesive partials for things like // "theming", "typography", "core". +@use 'sass:string'; @use '../typography/typography'; @use '@material/feature-targeting' as mdc-feature-targeting; -@use '@material/typography' as mdc-typography; @use '@material/theme/theme-color' as mdc-theme-color; @use '@material/theme/css' as mdc-theme-css; @@ -32,7 +32,7 @@ $mdc-typography-styles-query: typography; @function private-fallback-typography-from-mdc() { // This is very close to what we have in `define-typography-config`, but we can't use it here, // because it would cause a circular import and moving it here doesn't make sense. - $font-family: mdc-typography.$font-family; + $font-family: string.unquote('Roboto, sans-serif'); @return ( font-family: $font-family, headline-1: typography.typography-config-level-from-mdc(headline1, $font-family), diff --git a/src/material/core/theming/tests/test-typography-font-family.scss b/src/material/core/theming/tests/test-typography-font-family.scss index 302a3284f79a..91b9fb824ff3 100644 --- a/src/material/core/theming/tests/test-typography-font-family.scss +++ b/src/material/core/theming/tests/test-typography-font-family.scss @@ -1,7 +1,9 @@ -@use '@material/typography' as mdc-typography; -@use '../../m2/typography' as m2-typography; +@use 'sass:string'; @use 'sass:map'; @use 'sass:meta'; +@use '../../m2/typography' as m2-typography; + +$_font-family: string.unquote('Roboto, sans-serif'); @function assert-font-family($test-name, $obj, $expected) { @each $level-name, $level in $obj { @@ -16,7 +18,7 @@ $no-font-family: assert-font-family( 'should take default MDC font family if none is specified', m2-typography.define-typography-config(), - mdc-typography.$font-family); + $_font-family); $only-top-level-font-family: assert-font-family( 'should take custom font family if specified at top level', @@ -40,7 +42,7 @@ $individual-levels-without-font-families: assert-font-family( $button: m2-typography.define-typography-level($font-size: 1px), $overline: m2-typography.define-typography-level($font-size: 1px), ), - mdc-typography.$font-family + $_font-family ); $individual-levels-without-font-families-with-top-level-family: assert-font-family( diff --git a/src/material/dialog/dialog.scss b/src/material/dialog/dialog.scss index c6f4702b119c..57a831033745 100644 --- a/src/material/dialog/dialog.scss +++ b/src/material/dialog/dialog.scss @@ -1,5 +1,4 @@ @use '@angular/cdk'; -@use '@material/typography/typography' as mdc-typography; @use '../core/tokens/m2/mdc/dialog' as tokens-mdc-dialog; @use '../core/tokens/m2/mat/dialog' as tokens-mat-dialog; @use '../core/mdc-helpers/mdc-helpers'; @@ -153,12 +152,23 @@ $_emit-fallbacks: true; } .mat-mdc-dialog-title { - @include mdc-typography.text-baseline($top: 40px, $display: block, $lineHeight: null); + display: block; position: relative; flex-shrink: 0; box-sizing: border-box; margin: 0 0 1px; + // This was used by MDC to set the text baseline. We should figure out a way to + // remove it, because it can introduce unnecessary whitespace at the beginning + // of the element. + &::before { + display: inline-block; + width: 0; + height: 40px; + content: ''; + vertical-align: 0; + } + [dir='rtl'] & { text-align: right; } diff --git a/src/material/form-field/_form-field-subscript.scss b/src/material/form-field/_form-field-subscript.scss index d65aa058d89e..84edca21a142 100644 --- a/src/material/form-field/_form-field-subscript.scss +++ b/src/material/form-field/_form-field-subscript.scss @@ -1,7 +1,7 @@ -@use '@material/typography' as mdc-typography; @use '@material/textfield/variables' as mdc-textfield-variables; @use '../core/tokens/m2/mat/form-field' as tokens-mat-form-field; +@use '../core/style/vendor-prefixes'; @use '../core/tokens/token-utils'; @mixin private-form-field-subscript() { @@ -68,7 +68,7 @@ .mat-mdc-form-field-bottom-align::before { @include token-utils.use-tokens(tokens-mat-form-field.$prefix, tokens-mat-form-field.get-token-slots()) { - @include mdc-typography.smooth-font(); + @include vendor-prefixes.smooth-font(); @include token-utils.create-token-slot(font-family, subscript-text-font); @include token-utils.create-token-slot(line-height, subscript-text-line-height); @include token-utils.create-token-slot(font-size, subscript-text-size); diff --git a/src/material/form-field/_mdc-text-field-structure-overrides.scss b/src/material/form-field/_mdc-text-field-structure-overrides.scss index 94609c8dccda..f4a85b1953fd 100644 --- a/src/material/form-field/_mdc-text-field-structure-overrides.scss +++ b/src/material/form-field/_mdc-text-field-structure-overrides.scss @@ -1,5 +1,4 @@ @use '@material/textfield/variables' as mdc-textfield-variables; -@use '@material/typography/typography' as mdc-typography; @use '../core/tokens/m2/mat/form-field' as tokens-mat-form-field; @use '../core/tokens/token-utils'; @use '../core/style/vendor-prefixes'; @@ -25,7 +24,7 @@ $_enable-form-field-will-change-reset: true; // Note: We increase specificity here because the MDC textfield seems to override this, // depending on the CSS order, with an affix selector joint with the input. .mat-mdc-form-field-input-control.mat-mdc-form-field-input-control { - @include mdc-typography.smooth-font(); + @include vendor-prefixes.smooth-font(); font: inherit; letter-spacing: inherit; text-decoration: inherit; @@ -34,7 +33,7 @@ $_enable-form-field-will-change-reset: true; } .mat-mdc-form-field .mat-mdc-floating-label.mdc-floating-label { - @include mdc-typography.smooth-font(); + @include vendor-prefixes.smooth-font(); // In order to ensure proper alignment of the floating label, we reset its line-height. // The line-height is not important as the element is absolutely positioned and only has one diff --git a/src/material/form-field/form-field.scss b/src/material/form-field/form-field.scss index 1edabf17fecb..48d5481ee235 100644 --- a/src/material/form-field/form-field.scss +++ b/src/material/form-field/form-field.scss @@ -8,7 +8,6 @@ @use '@material/line-ripple/line-ripple' as mdc-line-ripple; @use '@material/line-ripple/line-ripple-theme' as mdc-line-ripple-theme; @use '@material/theme/custom-properties' as mdc-custom-properties; -@use '@material/typography' as mdc-typography; @use '../core/tokens/token-utils'; @use '../core/mdc-helpers/mdc-helpers'; @use '../core/style/vendor-prefixes'; @@ -91,7 +90,7 @@ $_icon-prefix-infix-padding: 4px; @include token-utils.use-tokens(tokens-mat-form-field.$prefix, tokens-mat-form-field.get-token-slots()) { - @include mdc-typography.smooth-font(); + @include vendor-prefixes.smooth-font(); @include token-utils.create-token-slot(font-family, container-text-font); @include token-utils.create-token-slot(line-height, container-text-line-height); @include token-utils.create-token-slot(font-size, container-text-size); diff --git a/src/material/list/_list-option-trailing-avatar-compat.scss b/src/material/list/_list-option-trailing-avatar-compat.scss index 051427222b8b..a50de012ab63 100644 --- a/src/material/list/_list-option-trailing-avatar-compat.scss +++ b/src/material/list/_list-option-trailing-avatar-compat.scss @@ -1,4 +1,3 @@ -@use '@material/typography/typography'; @use '@material/feature-targeting/feature-targeting'; @use '@material/density/functions' as density-functions; @use '@material/list/evolution-mixins' as mdc-list; @@ -22,8 +21,33 @@ @include mdc-list.item-end-size(40px, $query: $query); &.mdc-list-item--with-two-lines { + $top: 32px; + $bottom: 20px; + .mdc-list-item__primary-text { - @include typography.text-baseline($top: 32px, $bottom: 20px, $query: $query); + display: block; + margin-top: 0; + line-height: normal; + margin-bottom: $bottom * -1; + + // This was used by MDC to set the text baseline. We should figure out a way to + // remove it, because it can introduce unnecessary whitespace at the beginning + // of the element. + &::before { + display: inline-block; + width: 0; + height: $top; + content: ''; + vertical-align: 0; + } + + &::after { + display: inline-block; + width: 0; + height: $bottom; + content: ''; + vertical-align: $bottom * -1; + } } } diff --git a/src/material/paginator/paginator.scss b/src/material/paginator/paginator.scss index 4c66c6e456bd..f2488a36d07e 100644 --- a/src/material/paginator/paginator.scss +++ b/src/material/paginator/paginator.scss @@ -1,7 +1,7 @@ @use '@angular/cdk'; -@use '@material/typography/typography' as mdc-typography; @use '../core/tokens/m2/mat/paginator' as tokens-mat-paginator; @use '../core/tokens/token-utils'; +@use '../core/style/vendor-prefixes'; $padding: 0 8px; $page-size-margin-right: 8px; @@ -21,7 +21,7 @@ $button-icon-size: 28px; tokens-mat-paginator.$prefix, tokens-mat-paginator.get-token-slots() ) { - @include mdc-typography.smooth-font(); + @include vendor-prefixes.smooth-font(); @include token-utils.create-token-slot(color, container-text-color); @include token-utils.create-token-slot(background-color, container-background-color); @include token-utils.create-token-slot(font-family, container-text-font); diff --git a/src/material/select/select.scss b/src/material/select/select.scss index dff6e279fcf5..4654f3a06172 100644 --- a/src/material/select/select.scss +++ b/src/material/select/select.scss @@ -1,6 +1,5 @@ @use 'sass:math'; @use '@angular/cdk'; -@use '@material/typography/typography' as mdc-typography; @use '../core/style/vendor-prefixes'; @use '../core/style/variables'; @use '../core/tokens/token-utils'; @@ -22,7 +21,7 @@ $scale: 0.75 !default; @include token-utils.use-tokens( tokens-mat-select.$prefix, tokens-mat-select.get-token-slots()) { - @include mdc-typography.smooth-font(); + @include vendor-prefixes.smooth-font(); @include token-utils.create-token-slot(color, enabled-trigger-text-color); @include token-utils.create-token-slot(font-family, trigger-text-font); @include token-utils.create-token-slot(line-height, trigger-text-line-height); From 38306ee3c5707683c6be94a30ca72ca604b5d71d Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 2 Jul 2024 13:58:52 +0200 Subject: [PATCH 04/22] refactor(multiple): remove usages of external density utilities Reworks a few leftover places where we were using external utilities for dealing with density. --- .../core/tokens/m2/mat/_form-field.scss | 15 +++++++----- .../core/tokens/m2/mat/_paginator.scss | 16 ++++++++----- .../_list-option-trailing-avatar-compat.scss | 23 ------------------- 3 files changed, 19 insertions(+), 35 deletions(-) diff --git a/src/material/core/tokens/m2/mat/_form-field.scss b/src/material/core/tokens/m2/mat/_form-field.scss index 14f1d98327c3..bfee8de634c8 100644 --- a/src/material/core/tokens/m2/mat/_form-field.scss +++ b/src/material/core/tokens/m2/mat/_form-field.scss @@ -1,7 +1,6 @@ @use 'sass:math'; @use 'sass:map'; @use '@material/textfield' as mdc-textfield; -@use '@material/density' as mdc-density; @use '../../token-utils'; @use '../../../style/sass-utils'; @use '../../../theming/theming'; @@ -105,12 +104,16 @@ $prefix: (mat, form-field); // Tokens that can be configured through Angular Material's density theming API. @function get-density-tokens($theme) { - $density-scale: theming.clamp-density(inspection.get-theme-density($theme), -4); - $height: mdc-density.prop-value( - $density-config: mdc-textfield.$density-config, - $density-scale: inspection.get-theme-density($theme), - $property-name: height, + $density-scale: theming.clamp-density(inspection.get-theme-density($theme), -5); + $size-scale: ( + 0: 56px, + -1: 52px, + -2: 48px, + -3: 44px, + -4: 40px, + -5: 36px, ); + $height: map.get($size-scale, $density-scale); $hide-label: $height < mdc-textfield.$minimum-height-for-filled-label; // We computed the desired height of the form-field using the density configuration. The diff --git a/src/material/core/tokens/m2/mat/_paginator.scss b/src/material/core/tokens/m2/mat/_paginator.scss index 8a2398484773..b0cde30bda2a 100644 --- a/src/material/core/tokens/m2/mat/_paginator.scss +++ b/src/material/core/tokens/m2/mat/_paginator.scss @@ -1,7 +1,6 @@ @use 'sass:math'; @use 'sass:map'; @use '@material/textfield' as mdc-textfield; -@use '@material/density' as mdc-density; @use '../../token-utils'; @use '../../../theming/theming'; @use '../../../theming/inspection'; @@ -51,12 +50,17 @@ $prefix: (mat, paginator); -4: 40px, -5: 40px, ); - $form-field-density-scale: if($density-scale > -4, -4, $density-scale); - $form-field-height: mdc-density.prop-value( - $density-config: mdc-textfield.$density-config, - $density-scale: $form-field-density-scale, - $property-name: height, + $form-field-size-scale: ( + 0: 56px, + -1: 52px, + -2: 48px, + -3: 44px, + -4: 40px, + -5: 36px, ); + $form-field-density-scale: if($density-scale > -4, -4, $density-scale); + $form-field-height: map.get($form-field-size-scale, $form-field-density-scale); + // We computed the desired height of the form-field using the density configuration. The // spec only describes vertical spacing/alignment in non-dense mode. This means that we // cannot update the spacing to explicit numbers based on the density scale. Instead, we diff --git a/src/material/list/_list-option-trailing-avatar-compat.scss b/src/material/list/_list-option-trailing-avatar-compat.scss index a50de012ab63..826c84284ce9 100644 --- a/src/material/list/_list-option-trailing-avatar-compat.scss +++ b/src/material/list/_list-option-trailing-avatar-compat.scss @@ -1,7 +1,5 @@ @use '@material/feature-targeting/feature-targeting'; -@use '@material/density/functions' as density-functions; @use '@material/list/evolution-mixins' as mdc-list; -@use '@material/list/evolution-variables' as mdc-list-variables; @use '../core/mdc-helpers/mdc-helpers'; // For compatibility with the non-MDC selection list, we support avatars that are @@ -59,24 +57,3 @@ } } } - -@mixin density-styles($density-scale) { - $one-line-tall-height: density-functions.prop-value( - $density-config: mdc-list-variables.$one-line-item-tall-density-config, - $density-scale: $density-scale, - $property-name: height, - ); - - $two-line-tall-height: density-functions.prop-value( - $density-config: mdc-list-variables.$two-line-item-tall-density-config, - $density-scale: $density-scale, - $property-name: height, - ); - - @include mdc-helpers.disable-mdc-fallback-declarations { - .mat-mdc-list-option-with-trailing-avatar { - @include mdc-list.one-line-item-height($one-line-tall-height); - @include mdc-list.two-line-item-height($two-line-tall-height); - } - } -} From 0de61f9b28367f542965eaa5c9790d1dcb752bfd Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 2 Jul 2024 15:41:15 +0200 Subject: [PATCH 05/22] refactor(multiple): remove external ripple values Reworks a few components that were getting the values for the ripples externally. --- src/material/core/tokens/m2/mat/_fab-small.scss | 12 +++--------- src/material/core/tokens/m2/mat/_fab.scss | 12 +++--------- .../core/tokens/m2/mat/_filled-button.scss | 12 +++--------- src/material/core/tokens/m2/mat/_icon-button.scss | 12 +++--------- .../core/tokens/m2/mat/_outlined-button.scss | 12 +++--------- .../core/tokens/m2/mat/_protected-button.scss | 12 +++--------- src/material/core/tokens/m2/mat/_text-button.scss | 12 +++--------- src/material/slide-toggle/slide-toggle.scss | 15 +++++---------- 8 files changed, 26 insertions(+), 73 deletions(-) diff --git a/src/material/core/tokens/m2/mat/_fab-small.scss b/src/material/core/tokens/m2/mat/_fab-small.scss index b980384d4338..f7e514cdd3e3 100644 --- a/src/material/core/tokens/m2/mat/_fab-small.scss +++ b/src/material/core/tokens/m2/mat/_fab-small.scss @@ -1,6 +1,4 @@ -@use 'sass:map'; @use 'sass:meta'; -@use '@material/ripple/ripple-theme' as mdc-ripple-theme; @use '../../token-utils'; @use '../../../theming/theming'; @use '../../../theming/inspection'; @@ -19,10 +17,6 @@ $prefix: (mat, fab-small); // Tokens that can be configured through Angular Material's color theming API. @function get-color-tokens($theme) { $is-dark: inspection.get-theme-type($theme) == dark; - $ripple-opacities: if($is-dark, - mdc-ripple-theme.$light-ink-opacities, - mdc-ripple-theme.$dark-ink-opacities - ); @return ( // Color of icons and text projected into a FAB. @@ -38,13 +32,13 @@ $prefix: (mat, fab-small); ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1), // Opacity of the ripple when the button is hovered. - hover-state-layer-opacity: map.get($ripple-opacities, hover), + hover-state-layer-opacity: if($is-dark, 0.08, 0.04), // Opacity of the ripple when the button is focused. - focus-state-layer-opacity: map.get($ripple-opacities, focus), + focus-state-layer-opacity: if($is-dark, 0.24, 0.12), // Opacity of the ripple when the button is pressed. - pressed-state-layer-opacity: map.get($ripple-opacities, press), + pressed-state-layer-opacity: if($is-dark, 0.24, 0.12), // MDC doesn't have tokens for disabled FABs so we need to implemented them ourselves. // Background color of the container when the FAB is disabled. diff --git a/src/material/core/tokens/m2/mat/_fab.scss b/src/material/core/tokens/m2/mat/_fab.scss index cc8f48ed6723..687c252a0b36 100644 --- a/src/material/core/tokens/m2/mat/_fab.scss +++ b/src/material/core/tokens/m2/mat/_fab.scss @@ -1,6 +1,4 @@ -@use 'sass:map'; @use 'sass:meta'; -@use '@material/ripple/ripple-theme' as mdc-ripple-theme; @use '../../token-utils'; @use '../../../theming/theming'; @use '../../../theming/inspection'; @@ -19,10 +17,6 @@ $prefix: (mat, fab); // Tokens that can be configured through Angular Material's color theming API. @function get-color-tokens($theme) { $is-dark: inspection.get-theme-type($theme) == dark; - $ripple-opacities: if($is-dark, - mdc-ripple-theme.$light-ink-opacities, - mdc-ripple-theme.$dark-ink-opacities - ); @return ( // Color of icons and text projected into a FAB. @@ -38,13 +32,13 @@ $prefix: (mat, fab); ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1), // Opacity of the ripple when the button is hovered. - hover-state-layer-opacity: map.get($ripple-opacities, hover), + hover-state-layer-opacity: if($is-dark, 0.08, 0.04), // Opacity of the ripple when the button is focused. - focus-state-layer-opacity: map.get($ripple-opacities, focus), + focus-state-layer-opacity: if($is-dark, 0.24, 0.12), // Opacity of the ripple when the button is pressed. - pressed-state-layer-opacity: map.get($ripple-opacities, press), + pressed-state-layer-opacity: if($is-dark, 0.24, 0.12), // MDC doesn't have tokens for disabled FABs so we need to implemented them ourselves. // Background color of the container when the FAB is disabled. diff --git a/src/material/core/tokens/m2/mat/_filled-button.scss b/src/material/core/tokens/m2/mat/_filled-button.scss index 19dcbf51bbbf..e89b6e76828a 100644 --- a/src/material/core/tokens/m2/mat/_filled-button.scss +++ b/src/material/core/tokens/m2/mat/_filled-button.scss @@ -1,6 +1,4 @@ -@use 'sass:map'; @use 'sass:meta'; -@use '@material/ripple/ripple-theme' as mdc-ripple-theme; @use '../../token-utils'; @use '../../../theming/theming'; @use '../../../theming/inspection'; @@ -29,10 +27,6 @@ $prefix: (mat, filled-button); // Tokens that can be configured through Angular Material's color theming API. @function get-color-tokens($theme) { $is-dark: inspection.get-theme-type($theme) == dark; - $ripple-opacities: if($is-dark, - mdc-ripple-theme.$light-ink-opacities, - mdc-ripple-theme.$dark-ink-opacities - ); @return ( // Color of the element that shows the hover, focus and pressed states. @@ -45,13 +39,13 @@ $prefix: (mat, filled-button); ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1), // Opacity of the ripple when the button is hovered. - hover-state-layer-opacity: map.get($ripple-opacities, hover), + hover-state-layer-opacity: if($is-dark, 0.08, 0.04), // Opacity of the ripple when the button is focused. - focus-state-layer-opacity: map.get($ripple-opacities, focus), + focus-state-layer-opacity: if($is-dark, 0.24, 0.12), // Opacity of the ripple when the button is pressed. - pressed-state-layer-opacity: map.get($ripple-opacities, press), + pressed-state-layer-opacity: if($is-dark, 0.24, 0.12), ); } diff --git a/src/material/core/tokens/m2/mat/_icon-button.scss b/src/material/core/tokens/m2/mat/_icon-button.scss index 591af6611bf7..bc76f48a902a 100644 --- a/src/material/core/tokens/m2/mat/_icon-button.scss +++ b/src/material/core/tokens/m2/mat/_icon-button.scss @@ -1,6 +1,4 @@ -@use 'sass:map'; @use 'sass:meta'; -@use '@material/ripple/ripple-theme' as mdc-ripple-theme; @use '../../token-utils'; @use '../../../theming/theming'; @use '../../../theming/inspection'; @@ -18,10 +16,6 @@ $prefix: (mat, icon-button); // Tokens that can be configured through Angular Material's color theming API. @function get-color-tokens($theme) { $is-dark: inspection.get-theme-type($theme) == dark; - $ripple-opacities: if($is-dark, - mdc-ripple-theme.$light-ink-opacities, - mdc-ripple-theme.$dark-ink-opacities - ); @return ( // Color of the element that shows the hover, focus and pressed states. @@ -34,13 +28,13 @@ $prefix: (mat, icon-button); ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1), // Opacity of the ripple when the button is hovered. - hover-state-layer-opacity: map.get($ripple-opacities, hover), + hover-state-layer-opacity: if($is-dark, 0.08, 0.04), // Opacity of the ripple when the button is focused. - focus-state-layer-opacity: map.get($ripple-opacities, focus), + focus-state-layer-opacity: if($is-dark, 0.24, 0.12), // Opacity of the ripple when the button is pressed. - pressed-state-layer-opacity: map.get($ripple-opacities, press), + pressed-state-layer-opacity: if($is-dark, 0.24, 0.12), ); } diff --git a/src/material/core/tokens/m2/mat/_outlined-button.scss b/src/material/core/tokens/m2/mat/_outlined-button.scss index 2626386bc1ea..2a1dccf51d1f 100644 --- a/src/material/core/tokens/m2/mat/_outlined-button.scss +++ b/src/material/core/tokens/m2/mat/_outlined-button.scss @@ -1,6 +1,4 @@ -@use 'sass:map'; @use 'sass:meta'; -@use '@material/ripple/ripple-theme' as mdc-ripple-theme; @use '../../token-utils'; @use '../../../theming/theming'; @use '../../../theming/inspection'; @@ -28,10 +26,6 @@ $prefix: (mat, outlined-button); // Tokens that can be configured through Angular Material's color theming API. @function get-color-tokens($theme) { $is-dark: inspection.get-theme-type($theme) == dark; - $ripple-opacities: if($is-dark, - mdc-ripple-theme.$light-ink-opacities, - mdc-ripple-theme.$dark-ink-opacities - ); @return ( // Color of the element that shows the hover, focus and pressed states. @@ -44,13 +38,13 @@ $prefix: (mat, outlined-button); ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1), // Opacity of the ripple when the button is hovered. - hover-state-layer-opacity: map.get($ripple-opacities, hover), + hover-state-layer-opacity: if($is-dark, 0.08, 0.04), // Opacity of the ripple when the button is focused. - focus-state-layer-opacity: map.get($ripple-opacities, focus), + focus-state-layer-opacity: if($is-dark, 0.24, 0.12), // Opacity of the ripple when the button is pressed. - pressed-state-layer-opacity: map.get($ripple-opacities, press), + pressed-state-layer-opacity: if($is-dark, 0.24, 0.12), ); } diff --git a/src/material/core/tokens/m2/mat/_protected-button.scss b/src/material/core/tokens/m2/mat/_protected-button.scss index 26c621efcd1b..13c4cfa7fdaf 100644 --- a/src/material/core/tokens/m2/mat/_protected-button.scss +++ b/src/material/core/tokens/m2/mat/_protected-button.scss @@ -1,6 +1,4 @@ -@use 'sass:map'; @use 'sass:meta'; -@use '@material/ripple/ripple-theme' as mdc-ripple-theme; @use '../../token-utils'; @use '../../../theming/theming'; @use '../../../theming/inspection'; @@ -29,10 +27,6 @@ $prefix: (mat, protected-button); // Tokens that can be configured through Angular Material's color theming API. @function get-color-tokens($theme) { $is-dark: inspection.get-theme-type($theme) == dark; - $ripple-opacities: if($is-dark, - mdc-ripple-theme.$light-ink-opacities, - mdc-ripple-theme.$dark-ink-opacities - ); @return ( // Color of the element that shows the hover, focus and pressed states. @@ -45,13 +39,13 @@ $prefix: (mat, protected-button); ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1), // Opacity of the ripple when the button is hovered. - hover-state-layer-opacity: map.get($ripple-opacities, hover), + hover-state-layer-opacity: if($is-dark, 0.08, 0.04), // Opacity of the ripple when the button is focused. - focus-state-layer-opacity: map.get($ripple-opacities, focus), + focus-state-layer-opacity: if($is-dark, 0.24, 0.12), // Opacity of the ripple when the button is pressed. - pressed-state-layer-opacity: map.get($ripple-opacities, press), + pressed-state-layer-opacity: if($is-dark, 0.24, 0.12), ); } diff --git a/src/material/core/tokens/m2/mat/_text-button.scss b/src/material/core/tokens/m2/mat/_text-button.scss index 75603f1151df..70205ee5c7e8 100644 --- a/src/material/core/tokens/m2/mat/_text-button.scss +++ b/src/material/core/tokens/m2/mat/_text-button.scss @@ -1,6 +1,4 @@ -@use 'sass:map'; @use 'sass:meta'; -@use '@material/ripple/ripple-theme' as mdc-ripple-theme; @use '../../token-utils'; @use '../../../theming/theming'; @use '../../../theming/inspection'; @@ -31,10 +29,6 @@ $prefix: (mat, text-button); // Tokens that can be configured through Angular Material's color theming API. @function get-color-tokens($theme) { $is-dark: inspection.get-theme-type($theme) == dark; - $ripple-opacities: if($is-dark, - mdc-ripple-theme.$light-ink-opacities, - mdc-ripple-theme.$dark-ink-opacities - ); @return ( // Color of the element that shows the hover, focus and pressed states. @@ -47,13 +41,13 @@ $prefix: (mat, text-button); ripple-color: inspection.get-theme-color($theme, foreground, base, 0.1), // Opacity of the ripple when the button is hovered. - hover-state-layer-opacity: map.get($ripple-opacities, hover), + hover-state-layer-opacity: if($is-dark, 0.08, 0.04), // Opacity of the ripple when the button is focused. - focus-state-layer-opacity: map.get($ripple-opacities, focus), + focus-state-layer-opacity: if($is-dark, 0.24, 0.12), // Opacity of the ripple when the button is pressed. - pressed-state-layer-opacity: map.get($ripple-opacities, press), + pressed-state-layer-opacity: if($is-dark, 0.24, 0.12), ); } diff --git a/src/material/slide-toggle/slide-toggle.scss b/src/material/slide-toggle/slide-toggle.scss index 8e917b0d77b5..54cb1b956271 100644 --- a/src/material/slide-toggle/slide-toggle.scss +++ b/src/material/slide-toggle/slide-toggle.scss @@ -1,10 +1,7 @@ -@use 'sass:map'; -@use '@material/animation' as mdc-animation; @use '@material/switch/switch' as mdc-switch; @use '@material/switch/switch-theme' as mdc-switch-theme; -@use '@material/ripple' as mdc-ripple; -@use '../core/style/layout-common'; @use '@material/theme/custom-properties' as mdc-custom-properties; +@use '../core/style/layout-common'; @use '../core/tokens/m2/mat/switch' as tokens-mat-switch; @use '../core/tokens/m2/mdc/switch' as tokens-mdc-switch; @use '../core/tokens/token-utils'; @@ -86,14 +83,14 @@ } .mdc-switch:hover #{mdc-switch.$ripple-target}::after { - opacity: map.get(mdc-ripple.$dark-ink-opacities, hover); - transition: mdc-animation.enter(opacity, 75ms); + opacity: 0.04; + transition: 75ms opacity cubic-bezier(0, 0, 0.2, 1); } // Needs a little more specificity so the :hover styles don't override it. &.mat-mdc-slide-toggle-focused { .mdc-switch #{mdc-switch.$ripple-target}::after { - opacity: map.get(mdc-ripple.$dark-ink-opacities, focus); + opacity: 0.12; } // For slide-toggles render the focus indicator when we know @@ -103,10 +100,8 @@ } } - // We use an Angular Material ripple rather than an MDC ripple due to size concerns, so we need to - // style it appropriately. .mat-ripple-element { - opacity: map.get(mdc-ripple.$dark-ink-opacities, press); + opacity: 0.12; } // Slide-toggle components have to set `border-radius: 50%` in order to support density scaling From f8d9d167e90d2d57c955842fe07b15d94263ce8a Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 2 Jul 2024 17:06:49 +0000 Subject: [PATCH 06/22] refactor(material/chips): simplify structural styles (#29357) Simplifies the structural styles for the chips to make them simpler and easier to maintain. --- 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(), From 6659d8f9a99eaf1faf1b6cba234a50283a4e4698 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Tue, 2 Jul 2024 22:46:51 -0700 Subject: [PATCH 07/22] ci: fix lint issue (#29368) --- src/material/core/tokens/tests/test-validate-tokens.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/material/core/tokens/tests/test-validate-tokens.scss b/src/material/core/tokens/tests/test-validate-tokens.scss index b2e4ba548dd6..cd2d89205689 100644 --- a/src/material/core/tokens/tests/test-validate-tokens.scss +++ b/src/material/core/tokens/tests/test-validate-tokens.scss @@ -40,7 +40,6 @@ @use '../m2/mdc/tab-indicator' as tokens-mdc-tab-indicator; @use '../m2/mdc/snack-bar' as tokens-mdc-snack-bar; @use '../m2/mdc/slider' as tokens-mdc-slider; -@use '../m2/mdc/chip' as tokens-mdc-chip; @use '../m2/mdc/dialog' as tokens-mdc-dialog; @use '../m2/mdc/filled-text-field' as tokens-mdc-filled-text-field; @use '../m2/mdc/outlined-text-field' as tokens-mdc-outlined-text-field; From ea7e6aa461a080093f94caf01e9b2cefb62e0f1f Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 3 Jul 2024 11:06:17 +0000 Subject: [PATCH 08/22] docs: release notes for the v18.0.6 release --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a443a430c8cd..cb0d4b7f3e20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ + +# 18.0.6 "gallium-grape" (2024-07-03) +### material +| Commit | Type | Description | +| -- | -- | -- | +| [e5c5f151c](https://github.com/angular/components/commit/e5c5f151cc3a5293f629bfa84bcddb0b391cf268) | fix | **core:** add fallback if ripples get stuck ([#29323](https://github.com/angular/components/pull/29323)) | + + + # 18.1.0-next.4 "plastic-pliers" (2024-06-26) ### cdk From d60348739b77bea87bd541e96656be144552880d Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 3 Jul 2024 11:20:03 +0000 Subject: [PATCH 09/22] release: bump the next branch to v18.2.0-next.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d0955ceaaad..f3470978485c 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "ci-notify-slack-failure": "node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only scripts/circleci/notify-slack-job-failure.mts", "prepare": "husky" }, - "version": "18.1.0-next.4", + "version": "18.2.0-next.0", "dependencies": { "@angular/animations": "^18.1.0-next.3", "@angular/common": "^18.1.0-next.3", From 775391561e98d20a0265a49490f0bc56c9e2ca69 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 3 Jul 2024 11:20:03 +0000 Subject: [PATCH 10/22] docs: release notes for the v18.1.0-rc.0 release --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb0d4b7f3e20..b864f6d8cbe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ + +# 18.1.0-rc.0 "zirconium-zoo" (2024-07-03) +### material +| Commit | Type | Description | +| -- | -- | -- | +| [674538b77](https://github.com/angular/components/commit/674538b778e75d229ada06d19c362752db3a18cc) | fix | **core:** add fallback if ripples get stuck ([#29323](https://github.com/angular/components/pull/29323)) | + + + # 18.0.6 "gallium-grape" (2024-07-03) ### material From 051917432ad18aadd1747c5b6e8d5af0e861cc52 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 3 Jul 2024 16:33:26 +0000 Subject: [PATCH 11/22] fix(material/chips): navigate between rows on up/down arrow (#29364) Currently the up/down arrows behave in the same way as left/right, however that's incorrect based on [the reference implementation](https://www.w3.org/WAI/ARIA/apg/patterns/grid/examples/layout-grids/#ex2_label) which shows them navigating between the rows. These changes update the logic in the chip grid so that it matches the expected behavior. Fixes #29359. --- src/material/chips/chip-grid.spec.ts | 83 ++++++++++++++++++++-------- src/material/chips/chip-grid.ts | 31 +++++++++-- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/src/material/chips/chip-grid.spec.ts b/src/material/chips/chip-grid.spec.ts index cb18d49372f9..264675e33f49 100644 --- a/src/material/chips/chip-grid.spec.ts +++ b/src/material/chips/chip-grid.spec.ts @@ -3,6 +3,7 @@ import {Direction, Directionality} from '@angular/cdk/bidi'; import { BACKSPACE, DELETE, + DOWN_ARROW, END, ENTER, HOME, @@ -10,6 +11,7 @@ import { RIGHT_ARROW, SPACE, TAB, + UP_ARROW, } from '@angular/cdk/keycodes'; import { createKeyboardEvent, @@ -309,6 +311,48 @@ describe('MDC-based MatChipGrid', () => { .withContext('Expected focused item not to have changed.') .toBe(previousActiveElement); }); + + it('should focus primary action in next row when pressing DOWN ARROW on primary action', () => { + chips.first.focus(); + expect(document.activeElement).toBe(primaryActions[0]); + + dispatchKeyboardEvent(primaryActions[0], 'keydown', DOWN_ARROW); + fixture.detectChanges(); + + expect(document.activeElement).toBe(primaryActions[1]); + }); + + it('should focus primary action in previous row when pressing UP ARROW on primary action', () => { + const lastIndex = primaryActions.length - 1; + chips.last.focus(); + expect(document.activeElement).toBe(primaryActions[lastIndex]); + + dispatchKeyboardEvent(primaryActions[lastIndex], 'keydown', UP_ARROW); + fixture.detectChanges(); + + expect(document.activeElement).toBe(primaryActions[lastIndex - 1]); + }); + + it('should focus(trailing action in next row when pressing DOWN ARROW on(trailing action', () => { + trailingActions[0].focus(); + expect(document.activeElement).toBe(trailingActions[0]); + + dispatchKeyboardEvent(trailingActions[0], 'keydown', DOWN_ARROW); + fixture.detectChanges(); + + expect(document.activeElement).toBe(trailingActions[1]); + }); + + it('should focus trailing action in previous row when pressing UP ARROW on trailing action', () => { + const lastIndex = trailingActions.length - 1; + trailingActions[lastIndex].focus(); + expect(document.activeElement).toBe(trailingActions[lastIndex]); + + dispatchKeyboardEvent(trailingActions[lastIndex], 'keydown', UP_ARROW); + fixture.detectChanges(); + + expect(document.activeElement).toBe(trailingActions[lastIndex - 1]); + }); }); describe('RTL', () => { @@ -1034,11 +1078,8 @@ describe('MDC-based MatChipGrid', () => { template: ` @for (i of chips; track i) { - - {{name}} {{i + 1}} - -} + {{name}} {{i + 1}} + } `, }) @@ -1056,8 +1097,8 @@ class StandardChipGrid { Add a chip @for (chip of chips; track chip) { - {{chip}} -} + {{chip}} + } @@ -1081,10 +1122,10 @@ class FormFieldChipGrid { New food... @for (food of foods; track food) { - - {{ food.viewValue }} - -} + + {{ food.viewValue }} + + } @for (food of foods; track food) { - - {{food.viewValue}} - -} + {{food.viewValue}} + } Please select a chip, or type to add a new chip @@ -1179,8 +1218,8 @@ class ChipGridWithFormErrorMessages { template: ` @for (i of numbers; track i) { - {{i}} -} + {{i}} + } `, animations: [ @@ -1208,11 +1247,11 @@ class StandardChipGridWithAnimations { @for (i of chips; track i) { - - Chip {{i + 1}} - Remove - -} + + Chip {{i + 1}} + Remove + + } diff --git a/src/material/chips/chip-grid.ts b/src/material/chips/chip-grid.ts index d28ea15f5b66..1c57110d6f56 100644 --- a/src/material/chips/chip-grid.ts +++ b/src/material/chips/chip-grid.ts @@ -7,7 +7,7 @@ */ import {Directionality} from '@angular/cdk/bidi'; -import {hasModifierKey, TAB} from '@angular/cdk/keycodes'; +import {DOWN_ARROW, hasModifierKey, TAB, UP_ARROW} from '@angular/cdk/keycodes'; import { AfterContentInit, AfterViewInit, @@ -426,7 +426,10 @@ export class MatChipGrid /** Handles custom keyboard events. */ override _handleKeydown(event: KeyboardEvent) { - if (event.keyCode === TAB) { + const keyCode = event.keyCode; + const activeItem = this._keyManager.activeItem; + + if (keyCode === TAB) { if ( this._chipInput.focused && hasModifierKey(event, 'shiftKey') && @@ -435,8 +438,8 @@ export class MatChipGrid ) { event.preventDefault(); - if (this._keyManager.activeItem) { - this._keyManager.setActiveItem(this._keyManager.activeItem); + if (activeItem) { + this._keyManager.setActiveItem(activeItem); } else { this._focusLastChip(); } @@ -447,7 +450,25 @@ export class MatChipGrid super._allowFocusEscape(); } } else if (!this._chipInput.focused) { - super._handleKeydown(event); + // The up and down arrows are supposed to navigate between the individual rows in the grid. + // We do this by filtering the actions down to the ones that have the same `_isPrimary` + // flag as the active action and moving focus between them ourseles instead of delegating + // to the key manager. For more information, see #29359 and: + // https://www.w3.org/WAI/ARIA/apg/patterns/grid/examples/layout-grids/#ex2_label + if ((keyCode === UP_ARROW || keyCode === DOWN_ARROW) && activeItem) { + const eligibleActions = this._chipActions.filter( + action => action._isPrimary === activeItem._isPrimary && !this._skipPredicate(action), + ); + const currentIndex = eligibleActions.indexOf(activeItem); + const delta = event.keyCode === UP_ARROW ? -1 : 1; + + event.preventDefault(); + if (currentIndex > -1 && this._isValidIndex(currentIndex + delta)) { + this._keyManager.setActiveItem(eligibleActions[currentIndex + delta]); + } + } else { + super._handleKeydown(event); + } } this.stateChanges.next(); From 1f992d06c693a6e09332ac83d837c9ff8e1fdf7b Mon Sep 17 00:00:00 2001 From: DBowen33 <42016383+DBowen33@users.noreply.github.com> Date: Wed, 3 Jul 2024 18:00:16 +0000 Subject: [PATCH 12/22] fix(material/tree): aria-expanded attribute should not appear in the leaf node (#29273) * fix(material/tree): fixed unit tests fixed unit tests Fixes #21922 * fix(material/tree): updated public api file updated public api file Fixes #21922 --- src/cdk/tree/tree.spec.ts | 24 ++++++++---------------- src/cdk/tree/tree.ts | 22 +++++++++++++++++++++- src/material/tree/tree.spec.ts | 23 +++++++---------------- tools/public_api_guard/cdk/tree.md | 2 ++ 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/cdk/tree/tree.spec.ts b/src/cdk/tree/tree.spec.ts index db8d9eedbb15..093b4be86af5 100644 --- a/src/cdk/tree/tree.spec.ts +++ b/src/cdk/tree/tree.spec.ts @@ -145,20 +145,14 @@ describe('CdkTree', () => { let data = dataSource.data; dataSource.addChild(data[2]); fixture.detectChanges(); - expect( - getNodes(treeElement).every(node => { - return node.getAttribute('aria-expanded') === 'false'; - }), - ).toBe(true); + let ariaExpandedStates = getNodes(treeElement).map(n => n.getAttribute('aria-expanded')); + expect(ariaExpandedStates).toEqual([null, null, 'false', null]); component.treeControl.expandAll(); fixture.detectChanges(); - expect( - getNodes(treeElement).every(node => { - return node.getAttribute('aria-expanded') === 'true'; - }), - ).toBe(true); + ariaExpandedStates = getNodes(treeElement).map(n => n.getAttribute('aria-expanded')); + expect(ariaExpandedStates).toEqual([null, null, 'true', null]); }); it('with the right data', () => { @@ -805,11 +799,8 @@ describe('CdkTree', () => { }); it('with the right aria-expanded attrs', () => { - expect( - getNodes(treeElement).every(node => { - return node.getAttribute('aria-expanded') === 'false'; - }), - ).toBe(true); + let ariaExpandedStates = getNodes(treeElement).map(n => n.getAttribute('aria-expanded')); + expect(ariaExpandedStates).toEqual([null, null, null]); component.toggleRecursively = false; fixture.changeDetectorRef.markForCheck(); @@ -822,7 +813,7 @@ describe('CdkTree', () => { fixture.detectChanges(); const ariaExpanded = getNodes(treeElement).map(n => n.getAttribute('aria-expanded')); - expect(ariaExpanded).toEqual(['false', 'true', 'false', 'false']); + expect(ariaExpanded).toEqual([null, 'true', 'false', null]); }); it('should expand/collapse the node multiple times', () => { @@ -886,6 +877,7 @@ describe('CdkTree', () => { }); it('should expand/collapse the node recursively', () => { + fixture.changeDetectorRef.markForCheck(); let data = dataSource.data; const child = dataSource.addChild(data[1], false); dataSource.addChild(child, false); diff --git a/src/cdk/tree/tree.ts b/src/cdk/tree/tree.ts index 9746981bdf4f..16b2a21bc06f 100644 --- a/src/cdk/tree/tree.ts +++ b/src/cdk/tree/tree.ts @@ -324,7 +324,7 @@ export class CdkTree implements AfterContentChecked, CollectionViewer, exportAs: 'cdkTreeNode', host: { 'class': 'cdk-tree-node', - '[attr.aria-expanded]': 'isExpanded', + '[attr.aria-expanded]': 'isLeafNode ? null : isExpanded', }, standalone: true, }) @@ -375,6 +375,26 @@ export class CdkTreeNode implements FocusableOption, OnDestroy, OnInit return this._tree.treeControl.isExpanded(this._data); } + /* If leaf node, return true to not assign aria-expanded attribute */ + get isLeafNode(): boolean { + // If flat tree node data returns false for expandable property, it's a leaf node + if ( + this._tree.treeControl.isExpandable !== undefined && + !this._tree.treeControl.isExpandable(this._data) + ) { + return true; + + // If nested tree node data returns 0 descendants, it's a leaf node + } else if ( + this._tree.treeControl.isExpandable === undefined && + this._tree.treeControl.getDescendants(this._data).length === 0 + ) { + return true; + } + + return false; + } + get level(): number { // If the treeControl has a getLevel method, use it to get the level. Otherwise read the // aria-level off the parent node and use it as the level for this node (note aria-level is diff --git a/src/material/tree/tree.spec.ts b/src/material/tree/tree.spec.ts index 7091498106cc..fc6e29073623 100644 --- a/src/material/tree/tree.spec.ts +++ b/src/material/tree/tree.spec.ts @@ -78,20 +78,14 @@ describe('MatTree', () => { const data = underlyingDataSource.data; underlyingDataSource.addChild(data[2]); fixture.detectChanges(); - expect( - getNodes(treeElement).every(node => { - return node.getAttribute('aria-expanded') === 'false'; - }), - ).toBe(true); + let ariaExpandedStates = getNodes(treeElement).map(n => n.getAttribute('aria-expanded')); + expect(ariaExpandedStates).toEqual([null, null, 'false']); component.treeControl.expandAll(); fixture.detectChanges(); - expect( - getNodes(treeElement).every(node => { - return node.getAttribute('aria-expanded') === 'true'; - }), - ).toBe(true); + ariaExpandedStates = getNodes(treeElement).map(n => n.getAttribute('aria-expanded')); + expect(ariaExpandedStates).toEqual([null, null, 'true', null]); }); it('with the right data', () => { @@ -470,11 +464,8 @@ describe('MatTree', () => { }); it('with the right aria-expanded attrs', () => { - expect( - getNodes(treeElement).every(node => { - return node.getAttribute('aria-expanded') === 'false'; - }), - ).toBe(true); + let ariaExpandedStates = getNodes(treeElement).map(n => n.getAttribute('aria-expanded')); + expect(ariaExpandedStates).toEqual([null, null, null]); component.toggleRecursively = false; const data = underlyingDataSource.data; @@ -486,7 +477,7 @@ describe('MatTree', () => { fixture.detectChanges(); const ariaExpanded = getNodes(treeElement).map(n => n.getAttribute('aria-expanded')); - expect(ariaExpanded).toEqual(['false', 'true', 'false', 'false']); + expect(ariaExpanded).toEqual([null, 'true', 'false', null]); }); it('should expand/collapse the node', () => { diff --git a/tools/public_api_guard/cdk/tree.md b/tools/public_api_guard/cdk/tree.md index 693d41f1c2f3..92d38fefc764 100644 --- a/tools/public_api_guard/cdk/tree.md +++ b/tools/public_api_guard/cdk/tree.md @@ -127,6 +127,8 @@ export class CdkTreeNode implements FocusableOption, OnDestroy, OnInit // (undocumented) get isExpanded(): boolean; // (undocumented) + get isLeafNode(): boolean; + // (undocumented) get level(): number; static mostRecentTreeNode: CdkTreeNode | null; // (undocumented) From 4db4fc18a13d0529956a8b55701efd8349caf0be Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 4 Jul 2024 05:26:16 +0000 Subject: [PATCH 13/22] refactor(material/button): simplify structural styles (#29372) Simplifies the structural styles for the button to make them simpler and easier to maintain. --- src/material/button/_button-base.scss | 19 +- src/material/button/_button-theme.scss | 53 ++- src/material/button/button.scss | 324 ++++++++++++------ .../core/tokens/m2/mdc/_extended-fab.scss | 11 +- .../core/tokens/m2/mdc/_fab-small.scss | 8 +- src/material/core/tokens/m2/mdc/_fab.scss | 8 +- .../core/tokens/m2/mdc/_protected-button.scss | 20 +- .../core/tokens/m3/mdc/_protected-button.scss | 27 +- .../tokens/tests/test-validate-tokens.scss | 7 - 9 files changed, 297 insertions(+), 180 deletions(-) diff --git a/src/material/button/_button-base.scss b/src/material/button/_button-base.scss index cb5de5769cb2..6885abe5adf5 100644 --- a/src/material/button/_button-base.scss +++ b/src/material/button/_button-base.scss @@ -1,8 +1,5 @@ -@use '@material/touch-target' as mdc-touch-target; - @use '../core/tokens/token-utils'; @use '../core/style/layout-common'; -@use '../core/mdc-helpers/mdc-helpers'; // Adds styles necessary to provide stateful interactions with the button. This includes providing // content for the state container's ::before and ::after so that they can be given a background @@ -126,9 +123,19 @@ // the button itself would require us to wrap it in another div. See: // https://github.com/material-components/material-components-web/tree/master/packages/mdc-button#making-buttons-accessible .mat-mdc-button-touch-target { - @include mdc-touch-target.touch-target( - $set-width: $is-square, - $query: mdc-helpers.$mdc-base-styles-query); + position: absolute; + top: 50%; + height: 48px; + + @if $is-square { + left: 50%; + width: 48px; + transform: translate(-50%, -50%); + } @else { + left: 0; + right: 0; + transform: translateY(-50%); + } @include token-utils.use-tokens($prefix, $slots) { @include token-utils.create-token-slot(display, touch-target-display); diff --git a/src/material/button/_button-theme.scss b/src/material/button/_button-theme.scss index 906466a02fda..ee85325714fb 100644 --- a/src/material/button/_button-theme.scss +++ b/src/material/button/_button-theme.scss @@ -1,8 +1,3 @@ -@use '@material/button/button-text-theme' as mdc-button-text-theme; -@use '@material/button/button-filled-theme' as mdc-button-filled-theme; -@use '@material/button/button-protected-theme' as mdc-button-protected-theme; -@use '@material/button/button-outlined-theme' as mdc-button-outlined-theme; - @use '../core/theming/theming'; @use '../core/theming/inspection'; @use '../core/theming/validation'; @@ -29,7 +24,7 @@ tokens-mat-text-button.get-color-tokens($theme) ); - @include mdc-button-text-theme.theme($mdc-tokens); + @include token-utils.create-token-values(tokens-mdc-text-button.$prefix, $mdc-tokens); @include token-utils.create-token-values(tokens-mat-text-button.$prefix, $mat-tokens); } @@ -44,7 +39,7 @@ tokens-mat-filled-button.get-color-tokens($theme) ); - @include mdc-button-filled-theme.theme($mdc-tokens); + @include token-utils.create-token-values(tokens-mdc-filled-button.$prefix, $mdc-tokens); @include token-utils.create-token-values(tokens-mat-filled-button.$prefix, $mat-tokens); } @@ -59,7 +54,7 @@ tokens-mat-protected-button.get-color-tokens($theme) ); - @include mdc-button-protected-theme.theme($mdc-tokens); + @include token-utils.create-token-values(tokens-mdc-protected-button.$prefix, $mdc-tokens); @include token-utils.create-token-values(tokens-mat-protected-button.$prefix, $mat-tokens); } @@ -74,7 +69,7 @@ tokens-mat-outlined-button.get-color-tokens($theme) ); - @include mdc-button-outlined-theme.theme($mdc-tokens); + @include token-utils.create-token-values(tokens-mdc-outlined-button.$prefix, $mdc-tokens); @include token-utils.create-token-values(tokens-mat-outlined-button.$prefix, $mat-tokens); } @@ -97,10 +92,14 @@ token-utils.get-tokens-for($tokens, tokens-mat-filled-button.$prefix, $options...); $mat-outlined-button-tokens: token-utils.get-tokens-for($tokens, tokens-mat-outlined-button.$prefix, $options...); - @include mdc-button-text-theme.theme($mdc-text-button-tokens); - @include mdc-button-protected-theme.theme($mdc-protected-button-tokens); - @include mdc-button-filled-theme.theme($mdc-filled-button-tokens); - @include mdc-button-outlined-theme.theme($mdc-outlined-button-tokens); + + @include token-utils.create-token-values(tokens-mdc-text-button.$prefix, $mdc-text-button-tokens); + @include token-utils.create-token-values( + tokens-mdc-protected-button.$prefix, $mdc-protected-button-tokens); + @include token-utils.create-token-values( + tokens-mdc-filled-button.$prefix, $mdc-filled-button-tokens); + @include token-utils.create-token-values( + tokens-mdc-outlined-button.$prefix, $mdc-outlined-button-tokens); @include token-utils.create-token-values(tokens-mat-text-button.$prefix, $mat-text-button-tokens); @include token-utils.create-token-values( tokens-mat-protected-button.$prefix, $mat-protected-button-tokens); @@ -119,13 +118,13 @@ } @else { @include sass-utils.current-selector-or-root() { - @include mdc-button-text-theme.theme( + @include token-utils.create-token-values(tokens-mdc-text-button.$prefix, tokens-mdc-text-button.get-unthemable-tokens()); - @include mdc-button-filled-theme.theme( + @include token-utils.create-token-values(tokens-mdc-filled-button.$prefix, tokens-mdc-filled-button.get-unthemable-tokens()); - @include mdc-button-protected-theme.theme( + @include token-utils.create-token-values(tokens-mdc-protected-button.$prefix, tokens-mdc-protected-button.get-unthemable-tokens()); - @include mdc-button-outlined-theme.theme( + @include token-utils.create-token-values(tokens-mdc-outlined-button.$prefix, tokens-mdc-outlined-button.get-unthemable-tokens()); @include token-utils.create-token-values(tokens-mat-text-button.$prefix, @@ -223,14 +222,14 @@ } @else { @include sass-utils.current-selector-or-root() { - @include mdc-button-text-theme.theme( + @include token-utils.create-token-values(tokens-mdc-text-button.$prefix, tokens-mdc-text-button.get-typography-tokens($theme)); - @include mdc-button-filled-theme.theme( + @include token-utils.create-token-values(tokens-mdc-filled-button.$prefix, tokens-mdc-filled-button.get-typography-tokens($theme)); - @include mdc-button-outlined-theme.theme( - tokens-mdc-outlined-button.get-typography-tokens($theme)); - @include mdc-button-protected-theme.theme( + @include token-utils.create-token-values(tokens-mdc-protected-button.$prefix, tokens-mdc-protected-button.get-typography-tokens($theme)); + @include token-utils.create-token-values(tokens-mdc-outlined-button.$prefix, + tokens-mdc-outlined-button.get-typography-tokens($theme)); @include token-utils.create-token-values(tokens-mat-text-button.$prefix, tokens-mat-text-button.get-typography-tokens($theme)); @@ -252,14 +251,14 @@ } @else { @include sass-utils.current-selector-or-root() { - @include mdc-button-text-theme.theme( + @include token-utils.create-token-values(tokens-mdc-text-button.$prefix, tokens-mdc-text-button.get-density-tokens($theme)); - @include mdc-button-filled-theme.theme( + @include token-utils.create-token-values(tokens-mdc-filled-button.$prefix, tokens-mdc-filled-button.get-density-tokens($theme)); - @include mdc-button-outlined-theme.theme( - tokens-mdc-outlined-button.get-density-tokens($theme)); - @include mdc-button-protected-theme.theme( + @include token-utils.create-token-values(tokens-mdc-protected-button.$prefix, tokens-mdc-protected-button.get-density-tokens($theme)); + @include token-utils.create-token-values(tokens-mdc-outlined-button.$prefix, + tokens-mdc-outlined-button.get-density-tokens($theme)); @include token-utils.create-token-values(tokens-mat-text-button.$prefix, tokens-mat-text-button.get-density-tokens($theme)); diff --git a/src/material/button/button.scss b/src/material/button/button.scss index c05e3c600b61..7cc65bbcd4d0 100644 --- a/src/material/button/button.scss +++ b/src/material/button/button.scss @@ -1,15 +1,6 @@ -@use 'sass:map'; -@use '@material/button/button' as mdc-button; -@use '@material/button/variables' as mdc-button-variables; -@use '@material/button/button-text-theme' as mdc-button-text-theme; -@use '@material/button/button-filled-theme' as mdc-button-filled-theme; -@use '@material/button/button-protected-theme' as mdc-button-protected-theme; -@use '@material/button/button-outlined-theme' as mdc-button-outlined-theme; -@use '@material/theme/custom-properties' as mdc-custom-properties; - @use './button-base'; -@use '../core/mdc-helpers/mdc-helpers'; @use '../core/style/private' as style-private; +@use '../core/style/vendor-prefixes'; @use '../core/tokens/token-utils'; @use '../core/focus-indicators/private' as focus-indicators-private; @use '../core/tokens/m2/mdc/filled-button' as tokens-mdc-filled-button; @@ -21,127 +12,233 @@ @use '../core/tokens/m2/mdc/text-button' as tokens-mdc-text-button; @use '../core/tokens/m2/mat/text-button' as tokens-mat-text-button; -@include mdc-helpers.disable-mdc-fallback-declarations { - @include mdc-button.static-styles-without-ripple($query: mdc-helpers.$mdc-base-styles-query); +.mat-mdc-button-base { + text-decoration: none; } -@include mdc-custom-properties.configure($emit-fallback-values: false, $emit-fallback-vars: false) { - .mat-mdc-button { - $mdc-text-button-slots: tokens-mdc-text-button.get-token-slots(); - - @include mdc-button-text-theme.theme-styles($mdc-text-button-slots); - @include button-base.mat-private-button-horizontal-layout(tokens-mat-text-button.$prefix, - tokens-mat-text-button.get-token-slots(), true); - @include button-base.mat-private-button-ripple(tokens-mat-text-button.$prefix, - tokens-mat-text-button.get-token-slots()); - @include button-base.mat-private-button-touch-target(false, tokens-mat-text-button.$prefix, - tokens-mat-text-button.get-token-slots()); - - @include token-utils.use-tokens(tokens-mdc-text-button.$prefix, $mdc-text-button-slots) { - // We need to re-apply the disabled tokens since MDC uses - // `:disabled` which doesn't apply to anchors. - @include button-base.mat-private-button-disabled { - @include token-utils.create-token-slot(color, disabled-label-text-color); - } - } +.mdc-button { + @include vendor-prefixes.user-select(none); + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + min-width: 64px; + border: none; + outline: none; + line-height: inherit; + -webkit-appearance: none; + overflow: visible; + vertical-align: middle; + background: transparent; + padding: 0 8px; + + &::-moz-focus-inner { + padding: 0; + border: 0; } - .mat-mdc-unelevated-button { - $mdc-filled-button-slots: tokens-mdc-filled-button.get-token-slots(); - - @include mdc-button-filled-theme.theme-styles($mdc-filled-button-slots); - @include button-base.mat-private-button-horizontal-layout(tokens-mat-filled-button.$prefix, - tokens-mat-filled-button.get-token-slots(), false); - @include button-base.mat-private-button-ripple(tokens-mat-filled-button.$prefix, - tokens-mat-filled-button.get-token-slots()); - @include button-base.mat-private-button-touch-target(false, tokens-mat-filled-button.$prefix, - tokens-mat-filled-button.get-token-slots()); - - @include token-utils.use-tokens(tokens-mdc-filled-button.$prefix, $mdc-filled-button-slots) { - // We need to re-apply the disabled tokens since MDC uses - // `:disabled` which doesn't apply to anchors. - @include button-base.mat-private-button-disabled { - @include token-utils.create-token-slot(color, disabled-label-text-color); - @include token-utils.create-token-slot(background-color, disabled-container-color); - } + &:active { + outline: none; + } + + &:hover { + cursor: pointer; + } + + &:disabled { + cursor: default; + pointer-events: none; + } + + &[hidden] { + display: none; + } + + .mdc-button__label { + position: relative; + } +} + +.mat-mdc-button { + $mat-text-button-slots: tokens-mat-text-button.get-token-slots(); + + @include button-base.mat-private-button-horizontal-layout(tokens-mat-text-button.$prefix, + $mat-text-button-slots, true); + @include button-base.mat-private-button-ripple(tokens-mat-text-button.$prefix, + $mat-text-button-slots); + @include button-base.mat-private-button-touch-target(false, tokens-mat-text-button.$prefix, + $mat-text-button-slots); + + @include token-utils.use-tokens( + tokens-mdc-text-button.$prefix, + tokens-mdc-text-button.get-token-slots() + ) { + @include token-utils.create-token-slot(height, container-height); + @include token-utils.create-token-slot(font-family, label-text-font); + @include token-utils.create-token-slot(font-size, label-text-size); + @include token-utils.create-token-slot(letter-spacing, label-text-tracking); + @include token-utils.create-token-slot(text-transform, label-text-transform); + @include token-utils.create-token-slot(font-weight, label-text-weight); + + &, .mdc-button__ripple { + @include token-utils.create-token-slot(border-radius, container-shape); + } + + &:not(:disabled) { + @include token-utils.create-token-slot(color, label-text-color); + } + + // We need to re-apply the disabled tokens since MDC uses + // `:disabled` which doesn't apply to anchors. + @include button-base.mat-private-button-disabled { + @include token-utils.create-token-slot(color, disabled-label-text-color); } } +} - .mat-mdc-raised-button { - $mdc-button-protected-slots: tokens-mdc-protected-button.get-token-slots(); - - @include mdc-button-protected-theme.theme-styles(map.merge($mdc-button-protected-slots, ( - // Exclude the elevation tokens here since we output them manually below. - container-elevation: null, - hover-container-elevation: null, - disabled-container-elevation: null, - focus-container-elevation: null, - pressed-container-elevation: null, - container-shadow-color: null, - ))); - @include button-base.mat-private-button-horizontal-layout(tokens-mat-protected-button.$prefix, - tokens-mat-protected-button.get-token-slots(), false); - @include button-base.mat-private-button-ripple(tokens-mat-protected-button.$prefix, - tokens-mat-protected-button.get-token-slots()); - @include button-base.mat-private-button-touch-target(false, tokens-mat-protected-button.$prefix, - tokens-mat-protected-button.get-token-slots()); - - @include token-utils.use-tokens( - tokens-mdc-protected-button.$prefix, - $mdc-button-protected-slots) { - @include button-base.mat-private-button-elevation(container-elevation); - - &:hover { - @include button-base.mat-private-button-elevation(hover-container-elevation); - } +.mat-mdc-unelevated-button { + $mat-filled-button-slots: tokens-mat-filled-button.get-token-slots(); + transition: box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); - &:focus { - @include button-base.mat-private-button-elevation(focus-container-elevation); - } + @include button-base.mat-private-button-horizontal-layout(tokens-mat-filled-button.$prefix, + $mat-filled-button-slots, false); + @include button-base.mat-private-button-ripple(tokens-mat-filled-button.$prefix, + $mat-filled-button-slots); + @include button-base.mat-private-button-touch-target(false, tokens-mat-filled-button.$prefix, + $mat-filled-button-slots); - &:active, &:focus:active { - @include button-base.mat-private-button-elevation(pressed-container-elevation); - } + @include token-utils.use-tokens( + tokens-mdc-filled-button.$prefix, + tokens-mdc-filled-button.get-token-slots() + ) { + @include token-utils.create-token-slot(height, container-height); + @include token-utils.create-token-slot(font-family, label-text-font); + @include token-utils.create-token-slot(font-size, label-text-size); + @include token-utils.create-token-slot(letter-spacing, label-text-tracking); + @include token-utils.create-token-slot(text-transform, label-text-transform); + @include token-utils.create-token-slot(font-weight, label-text-weight); + + &:not(:disabled) { + @include token-utils.create-token-slot(color, label-text-color); + @include token-utils.create-token-slot(background-color, container-color); + } - // We need to re-apply the disabled tokens since MDC uses - // `:disabled` which doesn't apply to anchors. - @include button-base.mat-private-button-disabled { - @include token-utils.create-token-slot(color, disabled-label-text-color); - @include token-utils.create-token-slot(background-color, disabled-container-color); + &, .mdc-button__ripple { + @include token-utils.create-token-slot(border-radius, container-shape); + } - &.mat-mdc-button-disabled { - @include button-base.mat-private-button-elevation(disabled-container-elevation); - } - } + // We need to re-apply the disabled tokens since MDC uses + // `:disabled` which doesn't apply to anchors. + @include button-base.mat-private-button-disabled { + @include token-utils.create-token-slot(color, disabled-label-text-color); + @include token-utils.create-token-slot(background-color, disabled-container-color); } } +} + +.mat-mdc-raised-button { + $mat-protected-button-slots: tokens-mat-protected-button.get-token-slots(); + transition: box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1); + + @include button-base.mat-private-button-horizontal-layout(tokens-mat-protected-button.$prefix, + $mat-protected-button-slots, false); + @include button-base.mat-private-button-ripple(tokens-mat-protected-button.$prefix, + $mat-protected-button-slots); + @include button-base.mat-private-button-touch-target(false, tokens-mat-protected-button.$prefix, + $mat-protected-button-slots); + + @include token-utils.use-tokens( + tokens-mdc-protected-button.$prefix, + tokens-mdc-protected-button.get-token-slots() + ) { + @include button-base.mat-private-button-elevation(container-elevation); + @include token-utils.create-token-slot(height, container-height); + @include token-utils.create-token-slot(font-family, label-text-font); + @include token-utils.create-token-slot(font-size, label-text-size); + @include token-utils.create-token-slot(letter-spacing, label-text-tracking); + @include token-utils.create-token-slot(text-transform, label-text-transform); + @include token-utils.create-token-slot(font-weight, label-text-weight); + + &:not(:disabled) { + @include token-utils.create-token-slot(color, label-text-color); + @include token-utils.create-token-slot(background-color, container-color); + } + + &, .mdc-button__ripple { + @include token-utils.create-token-slot(border-radius, container-shape); + } + + &:hover { + @include button-base.mat-private-button-elevation(hover-container-elevation); + } + + &:focus { + @include button-base.mat-private-button-elevation(focus-container-elevation); + } - .mat-mdc-outlined-button { - $mdc-outlined-button-slots: tokens-mdc-outlined-button.get-token-slots(); - - @include mdc-button-outlined-theme.theme-styles($mdc-outlined-button-slots); - @include button-base.mat-private-button-horizontal-layout(tokens-mat-outlined-button.$prefix, - tokens-mat-outlined-button.get-token-slots(), false); - @include button-base.mat-private-button-ripple(tokens-mat-outlined-button.$prefix, - tokens-mat-outlined-button.get-token-slots()); - @include button-base.mat-private-button-touch-target(false, tokens-mat-outlined-button.$prefix, - tokens-mat-outlined-button.get-token-slots()); - - @include token-utils.use-tokens( - tokens-mdc-outlined-button.$prefix, - $mdc-outlined-button-slots) { - // We need to re-apply the disabled tokens since MDC uses - // `:disabled` which doesn't apply to anchors. - @include button-base.mat-private-button-disabled { - @include token-utils.create-token-slot(color, disabled-label-text-color); - @include token-utils.create-token-slot(border-color, disabled-outline-color); + &:active, &:focus:active { + @include button-base.mat-private-button-elevation(pressed-container-elevation); + } + + // We need to re-apply the disabled tokens since MDC uses + // `:disabled` which doesn't apply to anchors. + @include button-base.mat-private-button-disabled { + @include token-utils.create-token-slot(color, disabled-label-text-color); + @include token-utils.create-token-slot(background-color, disabled-container-color); + + &.mat-mdc-button-disabled { + @include button-base.mat-private-button-elevation(disabled-container-elevation); } } } } -.mat-mdc-button-base { - text-decoration: none; +.mat-mdc-outlined-button { + $mat-outlined-button-slots: tokens-mat-outlined-button.get-token-slots(); + border-style: solid; + transition: border 280ms cubic-bezier(0.4, 0, 0.2, 1); + + @include button-base.mat-private-button-horizontal-layout(tokens-mat-outlined-button.$prefix, + $mat-outlined-button-slots, false); + @include button-base.mat-private-button-ripple(tokens-mat-outlined-button.$prefix, + $mat-outlined-button-slots); + @include button-base.mat-private-button-touch-target(false, tokens-mat-outlined-button.$prefix, + $mat-outlined-button-slots); + + @include token-utils.use-tokens( + tokens-mdc-outlined-button.$prefix, + tokens-mdc-outlined-button.get-token-slots() + ) { + @include token-utils.create-token-slot(height, container-height); + @include token-utils.create-token-slot(font-family, label-text-font); + @include token-utils.create-token-slot(font-size, label-text-size); + @include token-utils.create-token-slot(letter-spacing, label-text-tracking); + @include token-utils.create-token-slot(text-transform, label-text-transform); + @include token-utils.create-token-slot(font-weight, label-text-weight); + @include token-utils.create-token-slot(border-radius, container-shape); + @include token-utils.create-token-slot(border-width, outline-width); + + &:not(:disabled) { + @include token-utils.create-token-slot(color, label-text-color); + @include token-utils.create-token-slot(border-color, outline-color); + } + + // We need to re-apply the disabled tokens since MDC uses + // `:disabled` which doesn't apply to anchors. + @include button-base.mat-private-button-disabled { + @include token-utils.create-token-slot(color, disabled-label-text-color); + @include token-utils.create-token-slot(border-color, disabled-outline-color); + } + + // TODO(crisbeto): this causes a weird gap between the ripple and the + // outline. We should remove it and update the screenshot tests. + .mdc-button__ripple { + @include token-utils.create-token-slot(border-width, outline-width); + border-style: solid; + border-color: transparent; + } + } } .mat-mdc-button, @@ -173,12 +270,11 @@ // then. See: https://github.com/angular/components/issues/13738 .mat-mdc-outlined-button .mat-mdc-button-ripple, .mat-mdc-outlined-button .mdc-button__ripple { - $offset: -(mdc-button-variables.$outlined-border-width); + $offset: -1px; top: $offset; left: $offset; bottom: $offset; right: $offset; - border-width: $offset; } // For the button element, default inset/offset values are necessary to ensure that diff --git a/src/material/core/tokens/m2/mdc/_extended-fab.scss b/src/material/core/tokens/m2/mdc/_extended-fab.scss index 97fbbafb5c75..042c01eced40 100644 --- a/src/material/core/tokens/m2/mdc/_extended-fab.scss +++ b/src/material/core/tokens/m2/mdc/_extended-fab.scss @@ -10,6 +10,10 @@ $prefix: (mdc, extended-fab); @return ( container-height: 48px, container-shape: 24px, + container-elevation-shadow: elevation.get-box-shadow(6), + focus-container-elevation-shadow: elevation.get-box-shadow(8), + hover-container-elevation-shadow: elevation.get-box-shadow(8), + pressed-container-elevation-shadow: elevation.get-box-shadow(12), // ============================================================================================= // = TOKENS NOT USED IN ANGULAR MATERIAL = @@ -49,12 +53,7 @@ $prefix: (mdc, extended-fab); // Tokens that can be configured through Angular Material's color theming API. @function get-color-tokens($theme) { - @return ( - container-elevation-shadow: elevation.get-box-shadow(6), - focus-container-elevation-shadow: elevation.get-box-shadow(8), - hover-container-elevation-shadow: elevation.get-box-shadow(8), - pressed-container-elevation-shadow: elevation.get-box-shadow(12), - ); + @return (); } // Tokens that can be configured through Angular Material's typography theming API. diff --git a/src/material/core/tokens/m2/mdc/_fab-small.scss b/src/material/core/tokens/m2/mdc/_fab-small.scss index 1e540a97ebd0..9aaf82d19229 100644 --- a/src/material/core/tokens/m2/mdc/_fab-small.scss +++ b/src/material/core/tokens/m2/mdc/_fab-small.scss @@ -9,6 +9,10 @@ $prefix: (mdc, fab-small); @function get-unthemable-tokens() { @return ( container-shape: 50%, + container-elevation-shadow: elevation.get-box-shadow(6), + focus-container-elevation-shadow: elevation.get-box-shadow(8), + hover-container-elevation-shadow: elevation.get-box-shadow(8), + pressed-container-elevation-shadow: elevation.get-box-shadow(12), // ============================================================================================= // = TOKENS NOT USED IN ANGULAR MATERIAL = @@ -52,10 +56,6 @@ $prefix: (mdc, fab-small); @return ( // Background color of the FAB. container-color: inspection.get-theme-color($theme, background, card), - container-elevation-shadow: elevation.get-box-shadow(6), - focus-container-elevation-shadow: elevation.get-box-shadow(8), - hover-container-elevation-shadow: elevation.get-box-shadow(8), - pressed-container-elevation-shadow: elevation.get-box-shadow(12), ); } diff --git a/src/material/core/tokens/m2/mdc/_fab.scss b/src/material/core/tokens/m2/mdc/_fab.scss index 559b1dc6e308..b751b75ee9f1 100644 --- a/src/material/core/tokens/m2/mdc/_fab.scss +++ b/src/material/core/tokens/m2/mdc/_fab.scss @@ -9,6 +9,10 @@ $prefix: (mdc, fab); @function get-unthemable-tokens() { @return ( container-shape: 50%, + container-elevation-shadow: elevation.get-box-shadow(6), + focus-container-elevation-shadow: elevation.get-box-shadow(8), + hover-container-elevation-shadow: elevation.get-box-shadow(8), + pressed-container-elevation-shadow: elevation.get-box-shadow(12), // ============================================================================================= // = TOKENS NOT USED IN ANGULAR MATERIAL = @@ -53,10 +57,6 @@ $prefix: (mdc, fab); @return ( // Background color of the FAB. container-color: inspection.get-theme-color($theme, background, card), - container-elevation-shadow: elevation.get-box-shadow(6), - focus-container-elevation-shadow: elevation.get-box-shadow(8), - hover-container-elevation-shadow: elevation.get-box-shadow(8), - pressed-container-elevation-shadow: elevation.get-box-shadow(12), ); } diff --git a/src/material/core/tokens/m2/mdc/_protected-button.scss b/src/material/core/tokens/m2/mdc/_protected-button.scss index c3b8eac6211e..072d82bd938a 100644 --- a/src/material/core/tokens/m2/mdc/_protected-button.scss +++ b/src/material/core/tokens/m2/mdc/_protected-button.scss @@ -1,6 +1,7 @@ @use 'sass:map'; @use '../../token-utils'; @use '../../../style/sass-utils'; +@use '../../../style/elevation'; @use '../../../theming/inspection'; @use '../../../theming/theming'; @use '../../../mdc-helpers/mdc-helpers'; @@ -17,7 +18,11 @@ $prefix: (mdc, protected-button); @function get-unthemable-tokens() { @return ( container-shape: 4px, - keep-touch-target: false, + container-elevation-shadow: elevation.get-box-shadow(2), + disabled-container-elevation-shadow: elevation.get-box-shadow(0), + focus-container-elevation-shadow: elevation.get-box-shadow(4), + hover-container-elevation-shadow: elevation.get-box-shadow(4), + pressed-container-elevation-shadow: elevation.get-box-shadow(8), // ============================================================================================= // = TOKENS NOT USED IN ANGULAR MATERIAL = @@ -39,6 +44,13 @@ $prefix: (mdc, protected-button); focus-state-layer-color: null, hover-state-layer-color: null, pressed-state-layer-color: null, + keep-touch-target: null, + container-elevation: null, + disabled-container-elevation: null, + focus-container-elevation: null, + hover-container-elevation: null, + pressed-container-elevation: null, + container-shadow-color: null, ); } @@ -53,12 +65,6 @@ $prefix: (mdc, protected-button); 0.12), disabled-label-text-color: inspection.get-theme-color($theme, foreground, disabled-button, if($is-dark, 0.5, 0.38)), - container-elevation: 2, - disabled-container-elevation: 0, - focus-container-elevation: 4, - hover-container-elevation: 4, - pressed-container-elevation: 8, - container-shadow-color: #000, ); } diff --git a/src/material/core/tokens/m3/mdc/_protected-button.scss b/src/material/core/tokens/m3/mdc/_protected-button.scss index 98b11a2b2b34..ead16381af9b 100644 --- a/src/material/core/tokens/m3/mdc/_protected-button.scss +++ b/src/material/core/tokens/m3/mdc/_protected-button.scss @@ -1,5 +1,6 @@ @use 'sass:map'; @use 'sass:meta'; +@use '../../../style/elevation'; @use '../../token-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -12,7 +13,7 @@ $prefix: (mdc, protected-button); /// @return {Map} A set of tokens for the MDC protected-button @function get-tokens($systems, $exclude-hardcoded, $token-slots) { // Note: in M3 the "protected" button is called "elevated". - $mdc-tokens: token-utils.get-mdc-tokens('elevated-button', $systems, $exclude-hardcoded); + $tokens: token-utils.get-mdc-tokens('elevated-button', $systems, $exclude-hardcoded); $variant-tokens: ( primary: (), // Default, no overrides needed. secondary: ( @@ -57,7 +58,7 @@ $prefix: (mdc, protected-button); ); @return token-utils.namespace-tokens($prefix, ( - _fix-tokens($mdc-tokens), + _fix-tokens($tokens), token-utils.map-values($variant-tokens, meta.get-function(_fix-tokens)), ), $token-slots); } @@ -67,11 +68,27 @@ $prefix: (mdc, protected-button); /// @param {Map} $initial-tokens Map of protected button tokens currently being generated. /// @return {Map} The given tokens, with the invalid values replaced with valid ones. @function _fix-tokens($initial-tokens) { - // Need to get the hardcoded values, because they include opacities that are used for the disabled - // state. + // Need to get the hardcoded values, because they include + // opacities that are used for the disabled state. $hardcoded-tokens: token-utils.get-mdc-tokens('elevated-button', (), false); + $tokens: $initial-tokens; + $elevation-tokens: ( + container-elevation, + disabled-container-elevation, + focus-container-elevation, + hover-container-elevation, + pressed-container-elevation, + ); + + @each $token in $elevation-tokens { + $elevation: map.get($tokens, $token); + + @if ($elevation != null) { + $tokens: map.set($tokens, $token + '-shadow', elevation.get-box-shadow($elevation)); + } + } - @return token-utils.combine-color-tokens($initial-tokens, $hardcoded-tokens, ( + @return token-utils.combine-color-tokens($tokens, $hardcoded-tokens, ( ( color: disabled-label-text-color, opacity: disabled-label-text-opacity, diff --git a/src/material/core/tokens/tests/test-validate-tokens.scss b/src/material/core/tokens/tests/test-validate-tokens.scss index cd2d89205689..42fd0bfa32d3 100644 --- a/src/material/core/tokens/tests/test-validate-tokens.scss +++ b/src/material/core/tokens/tests/test-validate-tokens.scss @@ -2,7 +2,6 @@ @use 'sass:map'; @use '@material/button/button-outlined-theme' as mdc-button-outlined-theme; -@use '@material/button/button-protected-theme' as mdc-button-protected-theme; @use '@material/button/button-filled-theme' as mdc-button-filled-theme; @use '@material/button/button-text-theme' as mdc-button-text-theme; @use '@material/card/elevated-card-theme' as mdc-elevated-card-theme; @@ -23,7 +22,6 @@ @use '@material/textfield/outlined-text-field-theme' as mdc-outlined-text-field-theme; @use '@material/theme/validate' as mdc-validate; -@use '../m2/mdc/protected-button' as tokens-mdc-protected-button; @use '../m2/mdc/filled-button' as tokens-mdc-filled-button; @use '../m2/mdc/text-button' as tokens-mdc-text-button; @use '../m2/mdc/outlined-button' as tokens-mdc-outlined-button; @@ -141,11 +139,6 @@ $slots: tokens-mdc-filled-button.get-token-slots(), $reference: mdc-button-filled-theme.$light-theme ); -@include validate-slots( - $component: 'm2.mdc.protected-button', - $slots: tokens-mdc-protected-button.get-token-slots(), - $reference: mdc-button-protected-theme.$light-theme -); @include validate-slots( $component: 'm2.mdc.text-button', $slots: tokens-mdc-text-button.get-token-slots(), From bfa3ac5da49b174689cafcbed353bb7df8e7842b Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 4 Jul 2024 05:26:45 +0000 Subject: [PATCH 14/22] fix(cdk/drag-drop): reset pointer events on descendants (#29370) When we create the preview and placeholder, we set `pointer-events: none` on them so they don't interfere with the `elementFromPoint` calls, however descendants of the element could be resetting it back to `auto` for themselves. These changes update the reset to prevent it from happening. --- src/cdk/drag-drop/resets.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cdk/drag-drop/resets.scss b/src/cdk/drag-drop/resets.scss index f218c05e51cc..530f97dbe5cc 100644 --- a/src/cdk/drag-drop/resets.scss +++ b/src/cdk/drag-drop/resets.scss @@ -6,3 +6,10 @@ color: inherit; } } + +// These elements get `pointer-events: none` when they're created, but any descendants might +// override it back to `auto`. Reset them here since they can affect the pointer position detection. +.cdk-drag-placeholder *, +.cdk-drag-preview * { + pointer-events: none !important; +} From 045806bfd2811fde9ec7a2448e67d676eca641b5 Mon Sep 17 00:00:00 2001 From: lindoRR-07 Date: Fri, 5 Jul 2024 12:36:22 +0200 Subject: [PATCH 15/22] docs(material/table): Example suggesting to use max-width to set column width is misleading (#29373) The fix is to use the width property instead of max-width since this can be misleading as though the width will never exceed the max-width provided. --- .../material/table/table-http/table-http-example.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components-examples/material/table/table-http/table-http-example.css b/src/components-examples/material/table/table-http/table-http-example.css index 1ecd1a098c1d..2f3caee0c42a 100644 --- a/src/components-examples/material/table/table-http/table-http-example.css +++ b/src/components-examples/material/table/table-http/table-http-example.css @@ -35,9 +35,9 @@ table { /* Column Widths */ .mat-column-number, .mat-column-state { - max-width: 64px; + width: 64px; } .mat-column-created { - max-width: 124px; + width: 124px; } From ccb996959bcdc3655f5d95b0b5779a5f44e1ed3f Mon Sep 17 00:00:00 2001 From: abdulloh-abid <10852025+abdulloh-abid@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:37:27 +0100 Subject: [PATCH 16/22] docs(material/form-field): unable to toggle icon in demo file (#29354) Fixes a bug in demo file where icon cannot be toggled. This is because `signal` was not called correctly --- .../form-field-prefix-suffix-example.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components-examples/material/form-field/form-field-prefix-suffix/form-field-prefix-suffix-example.ts b/src/components-examples/material/form-field/form-field-prefix-suffix/form-field-prefix-suffix-example.ts index 98f37a5ad0a9..6f796f2d569a 100644 --- a/src/components-examples/material/form-field/form-field-prefix-suffix/form-field-prefix-suffix-example.ts +++ b/src/components-examples/material/form-field/form-field-prefix-suffix/form-field-prefix-suffix-example.ts @@ -16,7 +16,7 @@ import {MatInputModule} from '@angular/material/input'; export class FormFieldPrefixSuffixExample { hide = signal(true); clickEvent(event: MouseEvent) { - this.hide.set(!this.hide); + this.hide.set(!this.hide()); event.stopPropagation(); } } From 39b15dcc527f054733eaaa5916d51a77bb53fb61 Mon Sep 17 00:00:00 2001 From: HD Haasbroek <35131918+hhaasbroek@users.noreply.github.com> Date: Fri, 5 Jul 2024 14:58:12 +0200 Subject: [PATCH 17/22] docs(material/datepicker): Added context to docs for date picker null value #29385 (#29387) * Added context for why the null value is there * Fixed formatting * removed extra * --- src/material/datepicker/datepicker-input-base.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/material/datepicker/datepicker-input-base.ts b/src/material/datepicker/datepicker-input-base.ts index 611d39a2ebf7..f0980c5a6c9e 100644 --- a/src/material/datepicker/datepicker-input-base.ts +++ b/src/material/datepicker/datepicker-input-base.ts @@ -56,7 +56,11 @@ export class MatDatepickerInputEvent { } } -/** Function that can be used to filter out dates from a calendar. */ +/** + * Function that can be used to filter out dates from a calendar. + * Datepicker can sometimes receive a null value as input for the date argument. + * This doesn't represent a "null date" but rather signifies that no date has been selected yet in the calendar. + */ export type DateFilterFn = (date: D | null) => boolean; /** From e6535b725563cb03e7489fe42e965800a43a9b93 Mon Sep 17 00:00:00 2001 From: zizifn <1803942+zizifn@users.noreply.github.com> Date: Sun, 7 Jul 2024 16:38:52 +0800 Subject: [PATCH 18/22] docs(cdk/listbox): remove forms validation and validation example (#29391) --- src/cdk/listbox/listbox.md | 17 ----------------- .../cdk-listbox-forms-validation-example.html | 3 --- .../cdk-listbox-forms-validation-example.ts | 8 +------- 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/src/cdk/listbox/listbox.md b/src/cdk/listbox/listbox.md index 542428177713..021b24a905aa 100644 --- a/src/cdk/listbox/listbox.md +++ b/src/cdk/listbox/listbox.md @@ -120,23 +120,6 @@ The CDK Listbox supports both template driven forms and reactive forms. "region": "listbox" }) --> -#### Forms validation - -The CDK listbox integrates with Angular's form validation API and has the following built-in -validation errors: - -- `cdkListboxUnexpectedOptionValues` - Raised when the bound value contains values that do not - appear as option value in the listbox. The validation error contains a `values` property that - lists the invalid values -- `cdkListboxUnexpectedMultipleValues` - Raised when a single-selection listbox is bound to a value - containing multiple selected options. - - - ### Disabling options You can disable options for selection by setting `cdkOptionDisabled`. diff --git a/src/components-examples/cdk/listbox/cdk-listbox-forms-validation/cdk-listbox-forms-validation-example.html b/src/components-examples/cdk/listbox/cdk-listbox-forms-validation/cdk-listbox-forms-validation-example.html index 391e26f1a7b2..0acdf0444327 100644 --- a/src/components-examples/cdk/listbox/cdk-listbox-forms-validation/cdk-listbox-forms-validation-example.html +++ b/src/components-examples/cdk/listbox/cdk-listbox-forms-validation/cdk-listbox-forms-validation-example.html @@ -20,8 +20,5 @@ }

Your zodiac sign is: {{signCtrl.value | json}}  -   -   -  

diff --git a/src/components-examples/cdk/listbox/cdk-listbox-forms-validation/cdk-listbox-forms-validation-example.ts b/src/components-examples/cdk/listbox/cdk-listbox-forms-validation/cdk-listbox-forms-validation-example.ts index fef79e8ae553..5da22612c0d8 100644 --- a/src/components-examples/cdk/listbox/cdk-listbox-forms-validation/cdk-listbox-forms-validation-example.ts +++ b/src/components-examples/cdk/listbox/cdk-listbox-forms-validation/cdk-listbox-forms-validation-example.ts @@ -45,13 +45,7 @@ export class CdkListboxFormsValidationExample { if (this.signCtrl.hasError('required')) { errors.push('You must enter your zodiac sign'); } - if (this.signCtrl.hasError('cdkListboxUnexpectedMultipleValues')) { - errors.push('You can only select one zodiac sign'); - } - if (this.signCtrl.hasError('cdkListboxUnexpectedOptionValues')) { - const invalidOptions = this.signCtrl.getError('cdkListboxUnexpectedOptionValues').values; - errors.push(`You entered an invalid zodiac sign: ${invalidOptions[0]}`); - } + return errors.length ? errors : null; } // #enddocregion errors From e95d88cdeb2a7905c1897917f2b335d0ef53cb31 Mon Sep 17 00:00:00 2001 From: Matthieu Riegler Date: Mon, 8 Jul 2024 18:30:23 +0200 Subject: [PATCH 19/22] fix(material/tabs): remove visibility style when hydrating (#29220) Before this commit, when using pre-rendering, the styling animation would only remove the `transform` style when hydrating the component. This commit fixes this by including `visibility` as styles to remove when playing the animations. --- src/material/tabs/tab-group.spec.ts | 6 +++--- src/material/tabs/tabs-animations.ts | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/material/tabs/tab-group.spec.ts b/src/material/tabs/tab-group.spec.ts index c93b7ca3cd89..bcd7ce602283 100644 --- a/src/material/tabs/tab-group.spec.ts +++ b/src/material/tabs/tab-group.spec.ts @@ -792,7 +792,7 @@ describe('MDC-based MatTabGroup', () => { ); expect(contentElements.map(element => element.style.visibility)).toEqual([ - '', + 'visible', 'hidden', 'hidden', 'hidden', @@ -805,7 +805,7 @@ describe('MDC-based MatTabGroup', () => { expect(contentElements.map(element => element.style.visibility)).toEqual([ 'hidden', 'hidden', - '', + 'visible', 'hidden', ]); @@ -815,7 +815,7 @@ describe('MDC-based MatTabGroup', () => { expect(contentElements.map(element => element.style.visibility)).toEqual([ 'hidden', - '', + 'visible', 'hidden', 'hidden', ]); diff --git a/src/material/tabs/tabs-animations.ts b/src/material/tabs/tabs-animations.ts index 5e7955d4a373..e0097556cba2 100644 --- a/src/material/tabs/tabs-animations.ts +++ b/src/material/tabs/tabs-animations.ts @@ -6,12 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ import { + AnimationTriggerMetadata, animate, state, style, transition, trigger, - AnimationTriggerMetadata, } from '@angular/animations'; /** @@ -24,7 +24,10 @@ export const matTabsAnimations: { /** Animation translates a tab along the X axis. */ translateTab: trigger('translateTab', [ // Transitions to `none` instead of 0, because some browsers might blur the content. - state('center, void, left-origin-center, right-origin-center', style({transform: 'none'})), + state( + 'center, void, left-origin-center, right-origin-center', + style({transform: 'none', visibility: 'visible'}), + ), // If the tab is either on the left or right, we additionally add a `min-height` of 1px // in order to ensure that the element has a height before its state changes. This is From b2c051d2c1b67f4c149aee1573a4aceddb496157 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 8 Jul 2024 16:47:25 +0000 Subject: [PATCH 20/22] feat(cdk/drag-drop): add input to specify dragged item scale (#29392) In some cases the parent of the dragged element might be scaled (e.g. when implementing zoom) which can throw off the positioning. Detecting this would be expensive, because we'd have to check the entire DOM tree so instead these changes add an input so the user can specify the scale amount. --- src/cdk/drag-drop/directives/drag.ts | 14 +++++ .../directives/drop-list-shared.spec.ts | 2 + .../directives/single-axis-drop-list.spec.ts | 25 +++++++++ .../directives/standalone-drag.spec.ts | 53 +++++++++++-------- src/cdk/drag-drop/drag-ref.ts | 9 +++- .../sorting/single-axis-sort-strategy.ts | 6 ++- tools/public_api_guard/cdk/drag-drop.md | 6 ++- 7 files changed, 89 insertions(+), 26 deletions(-) diff --git a/src/cdk/drag-drop/directives/drag.ts b/src/cdk/drag-drop/directives/drag.ts index 8d0f5c120113..f4469556e027 100644 --- a/src/cdk/drag-drop/directives/drag.ts +++ b/src/cdk/drag-drop/directives/drag.ts @@ -30,6 +30,7 @@ import { AfterViewInit, inject, Injector, + numberAttribute, } from '@angular/core'; import {coerceElement, coerceNumberProperty} from '@angular/cdk/coercion'; import {BehaviorSubject, Observable, Observer, Subject, merge} from 'rxjs'; @@ -159,6 +160,13 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { */ @Input('cdkDragPreviewContainer') previewContainer: PreviewContainer; + /** + * If the parent of the dragged element has a `scale` transform, it can throw off the + * positioning when the user starts dragging. Use this input to notify the CDK of the scale. + */ + @Input({alias: 'cdkDragScale', transform: numberAttribute}) + scale: number = 1; + /** Emits when the user starts dragging the item. */ @Output('cdkDragStarted') readonly started: EventEmitter = new EventEmitter(); @@ -261,6 +269,11 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { if (dropContainer) { this._dragRef._withDropContainer(dropContainer._dropListRef); dropContainer.addItem(this); + + // The drop container reads this so we need to sync it here. + dropContainer._dropListRef.beforeStarted.pipe(takeUntil(this._destroyed)).subscribe(() => { + this._dragRef.scale = this.scale; + }); } this._syncInputs(this._dragRef); @@ -448,6 +461,7 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { ref.disabled = this.disabled; ref.lockAxis = this.lockAxis; + ref.scale = this.scale; ref.dragStartDelay = typeof dragStartDelay === 'object' && dragStartDelay ? dragStartDelay diff --git a/src/cdk/drag-drop/directives/drop-list-shared.spec.ts b/src/cdk/drag-drop/directives/drop-list-shared.spec.ts index 4e9a6bf2b32b..fef579b3891a 100644 --- a/src/cdk/drag-drop/directives/drop-list-shared.spec.ts +++ b/src/cdk/drag-drop/directives/drop-list-shared.spec.ts @@ -5006,6 +5006,7 @@ const DROP_ZONE_FIXTURE_TEMPLATE = ` [cdkDragBoundary]="boundarySelector" [cdkDragPreviewClass]="previewClass" [cdkDragPreviewContainer]="previewContainer" + [cdkDragScale]="scale" [style.height.px]="item.height" [style.margin-bottom.px]="item.margin" (cdkDragStarted)="startedSpy($event)" @@ -5041,6 +5042,7 @@ export class DraggableInDropZone implements AfterViewInit { previewContainer: PreviewContainer = 'global'; dropDisabled = signal(false); dropLockAxis = signal(undefined); + scale = 1; constructor(protected _elementRef: ElementRef) {} diff --git a/src/cdk/drag-drop/directives/single-axis-drop-list.spec.ts b/src/cdk/drag-drop/directives/single-axis-drop-list.spec.ts index 324ccc8b8776..28e8938b6c32 100644 --- a/src/cdk/drag-drop/directives/single-axis-drop-list.spec.ts +++ b/src/cdk/drag-drop/directives/single-axis-drop-list.spec.ts @@ -311,4 +311,29 @@ describe('Single-axis drop list', () => { dispatchMouseEvent(document, 'mouseup'); })); + + it('should lay out the elements correctly when scaled', fakeAsync(() => { + const fixture = createComponent(DraggableInDropZone); + fixture.componentInstance.scale = 0.5; + fixture.detectChanges(); + + const items = fixture.componentInstance.dragItems.map(i => i.element.nativeElement); + const {top, left} = items[0].getBoundingClientRect(); + + startDraggingViaMouse(fixture, items[0], left, top); + + const placeholder = document.querySelector('.cdk-drag-placeholder')! as HTMLElement; + const target = items[1]; + const targetRect = target.getBoundingClientRect(); + + dispatchMouseEvent(document, 'mousemove', targetRect.left, targetRect.top + 5); + fixture.detectChanges(); + + expect(placeholder.style.transform).toBe(`translate3d(0px, ${ITEM_HEIGHT * 2}px, 0px)`); + expect(target.style.transform).toBe(`translate3d(0px, ${-ITEM_HEIGHT * 2}px, 0px)`); + + dispatchMouseEvent(document, 'mouseup'); + fixture.detectChanges(); + flush(); + })); }); diff --git a/src/cdk/drag-drop/directives/standalone-drag.spec.ts b/src/cdk/drag-drop/directives/standalone-drag.spec.ts index 4a7e14a8d9a8..33f0588260cf 100644 --- a/src/cdk/drag-drop/directives/standalone-drag.spec.ts +++ b/src/cdk/drag-drop/directives/standalone-drag.spec.ts @@ -1470,34 +1470,41 @@ describe('Standalone CdkDrag', () => { cleanup(); })); - it( - 'should update the free drag position if the user moves their pointer after the page ' + - 'is scrolled', - fakeAsync(() => { - const fixture = createComponent(StandaloneDraggable); - fixture.detectChanges(); + it('should update the free drag position if the user moves their pointer after the page is scrolled', fakeAsync(() => { + const fixture = createComponent(StandaloneDraggable); + fixture.detectChanges(); - const cleanup = makeScrollable(); - const dragElement = fixture.componentInstance.dragElement.nativeElement; + const cleanup = makeScrollable(); + const dragElement = fixture.componentInstance.dragElement.nativeElement; - expect(dragElement.style.transform).toBeFalsy(); - startDraggingViaMouse(fixture, dragElement, 0, 0); - dispatchMouseEvent(document, 'mousemove', 50, 100); - fixture.detectChanges(); + expect(dragElement.style.transform).toBeFalsy(); + startDraggingViaMouse(fixture, dragElement, 0, 0); + dispatchMouseEvent(document, 'mousemove', 50, 100); + fixture.detectChanges(); - expect(dragElement.style.transform).toBe('translate3d(50px, 100px, 0px)'); + expect(dragElement.style.transform).toBe('translate3d(50px, 100px, 0px)'); - scrollTo(0, 500); - dispatchFakeEvent(document, 'scroll'); - fixture.detectChanges(); - dispatchMouseEvent(document, 'mousemove', 50, 200); - fixture.detectChanges(); + scrollTo(0, 500); + dispatchFakeEvent(document, 'scroll'); + fixture.detectChanges(); + dispatchMouseEvent(document, 'mousemove', 50, 200); + fixture.detectChanges(); - expect(dragElement.style.transform).toBe('translate3d(50px, 700px, 0px)'); + expect(dragElement.style.transform).toBe('translate3d(50px, 700px, 0px)'); - cleanup(); - }), - ); + cleanup(); + })); + + it('should account for scale when moving the element', fakeAsync(() => { + const fixture = createComponent(StandaloneDraggable); + fixture.componentInstance.scale = 0.5; + fixture.detectChanges(); + const dragElement = fixture.componentInstance.dragElement.nativeElement; + + expect(dragElement.style.transform).toBeFalsy(); + dragElementViaMouse(fixture, dragElement, 50, 100); + expect(dragElement.style.transform).toBe('translate3d(100px, 200px, 0px)'); + })); describe('with a handle', () => { it('should not be able to drag the entire element if it has a handle', fakeAsync(() => { @@ -1718,6 +1725,7 @@ describe('Standalone CdkDrag', () => { [cdkDragFreeDragPosition]="freeDragPosition" [cdkDragDisabled]="dragDisabled()" [cdkDragLockAxis]="dragLockAxis()" + [cdkDragScale]="scale" (cdkDragStarted)="startedSpy($event)" (cdkDragReleased)="releasedSpy($event)" (cdkDragEnded)="endedSpy($event)" @@ -1745,6 +1753,7 @@ class StandaloneDraggable { freeDragPosition?: {x: number; y: number}; dragDisabled = signal(false); dragLockAxis = signal(undefined); + scale = 1; } @Component({ diff --git a/src/cdk/drag-drop/drag-ref.ts b/src/cdk/drag-drop/drag-ref.ts index 2b751e373171..a8ed7bb55f5f 100644 --- a/src/cdk/drag-drop/drag-ref.ts +++ b/src/cdk/drag-drop/drag-ref.ts @@ -288,6 +288,12 @@ export class DragRef { /** Class to be added to the preview element. */ previewClass: string | string[] | undefined; + /** + * If the parent of the dragged element has a `scale` transform, it can throw off the + * positioning when the user starts dragging. Use this input to notify the CDK of the scale. + */ + scale: number = 1; + /** Whether starting to drag this element is disabled. */ get disabled(): boolean { return this._disabled || !!(this._dropContainer && this._dropContainer.disabled); @@ -1288,7 +1294,8 @@ export class DragRef { * @param y New transform value along the Y axis. */ private _applyRootElementTransform(x: number, y: number) { - const transform = getTransform(x, y); + const scale = 1 / this.scale; + const transform = getTransform(x * scale, y * scale); const styles = this._rootElement.style; // Cache the previous transform amount only after the first drag sequence, because diff --git a/src/cdk/drag-drop/sorting/single-axis-sort-strategy.ts b/src/cdk/drag-drop/sorting/single-axis-sort-strategy.ts index 28ed95c6517b..9d3ad3a99702 100644 --- a/src/cdk/drag-drop/sorting/single-axis-sort-strategy.ts +++ b/src/cdk/drag-drop/sorting/single-axis-sort-strategy.ts @@ -128,6 +128,8 @@ export class SingleAxisSortStrategy implements DropListSortStrategy { // Update the offset to reflect the new position. sibling.offset += offset; + const transformAmount = Math.round(sibling.offset * (1 / sibling.drag.scale)); + // Since we're moving the items with a `transform`, we need to adjust their cached // client rects to reflect their new position, as well as swap their positions in the cache. // Note that we shouldn't use `getBoundingClientRect` here to update the cache, because the @@ -136,13 +138,13 @@ export class SingleAxisSortStrategy implements DropListSortStrategy { // Round the transforms since some browsers will // blur the elements, for sub-pixel transforms. elementToOffset.style.transform = combineTransforms( - `translate3d(${Math.round(sibling.offset)}px, 0, 0)`, + `translate3d(${transformAmount}px, 0, 0)`, sibling.initialTransform, ); adjustDomRect(sibling.clientRect, 0, offset); } else { elementToOffset.style.transform = combineTransforms( - `translate3d(0, ${Math.round(sibling.offset)}px, 0)`, + `translate3d(0, ${transformAmount}px, 0)`, sibling.initialTransform, ); adjustDomRect(sibling.clientRect, offset, 0); diff --git a/tools/public_api_guard/cdk/drag-drop.md b/tools/public_api_guard/cdk/drag-drop.md index 82b57b513f0d..6611f665f4f7 100644 --- a/tools/public_api_guard/cdk/drag-drop.md +++ b/tools/public_api_guard/cdk/drag-drop.md @@ -76,6 +76,8 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { // (undocumented) static ngAcceptInputType_disabled: unknown; // (undocumented) + static ngAcceptInputType_scale: unknown; + // (undocumented) ngAfterViewInit(): void; // (undocumented) ngOnChanges(changes: SimpleChanges): void; @@ -92,6 +94,7 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { // (undocumented) _resetPreviewTemplate(preview: CdkDragPreview): void; rootElementSelector: string; + scale: number; setFreeDragPosition(value: Point): void; // (undocumented) _setPlaceholderTemplate(placeholder: CdkDragPlaceholder): void; @@ -99,7 +102,7 @@ export class CdkDrag implements AfterViewInit, OnChanges, OnDestroy { _setPreviewTemplate(preview: CdkDragPreview): void; readonly started: EventEmitter; // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration, "[cdkDrag]", ["cdkDrag"], { "data": { "alias": "cdkDragData"; "required": false; }; "lockAxis": { "alias": "cdkDragLockAxis"; "required": false; }; "rootElementSelector": { "alias": "cdkDragRootElement"; "required": false; }; "boundaryElement": { "alias": "cdkDragBoundary"; "required": false; }; "dragStartDelay": { "alias": "cdkDragStartDelay"; "required": false; }; "freeDragPosition": { "alias": "cdkDragFreeDragPosition"; "required": false; }; "disabled": { "alias": "cdkDragDisabled"; "required": false; }; "constrainPosition": { "alias": "cdkDragConstrainPosition"; "required": false; }; "previewClass": { "alias": "cdkDragPreviewClass"; "required": false; }; "previewContainer": { "alias": "cdkDragPreviewContainer"; "required": false; }; }, { "started": "cdkDragStarted"; "released": "cdkDragReleased"; "ended": "cdkDragEnded"; "entered": "cdkDragEntered"; "exited": "cdkDragExited"; "dropped": "cdkDragDropped"; "moved": "cdkDragMoved"; }, never, never, true, never>; + static ɵdir: i0.ɵɵDirectiveDeclaration, "[cdkDrag]", ["cdkDrag"], { "data": { "alias": "cdkDragData"; "required": false; }; "lockAxis": { "alias": "cdkDragLockAxis"; "required": false; }; "rootElementSelector": { "alias": "cdkDragRootElement"; "required": false; }; "boundaryElement": { "alias": "cdkDragBoundary"; "required": false; }; "dragStartDelay": { "alias": "cdkDragStartDelay"; "required": false; }; "freeDragPosition": { "alias": "cdkDragFreeDragPosition"; "required": false; }; "disabled": { "alias": "cdkDragDisabled"; "required": false; }; "constrainPosition": { "alias": "cdkDragConstrainPosition"; "required": false; }; "previewClass": { "alias": "cdkDragPreviewClass"; "required": false; }; "previewContainer": { "alias": "cdkDragPreviewContainer"; "required": false; }; "scale": { "alias": "cdkDragScale"; "required": false; }; }, { "started": "cdkDragStarted"; "released": "cdkDragReleased"; "ended": "cdkDragEnded"; "entered": "cdkDragEntered"; "exited": "cdkDragExited"; "dropped": "cdkDragDropped"; "moved": "cdkDragMoved"; }, never, never, true, never>; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration, [null, { optional: true; skipSelf: true; }, null, null, null, { optional: true; }, { optional: true; }, null, null, { optional: true; self: true; }, { optional: true; skipSelf: true; }]>; } @@ -440,6 +443,7 @@ export class DragRef { event: MouseEvent | TouchEvent; }>; reset(): void; + scale: number; setFreeDragPosition(value: Point): this; _sortFromLastPointerPosition(): void; readonly started: Subject<{ From bbf2dbbb2b6115b238141235bcda28b0a2242bc3 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 8 Jul 2024 19:24:30 +0000 Subject: [PATCH 21/22] refactor(material/list): simplify structural styles (#29383) Reworks the structural styles for the list to make them smaller and easier to maintain. --- src/material/list/BUILD.bazel | 10 + .../list/_list-inherited-structure.scss | 516 ++++++++++++++++++ .../list/_list-item-hcm-indicator.scss | 5 +- .../_list-option-trailing-avatar-compat.scss | 59 -- src/material/list/_list-theme.scss | 35 +- src/material/list/list-option.scss | 61 ++- src/material/list/list-option.ts | 18 + src/material/list/list.scss | 126 ++--- src/material/list/list.ts | 7 + tools/public_api_guard/material/list.md | 4 + 10 files changed, 680 insertions(+), 161 deletions(-) create mode 100644 src/material/list/_list-inherited-structure.scss delete mode 100644 src/material/list/_list-option-trailing-avatar-compat.scss diff --git a/src/material/list/BUILD.bazel b/src/material/list/BUILD.bazel index 5bd7a26b1467..ded4cec61b5d 100644 --- a/src/material/list/BUILD.bazel +++ b/src/material/list/BUILD.bazel @@ -42,6 +42,15 @@ sass_library( ], ) +sass_library( + name = "inherited_structure_scss_lib", + srcs = ["_list-inherited-structure.scss"], + deps = [ + "//src/cdk:sass_lib", + "//src/material/core:core_scss_lib", + ], +) + sass_library( name = "list_scss_lib", srcs = glob(["**/_*.scss"]), @@ -59,6 +68,7 @@ sass_binary( src = "list.scss", deps = [ ":hcm_indicator_scss_lib", + ":inherited_structure_scss_lib", "//:mdc_sass_lib", "//src/material/core:core_scss_lib", ], diff --git a/src/material/list/_list-inherited-structure.scss b/src/material/list/_list-inherited-structure.scss new file mode 100644 index 000000000000..f8dea90da71d --- /dev/null +++ b/src/material/list/_list-inherited-structure.scss @@ -0,0 +1,516 @@ +@use '@angular/cdk'; +@use '../core/style/vendor-prefixes'; +@use '../core/tokens/m2/mdc/list' as tokens-mdc-list; +@use '../core/tokens/token-utils'; + +// Includes the structural styles for the list that were inherited from MDC. +@mixin private-list-inherited-structural-styles { + $tokens: (tokens-mdc-list.$prefix, tokens-mdc-list.get-token-slots()); + + .mdc-list { + margin: 0; + padding: 8px 0; + list-style-type: none; + + &:focus { + outline: none; + } + } + + .mdc-list-item { + display: flex; + position: relative; + justify-content: flex-start; + overflow: hidden; + padding: 0; + align-items: stretch; + cursor: pointer; + padding-left: 16px; + padding-right: 16px; + + @include token-utils.use-tokens($tokens...) { + @include token-utils.create-token-slot(background-color, list-item-container-color); + @include token-utils.create-token-slot(border-radius, list-item-container-shape); + + &.mdc-list-item--selected { + @include token-utils.create-token-slot(background-color, + list-item-selected-container-color); + } + } + + &:focus { + outline: 0; + } + + &.mdc-list-item--disabled { + cursor: auto; + } + + &.mdc-list-item--with-one-line { + @include token-utils.use-tokens($tokens...) { + @include token-utils.create-token-slot(height, list-item-one-line-container-height); + } + + .mdc-list-item__start { + align-self: center; + margin-top: 0; + } + + .mdc-list-item__end { + align-self: center; + margin-top: 0; + } + } + + &.mdc-list-item--with-two-lines { + @include token-utils.use-tokens($tokens...) { + @include token-utils.create-token-slot(height, list-item-two-line-container-height); + } + + .mdc-list-item__start { + align-self: flex-start; + margin-top: 16px; + } + + .mdc-list-item__end { + align-self: center; + margin-top: 0; + } + } + + &.mdc-list-item--with-three-lines { + @include token-utils.use-tokens($tokens...) { + @include token-utils.create-token-slot(height, list-item-three-line-container-height); + } + + .mdc-list-item__start { + align-self: flex-start; + margin-top: 16px; + } + + .mdc-list-item__end { + align-self: flex-start; + margin-top: 16px; + } + } + + &.mdc-list-item--selected::before, + &.mdc-list-item--selected:focus::before, + &:not(.mdc-list-item--selected):focus::before { + position: absolute; + box-sizing: border-box; + width: 100%; + height: 100%; + top: 0; + left: 0; + border: 1px solid transparent; + border-radius: inherit; + content: ''; + pointer-events: none; + + @include cdk.high-contrast(active, off) { + border-color: CanvasText; + } + } + + &.mdc-list-item--selected:focus::before, + &.mdc-list-item--selected::before { + border-width: 3px; + border-style: double; + } + } + + a.mdc-list-item { + color: inherit; + text-decoration: none; + } + + .mdc-list-item__start { + fill: currentColor; + flex-shrink: 0; + pointer-events: none; + + @include token-utils.use-tokens($tokens...) { + .mdc-list-item--with-leading-icon & { + @include token-utils.create-token-slot(color, list-item-leading-icon-color); + @include token-utils.create-token-slot(width, list-item-leading-icon-size); + @include token-utils.create-token-slot(height, list-item-leading-icon-size); + margin-left: 16px; + margin-right: 32px; + } + + [dir='rtl'] .mdc-list-item--with-leading-icon & { + margin-left: 32px; + margin-right: 16px; + } + + .mdc-list-item--with-leading-icon:hover & { + @include token-utils.create-token-slot(color, list-item-hover-leading-icon-color); + } + + // This is the same in RTL, but we need the specificity. + .mdc-list-item--with-leading-avatar & { + @include token-utils.create-token-slot(width, list-item-leading-avatar-size); + @include token-utils.create-token-slot(height, list-item-leading-avatar-size); + margin-left: 16px; + margin-right: 16px; + border-radius: 50%; + } + + .mdc-list-item--with-leading-avatar &, + [dir='rtl'] .mdc-list-item--with-leading-avatar & { + margin-left: 16px; + margin-right: 16px; + border-radius: 50%; + } + } + } + + .mdc-list-item__end { + flex-shrink: 0; + pointer-events: none; + + @include token-utils.use-tokens($tokens...) { + .mdc-list-item--with-trailing-meta & { + @include token-utils.create-token-slot(font-family, + list-item-trailing-supporting-text-font); + @include token-utils.create-token-slot(line-height, + list-item-trailing-supporting-text-line-height); + @include token-utils.create-token-slot(font-size, + list-item-trailing-supporting-text-size); + @include token-utils.create-token-slot(font-weight, + list-item-trailing-supporting-text-weight); + @include token-utils.create-token-slot(letter-spacing, + list-item-trailing-supporting-text-tracking); + } + + .mdc-list-item--with-trailing-icon & { + @include token-utils.create-token-slot(color, list-item-trailing-icon-color); + @include token-utils.create-token-slot(width, list-item-trailing-icon-size); + @include token-utils.create-token-slot(height, list-item-trailing-icon-size); + } + + .mdc-list-item--with-trailing-icon:hover & { + @include token-utils.create-token-slot(color, list-item-hover-trailing-icon-color); + } + + // For some reason this has an increased specificity just for the `color`. + // Keeping it in place for now to reduce the amount of screenshot diffs. + .mdc-list-item.mdc-list-item--with-trailing-meta & { + @include token-utils.create-token-slot(color, list-item-trailing-supporting-text-color); + } + + .mdc-list-item--selected.mdc-list-item--with-trailing-icon & { + @include token-utils.create-token-slot(color, list-item-selected-trailing-icon-color); + } + } + } + + .mdc-list-item__content { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + align-self: center; + flex: 1; + pointer-events: none; + + .mdc-list-item--with-two-lines &, + .mdc-list-item--with-three-lines & { + align-self: stretch; + } + } + + .mdc-list-item__primary-text { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + + @include token-utils.use-tokens($tokens...) { + @include token-utils.create-token-slot(color, list-item-label-text-color); + @include token-utils.create-token-slot(font-family, list-item-label-text-font); + @include token-utils.create-token-slot(line-height, list-item-label-text-line-height); + @include token-utils.create-token-slot(font-size, list-item-label-text-size); + @include token-utils.create-token-slot(font-weight, list-item-label-text-weight); + @include token-utils.create-token-slot(letter-spacing, list-item-label-text-tracking); + + .mdc-list-item:hover & { + @include token-utils.create-token-slot(color, list-item-hover-label-text-color); + } + + .mdc-list-item:focus & { + @include token-utils.create-token-slot(color, list-item-focus-label-text-color); + } + } + + .mdc-list-item--with-two-lines &, + .mdc-list-item--with-three-lines & { + display: block; + margin-top: 0; + line-height: normal; + margin-bottom: -20px; + } + + .mdc-list-item--with-two-lines &::before, + .mdc-list-item--with-three-lines &::before { + display: inline-block; + width: 0; + height: 28px; + content: ''; + vertical-align: 0; + } + + .mdc-list-item--with-two-lines &::after, + .mdc-list-item--with-three-lines &::after { + display: inline-block; + width: 0; + height: 20px; + content: ''; + vertical-align: -20px; + } + } + + .mdc-list-item__secondary-text { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + display: block; + margin-top: 0; + + @include token-utils.use-tokens($tokens...) { + @include token-utils.create-token-slot(color, list-item-supporting-text-color); + @include token-utils.create-token-slot(font-family, list-item-supporting-text-font); + @include token-utils.create-token-slot(line-height, list-item-supporting-text-line-height); + @include token-utils.create-token-slot(font-size, list-item-supporting-text-size); + @include token-utils.create-token-slot(font-weight, list-item-supporting-text-weight); + @include token-utils.create-token-slot(letter-spacing, list-item-supporting-text-tracking); + } + + &::before { + display: inline-block; + width: 0; + height: 20px; + content: ''; + vertical-align: 0; + } + + .mdc-list-item--with-three-lines & { + white-space: normal; + line-height: 20px; + } + + .mdc-list-item--with-overline & { + white-space: nowrap; + line-height: auto; + } + } + + .mdc-list-item--with-leading-radio, + .mdc-list-item--with-leading-checkbox, + .mdc-list-item--with-leading-icon, + .mdc-list-item--with-leading-avatar { + &.mdc-list-item { + padding-left: 0; + padding-right: 16px; + + [dir='rtl'] & { + padding-left: 16px; + padding-right: 0; + } + } + + &.mdc-list-item--with-two-lines { + .mdc-list-item__primary-text { + display: block; + margin-top: 0; + line-height: normal; + margin-bottom: -20px; + + // This was used by MDC to set the text baseline. We should figure out a way to + // remove it, because it can introduce unnecessary whitespace at the beginning + // of the element. + &::before { + display: inline-block; + width: 0; + height: 32px; + content: ''; + vertical-align: 0; + } + + &::after { + display: inline-block; + width: 0; + height: 20px; + content: ''; + vertical-align: -20px; + } + } + + &.mdc-list-item--with-trailing-meta { + .mdc-list-item__end { + display: block; + margin-top: 0; + line-height: normal; + + &::before { + display: inline-block; + width: 0; + height: 32px; + content: ''; + vertical-align: 0; + } + } + } + } + } + + .mdc-list-item--with-trailing-icon { + &.mdc-list-item { + // This is the same in RTL, but we need the specificity. + &, [dir='rtl'] & { + padding-left: 0; + padding-right: 0; + } + } + + .mdc-list-item__end { + margin-left: 16px; + margin-right: 16px; + } + } + + .mdc-list-item--with-trailing-meta { + &.mdc-list-item { + padding-left: 16px; + padding-right: 0; + + [dir='rtl'] & { + padding-left: 0; + padding-right: 16px; + } + } + + .mdc-list-item__end { + @include vendor-prefixes.user-select(none); + margin-left: 28px; + margin-right: 16px; + + [dir='rtl'] & { + margin-left: 16px; + margin-right: 28px; + } + } + + &.mdc-list-item--with-three-lines .mdc-list-item__end, + &.mdc-list-item--with-two-lines .mdc-list-item__end { + display: block; + line-height: normal; + align-self: flex-start; + margin-top: 0; + + &::before { + display: inline-block; + width: 0; + height: 28px; + content: ''; + vertical-align: 0; + } + } + } + + .mdc-list-item--with-leading-radio, + .mdc-list-item--with-leading-checkbox { + .mdc-list-item__start { + margin-left: 8px; + margin-right: 24px; + + [dir='rtl'] & { + margin-left: 24px; + margin-right: 8px; + } + } + + &.mdc-list-item--with-two-lines { + .mdc-list-item__start { + align-self: flex-start; + margin-top: 8px; + } + } + } + + .mdc-list-item--with-trailing-radio, + .mdc-list-item--with-trailing-checkbox { + &.mdc-list-item { + padding-left: 16px; + padding-right: 0; + + [dir='rtl'] & { + padding-left: 0; + padding-right: 16px; + } + } + + &.mdc-list-item--with-leading-icon, + &.mdc-list-item--with-leading-avatar { + padding-left: 0; + + [dir='rtl'] & { + padding-right: 0; + } + } + + .mdc-list-item__end { + margin-left: 24px; + margin-right: 8px; + + [dir='rtl'] & { + margin-left: 8px; + margin-right: 24px; + } + } + + &.mdc-list-item--with-three-lines .mdc-list-item__end { + align-self: flex-start; + margin-top: 8px; + } + } + + .mdc-list-group__subheader { + margin: 0.75rem 16px; + } + + .mdc-list-item--disabled { + .mdc-list-item__start, + .mdc-list-item__content, + .mdc-list-item__end { + opacity: 1; + } + + .mdc-list-item__primary-text, + .mdc-list-item__secondary-text { + @include token-utils.use-tokens($tokens...) { + @include token-utils.create-token-slot(opacity, list-item-disabled-label-text-opacity); + } + } + + &.mdc-list-item--with-leading-icon .mdc-list-item__start { + @include token-utils.use-tokens($tokens...) { + @include token-utils.create-token-slot(color, list-item-disabled-leading-icon-color); + @include token-utils.create-token-slot(opacity, list-item-disabled-leading-icon-opacity); + } + } + + &.mdc-list-item--with-trailing-icon .mdc-list-item__end { + @include token-utils.use-tokens($tokens...) { + @include token-utils.create-token-slot(color, list-item-disabled-trailing-icon-color); + @include token-utils.create-token-slot(opacity, list-item-disabled-trailing-icon-opacity); + } + } + } + + .mat-mdc-list-item.mat-mdc-list-item-both-leading-and-trailing { + &, [dir='rtl'] & { + padding-left: 0; + padding-right: 0; + } + } +} diff --git a/src/material/list/_list-item-hcm-indicator.scss b/src/material/list/_list-item-hcm-indicator.scss index 488064bb58a2..90e80eb13846 100644 --- a/src/material/list/_list-item-hcm-indicator.scss +++ b/src/material/list/_list-item-hcm-indicator.scss @@ -1,5 +1,4 @@ @use '@angular/cdk'; -@use '@material/list/evolution-variables' as mdc-list-variables; // Renders a circle indicator when Windows Hich Constrast mode (HCM) is enabled. In some // situations, such as a selected option, the list item communicates the selected state by changing @@ -12,7 +11,7 @@ content: ''; position: absolute; top: 50%; - right: mdc-list-variables.$side-padding; + right: 16px; transform: translateY(-50%); width: $size; height: 0; @@ -23,7 +22,7 @@ [dir='rtl'] { &::after { right: auto; - left: mdc-list-variables.$side-padding; + left: 16px; } } } diff --git a/src/material/list/_list-option-trailing-avatar-compat.scss b/src/material/list/_list-option-trailing-avatar-compat.scss deleted file mode 100644 index 826c84284ce9..000000000000 --- a/src/material/list/_list-option-trailing-avatar-compat.scss +++ /dev/null @@ -1,59 +0,0 @@ -@use '@material/feature-targeting/feature-targeting'; -@use '@material/list/evolution-mixins' as mdc-list; -@use '../core/mdc-helpers/mdc-helpers'; - -// For compatibility with the non-MDC selection list, we support avatars that are -// shown at the end of the list option. This is not supported by the MDC list as the -// spec only defines avatars at the beginning of a list item. For selection list options, -// we support changing the checkbox position to `before`. This results in the avatar from -// the list start being moved to the end. Similar to MDC's `--trailing-icon` class, we -// implement a `--trailing-avatar` class that is based on the original `--leading-avatar` -// implementation. See: https://github.com/material-components/material-components-web/blob/3f342c3f4715fd3587f327ce4ea6b5dd314c5c55/packages/mdc-list/_evolution-mixins.scss#L198-L217 - -@mixin core-styles($query) { - $feat-structure: feature-targeting.create-target($query, structure); - - @include mdc-helpers.disable-mdc-fallback-declarations { - .mat-mdc-list-option-with-trailing-avatar { - @include mdc-list.item-end-spacing(16px, $query: $query); - @include mdc-list.item-end-size(40px, $query: $query); - - &.mdc-list-item--with-two-lines { - $top: 32px; - $bottom: 20px; - - .mdc-list-item__primary-text { - display: block; - margin-top: 0; - line-height: normal; - margin-bottom: $bottom * -1; - - // This was used by MDC to set the text baseline. We should figure out a way to - // remove it, because it can introduce unnecessary whitespace at the beginning - // of the element. - &::before { - display: inline-block; - width: 0; - height: $top; - content: ''; - vertical-align: 0; - } - - &::after { - display: inline-block; - width: 0; - height: $bottom; - content: ''; - vertical-align: $bottom * -1; - } - } - } - - .mdc-list-item__end { - @include feature-targeting.targets($feat-structure) { - border-radius: 50%; - } - } - } - } -} diff --git a/src/material/list/_list-theme.scss b/src/material/list/_list-theme.scss index af48cc9c0ee9..daa96261b4ab 100644 --- a/src/material/list/_list-theme.scss +++ b/src/material/list/_list-theme.scss @@ -1,6 +1,4 @@ @use 'sass:map'; -@use '@material/list/evolution-mixins'; -@use '@material/list/list-theme' as mdc-list-theme; @use '../core/style/sass-utils'; @use '../core/theming/theming'; @@ -20,7 +18,8 @@ } @else { @include sass-utils.current-selector-or-root() { - @include mdc-list-theme.theme(tokens-mdc-list.get-unthemable-tokens()); + @include token-utils.create-token-values( + tokens-mdc-list.$prefix, tokens-mdc-list.get-unthemable-tokens()); @include token-utils.create-token-values( tokens-mat-list.$prefix, tokens-mat-list.get-unthemable-tokens()); } @@ -32,11 +31,9 @@ @include _theme-from-tokens(inspection.get-theme-tokens($theme, color)); } @else { - $mdc-list-color-tokens: tokens-mdc-list.get-color-tokens($theme); - - // Add values for MDC list tokens. @include sass-utils.current-selector-or-root() { - @include mdc-list-theme.theme($mdc-list-color-tokens); + @include token-utils.create-token-values( + tokens-mdc-list.$prefix, tokens-mdc-list.get-color-tokens($theme)); @include token-utils.create-token-values( tokens-mat-list.$prefix, tokens-mat-list.get-color-tokens($theme)); } @@ -79,8 +76,13 @@ // There is no token for activated color on nav list. // TODO(mmalerba): Add a token to MDC or make a custom one. .mat-mdc-list-base.mat-mdc-list-base { - @include evolution-mixins.list-selected-ink-color( - inspection.get-theme-color($theme, primary)); + .mdc-list-item--selected, + .mdc-list-item--activated { + .mdc-list-item__primary-text, + .mdc-list-item__start { + color: inspection.get-theme-color($theme, primary); + } + } } // TODO(mmalerba): Leaking styles from the old MDC list mixins used in other components can @@ -102,11 +104,10 @@ } @else { $density-scale: inspection.get-theme-density($theme); - $mdc-list-density-tokens: tokens-mdc-list.get-density-tokens($theme); - // Add values for MDC list tokens. @include sass-utils.current-selector-or-root() { - @include mdc-list-theme.theme($mdc-list-density-tokens); + @include token-utils.create-token-values( + tokens-mdc-list.$prefix, tokens-mdc-list.get-density-tokens($theme)); @include token-utils.create-token-values( tokens-mat-list.$prefix, tokens-mat-list.get-density-tokens($theme)); } @@ -155,11 +156,9 @@ @include _theme-from-tokens(inspection.get-theme-tokens($theme, typography)); } @else { - $mdc-list-typography-tokens: tokens-mdc-list.get-typography-tokens($theme); - - // Add values for MDC list tokens. @include sass-utils.current-selector-or-root() { - @include mdc-list-theme.theme($mdc-list-typography-tokens); + @include token-utils.create-token-values( + tokens-mdc-list.$prefix, tokens-mdc-list.get-typography-tokens($theme)); @include token-utils.create-token-values( tokens-mat-list.$prefix, tokens-mat-list.get-typography-tokens($theme)); } @@ -204,8 +203,8 @@ @mixin _theme-from-tokens($tokens) { @include validation.selector-defined( 'Calls to Angular Material theme mixins with an M3 theme must be wrapped in a selector'); - @include mdc-list-theme.theme(token-utils.get-tokens-for($tokens, tokens-mdc-list.$prefix)); - + $mdc-list-tokens: token-utils.get-tokens-for($tokens, tokens-mdc-list.$prefix); $mat-list-tokens: token-utils.get-tokens-for($tokens, tokens-mat-list.$prefix); + @include token-utils.create-token-values(tokens-mdc-list.$prefix, $mdc-list-tokens); @include token-utils.create-token-values(tokens-mat-list.$prefix, $mat-list-tokens); } diff --git a/src/material/list/list-option.scss b/src/material/list/list-option.scss index f4ea5f27bf75..80f5b6fe7ac8 100644 --- a/src/material/list/list-option.scss +++ b/src/material/list/list-option.scss @@ -1,12 +1,63 @@ @use '../checkbox/checkbox-common'; @use '../radio/radio-common'; -@use '../core/mdc-helpers/mdc-helpers'; -@use './list-option-trailing-avatar-compat'; @use './list-item-hcm-indicator'; -// For compatibility with the non-MDC list, we support avatars that are shown at the end -// of the list option. We create a class similar to MDC's `--trailing-icon` one. -@include list-option-trailing-avatar-compat.core-styles($query: mdc-helpers.$mdc-base-styles-query); +// For compatibility with the non-MDC selection list, we support avatars that are +// shown at the end of the list option. This is not supported by the MDC list as the +// spec only defines avatars at the beginning of a list item. For selection list options, +// we support changing the checkbox position to `before`. This results in the avatar from +// the list start being moved to the end. Similar to MDC's `--trailing-icon` class, we +// implement a `--trailing-avatar` class that is based on the original `--leading-avatar` +// implementation. See: https://github.com/material-components/material-components-web/blob/3f342c3f4715fd3587f327ce4ea6b5dd314c5c55/packages/mdc-list/_evolution-mixins.scss#L198-L217 +.mat-mdc-list-option-with-trailing-avatar { + &.mdc-list-item, + [dir='rtl'] &.mdc-list-item { + padding-left: 0; + padding-right: 0; + } + + .mdc-list-item__end { + margin-left: 16px; + margin-right: 16px; + width: 40px; + height: 40px; + } + + &.mdc-list-item--with-two-lines { + $top: 32px; + $bottom: 20px; + + .mdc-list-item__primary-text { + display: block; + margin-top: 0; + line-height: normal; + margin-bottom: $bottom * -1; + + // This was used by MDC to set the text baseline. We should figure out a way to + // remove it, because it can introduce unnecessary whitespace at the beginning + // of the element. + &::before { + display: inline-block; + width: 0; + height: $top; + content: ''; + vertical-align: 0; + } + + &::after { + display: inline-block; + width: 0; + height: $bottom; + content: ''; + vertical-align: $bottom * -1; + } + } + } + + .mdc-list-item__end { + border-radius: 50%; + } +} .mat-mdc-list-option { // We can't use the MDC checkbox here directly, because this checkbox is purely diff --git a/src/material/list/list-option.ts b/src/material/list/list-option.ts index 8c4c7049fb66..7e900e6e557b 100644 --- a/src/material/list/list-option.ts +++ b/src/material/list/list-option.ts @@ -83,6 +83,10 @@ export interface SelectionList extends MatListBase { '[class.mdc-list-item--with-trailing-checkbox]': '_hasCheckboxAt("after")', '[class.mdc-list-item--with-leading-radio]': '_hasRadioAt("before")', '[class.mdc-list-item--with-trailing-radio]': '_hasRadioAt("after")', + + // Utility class that makes it easier to target the case where there's both a leading + // and a trailing icon. Avoids having to write out all the combinations. + '[class.mat-mdc-list-item-both-leading-and-trailing]': '_hasBothLeadingAndTrailing()', '[class.mat-accent]': 'color !== "primary" && color !== "warn"', '[class.mat-warn]': 'color === "warn"', '[class._mat-animation-noopable]': '_noopAnimations', @@ -337,4 +341,18 @@ export class MatListOption extends MatListItemBase implements ListOption, OnInit _setTabindex(value: number) { this._hostElement.setAttribute('tabindex', value + ''); } + + protected _hasBothLeadingAndTrailing(): boolean { + const hasLeading = + this._hasProjected('avatars', 'before') || + this._hasProjected('icons', 'before') || + this._hasCheckboxAt('before') || + this._hasRadioAt('before'); + const hasTrailing = + this._hasProjected('icons', 'after') || + this._hasProjected('avatars', 'after') || + this._hasCheckboxAt('after') || + this._hasRadioAt('after'); + return hasLeading && hasTrailing; + } } diff --git a/src/material/list/list.scss b/src/material/list/list.scss index 61e246175f42..fbbf4c4d5896 100644 --- a/src/material/list/list.scss +++ b/src/material/list/list.scss @@ -1,87 +1,61 @@ -@use 'sass:map'; -@use '@material/list/list' as mdc-list; -@use '@material/list/list-theme' as mdc-list-theme; -@use '@material/theme/custom-properties' as mdc-custom-properties; @use '../core/style/layout-common'; -@use '../core/tokens/m2/mat/list' as m2-mat-list; -@use '../core/tokens/m2/mdc/list' as m2-mdc-list; +@use '../core/tokens/m2/mat/list' as tokens-mat-list; +@use '../core/tokens/m2/mdc/list' as tokens-mdc-list; @use '../core/tokens/token-utils'; @use './list-item-hcm-indicator'; +@use './list-inherited-structure'; -// 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-list-token-slots: m2-mdc-list.get-token-slots(); - - // Add the MDC list static styles. - @include mdc-list.static-styles(); - - // Add the official slots for the MDC list. - @include mdc-list-theme.theme-styles(map.merge($mdc-list-token-slots, ( - // We structure the avatar differently from how MDC expects, so we add these slots ourselves. - list-item-leading-avatar-shape: null, - list-item-leading-avatar-color: null, - // We add this slot ourselves with more specificity, so we don't need MDC to emit it. - list-item-disabled-label-text-color: null, - // We don't use MDC's state layers, so we add these slots ourselves instead. - list-item-hover-state-layer-color: null, - list-item-hover-state-layer-opacity: null, - list-item-focus-state-layer-color: null, - list-item-focus-state-layer-opacity: null, - list-item-disabled-state-layer-color: null, - list-item-disabled-state-layer-opacity: null, - ))); - - // Add additional slots for the MDC list tokens, needed in Angular Material. - @include token-utils.use-tokens(m2-mdc-list.$prefix, $mdc-list-token-slots) { - // MDC allows focus and hover colors to take precedence over disabled color. We add the disabled - // color here with higher specificity so that the disabled color takes precedence. - // TODO(mmalerba): Dicuss with MDC whether to change this in their code. - .mdc-list-item.mdc-list-item--disabled .mdc-list-item__primary-text { - @include token-utils.create-token-slot(color, list-item-disabled-label-text-color); - } +@include list-inherited-structure.private-list-inherited-structural-styles; - // We don't use MDC's state layer since it's tied in with their ripple. Instead we emit slots - // for our own state layer. - // TODO(mmalerba): Consider using MDC's ripple & state layer. - .mdc-list-item:hover::before { - @include token-utils.create-token-slot(background-color, list-item-hover-state-layer-color); - @include token-utils.create-token-slot(opacity, list-item-hover-state-layer-opacity); - } - .mdc-list-item.mdc-list-item--disabled::before { - @include token-utils.create-token-slot( - background-color, list-item-disabled-state-layer-color); - @include token-utils.create-token-slot(opacity, list-item-disabled-state-layer-opacity); - } - .mdc-list-item:focus::before { - @include token-utils.create-token-slot(background-color, list-item-focus-state-layer-color); - @include token-utils.create-token-slot(opacity, list-item-focus-state-layer-opacity); - } +// Add additional slots for the MDC list tokens, needed in Angular Material. +@include token-utils.use-tokens(tokens-mdc-list.$prefix, tokens-mdc-list.get-token-slots()) { + // MDC allows focus and hover colors to take precedence over disabled color. We add the disabled + // color here with higher specificity so that the disabled color takes precedence. + // TODO(mmalerba): Dicuss with MDC whether to change this in their code. + .mdc-list-item.mdc-list-item--disabled .mdc-list-item__primary-text { + @include token-utils.create-token-slot(color, list-item-disabled-label-text-color); + } - // Apply the disabled opacity to the checkbox/radio indicators. - // TODO(mmalerba): We should probably stop doing this and allow the checkbox/radio to decide - // what their disabled state looks like. This is done for now to avoid screenshot diffs. - .mdc-list-item--disabled { - .mdc-radio, - .mdc-checkbox { - @include token-utils.create-token-slot(opacity, list-item-disabled-label-text-opacity); - } - } + // We don't use MDC's state layer since it's tied in with their ripple. Instead we emit slots + // for our own state layer. + // TODO(mmalerba): Consider using MDC's ripple & state layer. + .mdc-list-item:hover::before { + @include token-utils.create-token-slot(background-color, list-item-hover-state-layer-color); + @include token-utils.create-token-slot(opacity, list-item-hover-state-layer-opacity); + } + .mdc-list-item.mdc-list-item--disabled::before { + @include token-utils.create-token-slot(background-color, list-item-disabled-state-layer-color); + @include token-utils.create-token-slot(opacity, list-item-disabled-state-layer-opacity); + } + .mdc-list-item:focus::before { + @include token-utils.create-token-slot(background-color, list-item-focus-state-layer-color); + @include token-utils.create-token-slot(opacity, list-item-focus-state-layer-opacity); + } - // In Angular Material we put the avatar class directly on the .mdc-list-item__start element, - // rather than nested inside it, so we need to emit avatar slots ourselves. - // TODO(mmalerba): We should try to change MDC's recommended DOM or change ours to match their - // recommendation. - .mdc-list-item--with-leading-avatar .mat-mdc-list-item-avatar { - @include token-utils.create-token-slot(border-radius, list-item-leading-avatar-shape); - @include token-utils.create-token-slot(background-color, list-item-leading-avatar-color); + // Apply the disabled opacity to the checkbox/radio indicators. + // TODO(mmalerba): We should probably stop doing this and allow the checkbox/radio to decide + // what their disabled state looks like. This is done for now to avoid screenshot diffs. + .mdc-list-item--disabled { + .mdc-radio, + .mdc-checkbox { + @include token-utils.create-token-slot(opacity, list-item-disabled-label-text-opacity); } + } - // Set font-size of leading icon to same value as its width and height. Ensure icon scales to - // "list-item-leading-icon-size" token. In Angular Material, the icon is on the same element as - // ".mdc-list-item__start", rather than a child of ".mdc-list-item__start". - .mat-mdc-list-item-icon { - @include token-utils.create-token-slot(font-size, list-item-leading-icon-size); - } + // In Angular Material we put the avatar class directly on the .mdc-list-item__start element, + // rather than nested inside it, so we need to emit avatar slots ourselves. + // TODO(mmalerba): We should try to change MDC's recommended DOM or change ours to match their + // recommendation. + .mdc-list-item--with-leading-avatar .mat-mdc-list-item-avatar { + @include token-utils.create-token-slot(border-radius, list-item-leading-avatar-shape); + @include token-utils.create-token-slot(background-color, list-item-leading-avatar-color); + } + + // Set font-size of leading icon to same value as its width and height. Ensure icon scales to + // "list-item-leading-icon-size" token. In Angular Material, the icon is on the same element as + // ".mdc-list-item__start", rather than a child of ".mdc-list-item__start". + .mat-mdc-list-item-icon { + @include token-utils.create-token-slot(font-size, list-item-leading-icon-size); } } @@ -200,7 +174,7 @@ mat-action-list button { } } -@include token-utils.use-tokens(m2-mat-list.$prefix, m2-mat-list.get-token-slots()) { +@include token-utils.use-tokens(tokens-mat-list.$prefix, tokens-mat-list.get-token-slots()) { .mdc-list-item--with-leading-icon .mdc-list-item__start { @include token-utils.create-token-slot(margin-inline-start, list-item-leading-icon-start-space); @include token-utils.create-token-slot(margin-inline-end, list-item-leading-icon-end-space); diff --git a/src/material/list/list.ts b/src/material/list/list.ts index fcfc79bbd97d..fd6662d4a468 100644 --- a/src/material/list/list.ts +++ b/src/material/list/list.ts @@ -59,6 +59,9 @@ export class MatList extends MatListBase {} '[class.mdc-list-item--with-leading-avatar]': '_avatars.length !== 0', '[class.mdc-list-item--with-leading-icon]': '_icons.length !== 0', '[class.mdc-list-item--with-trailing-meta]': '_meta.length !== 0', + // Utility class that makes it easier to target the case where there's both a leading + // and a trailing icon. Avoids having to write out all the combinations. + '[class.mat-mdc-list-item-both-leading-and-trailing]': '_hasBothLeadingAndTrailing()', '[class._mat-animation-noopable]': '_noopAnimations', '[attr.aria-current]': '_getAriaCurrent()', }, @@ -103,4 +106,8 @@ export class MatListItem extends MatListItemBase { _getAriaCurrent(): string | null { return this._hostElement.nodeName === 'A' && this._activated ? 'page' : null; } + + protected _hasBothLeadingAndTrailing(): boolean { + return this._meta.length !== 0 && (this._avatars.length !== 0 || this._icons.length !== 0); + } } diff --git a/tools/public_api_guard/material/list.md b/tools/public_api_guard/material/list.md index 3a35d95306cc..da4aad970fa1 100644 --- a/tools/public_api_guard/material/list.md +++ b/tools/public_api_guard/material/list.md @@ -73,6 +73,8 @@ export class MatListItem extends MatListItemBase { _activated: boolean; _getAriaCurrent(): string | null; // (undocumented) + protected _hasBothLeadingAndTrailing(): boolean; + // (undocumented) _itemText: ElementRef; // (undocumented) _lines: QueryList; @@ -170,6 +172,8 @@ export class MatListOption extends MatListItemBase implements ListOption, OnInit _getTogglePosition(): MatListOptionTogglePosition; // (undocumented) _handleBlur(): void; + // (undocumented) + protected _hasBothLeadingAndTrailing(): boolean; _hasCheckboxAt(position: MatListOptionTogglePosition): boolean; _hasIconsOrAvatarsAt(position: 'before' | 'after'): boolean; _hasProjected(type: 'icons' | 'avatars', position: 'before' | 'after'): boolean; From ca7c090fb948c2f27f13d76e7cb567d0ba870ab9 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 8 Jul 2024 20:10:28 +0000 Subject: [PATCH 22/22] refactor(material/core): simplify internal form field styles (#29389) Reworks the internal form field styles to be smaller and easier to maintain. --- .../internal-form-field.scss | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/material/core/internal-form-field/internal-form-field.scss b/src/material/core/internal-form-field/internal-form-field.scss index cb4f7e701cbb..fb2191d05b34 100644 --- a/src/material/core/internal-form-field/internal-form-field.scss +++ b/src/material/core/internal-form-field/internal-form-field.scss @@ -1,12 +1,40 @@ -@use '@material/form-field/form-field' as mdc-form-field; -@use '@material/theme/custom-properties' as mdc-custom-properties; @use '../style/vendor-prefixes'; -@use '../mdc-helpers/mdc-helpers'; - -@include mdc-custom-properties.configure($emit-fallback-values: false, $emit-fallback-vars: false) { - @include mdc-form-field.static-styles($query: mdc-helpers.$mdc-base-styles-query); -} .mat-internal-form-field { @include vendor-prefixes.smooth-font(); + display: inline-flex; + align-items: center; + vertical-align: middle; + + & > label { + margin-left: 0; + margin-right: auto; + padding-left: 4px; + padding-right: 0; + order: 0; + } + + [dir='rtl'] & > label { + margin-left: auto; + margin-right: 0; + padding-left: 0; + padding-right: 4px; + } +} + +.mdc-form-field--align-end { + & > label { + margin-left: auto; + margin-right: 0; + padding-left: 0; + padding-right: 4px; + order: -1; + } + + [dir='rtl'] .mdc-form-field--align-end & label { + margin-left: 0; + margin-right: auto; + padding-left: 4px; + padding-right: 0; + } }