From 07225d29dae226168dd279dc993b249361910dcc Mon Sep 17 00:00:00 2001 From: naaajii Date: Sat, 12 Oct 2024 04:55:46 +0500 Subject: [PATCH] feat(material/card): support `filled` variant this commit add `filled` variant for material card which provides subtle seperation from background and has less emphasis than elevated or outlined cards fixes #29840 --- src/material/card/_card-theme.scss | 30 +++++++ src/material/card/card.scss | 12 +++ src/material/card/card.ts | 4 +- src/material/core/tokens/m2/_index.scss | 4 +- .../core/tokens/m2/mdc/_filled-card.scss | 81 +++++++++++++++++++ src/material/core/tokens/m3/_index.scss | 4 +- .../core/tokens/m3/mdc/_filled-card.scss | 22 +++++ tools/public_api_guard/material/card.md | 2 +- 8 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 src/material/core/tokens/m2/mdc/_filled-card.scss create mode 100644 src/material/core/tokens/m3/mdc/_filled-card.scss diff --git a/src/material/card/_card-theme.scss b/src/material/card/_card-theme.scss index c8c4a7f59311..4e93fcce3362 100644 --- a/src/material/card/_card-theme.scss +++ b/src/material/card/_card-theme.scss @@ -8,6 +8,7 @@ @use '../core/tokens/m2/mat/card' as tokens-mat-card; @use '../core/tokens/m2/mdc/elevated-card' as tokens-mdc-elevated-card; @use '../core/tokens/m2/mdc/outlined-card' as tokens-mdc-outlined-card; +@use '../core/tokens/m2/mdc/filled-card' as tokens-mdc-filled-card; @mixin base($theme) { @if inspection.get-theme-version($theme) == 1 { @@ -22,6 +23,10 @@ tokens-mdc-outlined-card.$prefix, tokens-mdc-outlined-card.get-unthemable-tokens() ); + @include token-utils.create-token-values( + tokens-mdc-filled-card.$prefix, + tokens-mdc-filled-card.get-unthemable-tokens() + ); @include token-utils.create-token-values( tokens-mat-card.$prefix, tokens-mat-card.get-unthemable-tokens() @@ -43,6 +48,10 @@ tokens-mdc-outlined-card.$prefix, tokens-mdc-outlined-card.get-color-tokens($theme) ); + @include token-utils.create-token-values( + tokens-mdc-filled-card.$prefix, + tokens-mdc-filled-card.get-color-tokens($theme) + ); @include token-utils.create-token-values( tokens-mat-card.$prefix, tokens-mat-card.get-color-tokens($theme) @@ -64,6 +73,10 @@ tokens-mdc-outlined-card.$prefix, tokens-mdc-outlined-card.get-typography-tokens($theme) ); + @include token-utils.create-token-values( + tokens-mdc-filled-card.$prefix, + tokens-mdc-filled-card.get-typography-tokens($theme) + ); @include token-utils.create-token-values( tokens-mat-card.$prefix, tokens-mat-card.get-typography-tokens($theme) @@ -85,6 +98,14 @@ tokens-mdc-outlined-card.$prefix, tokens-mdc-outlined-card.get-density-tokens($theme) ); + @include token-utils.create-token-values( + tokens-mdc-outlined-card.$prefix, + tokens-mdc-outlined-card.get-density-tokens($theme) + ); + @include token-utils.create-token-values( + tokens-mdc-filled-card.$prefix, + tokens-mdc-filled-card.get-density-tokens($theme) + ); @include token-utils.create-token-values( tokens-mat-card.$prefix, tokens-mat-card.get-density-tokens($theme) @@ -109,6 +130,11 @@ namespace: tokens-mdc-outlined-card.$prefix, tokens: tokens-mdc-outlined-card.get-token-slots(), prefix: 'outlined-', + ), + ( + namespace: tokens-mdc-filled-card.$prefix, + tokens: tokens-mdc-filled-card.get-token-slots(), + prefix: 'outlined-', ) ); } @@ -145,6 +171,10 @@ tokens-mdc-outlined-card.$prefix, map.get($tokens, tokens-mdc-outlined-card.$prefix) ); + @include token-utils.create-token-values( + tokens-mdc-filled-card.$prefix, + map.get($tokens, tokens-mdc-filled-card.$prefix) + ); @include token-utils.create-token-values( tokens-mat-card.$prefix, map.get($tokens, tokens-mat-card.$prefix) diff --git a/src/material/card/card.scss b/src/material/card/card.scss index 21ae1ee38b42..032373d74a33 100644 --- a/src/material/card/card.scss +++ b/src/material/card/card.scss @@ -2,6 +2,7 @@ @use '../core/tokens/m2/mat/card' as tokens-mat-card; @use '../core/tokens/m2/mdc/elevated-card' as tokens-mdc-elevated-card; @use '../core/tokens/m2/mdc/outlined-card' as tokens-mdc-outlined-card; +@use '../core/tokens/m2/mdc/filled-card' as tokens-mdc-filled-card; // Size of the `mat-card-header` region custom to Angular Material. $mat-card-header-size: 40px !default; @@ -68,6 +69,17 @@ $mat-card-default-padding: 16px !default; } } +.mat-mdc-card-filled { + @include token-utils.use-tokens( + tokens-mdc-filled-card.$prefix, + tokens-mdc-filled-card.get-token-slots() + ) { + @include token-utils.create-token-slot(background-color, container-color); + @include token-utils.create-token-slot(border-radius, container-shape); + @include token-utils.create-token-slot(box-shadow, container-elevation); + } +} + .mdc-card__media { position: relative; box-sizing: border-box; diff --git a/src/material/card/card.ts b/src/material/card/card.ts index 2a9b89fb4765..7f03f437a333 100644 --- a/src/material/card/card.ts +++ b/src/material/card/card.ts @@ -16,7 +16,7 @@ import { inject, } from '@angular/core'; -export type MatCardAppearance = 'outlined' | 'raised'; +export type MatCardAppearance = 'outlined' | 'raised' | 'filled'; /** Object that can be used to configure the default options for the card module. */ export interface MatCardConfig { @@ -41,6 +41,8 @@ export const MAT_CARD_CONFIG = new InjectionToken('MAT_CARD_CONFI 'class': 'mat-mdc-card mdc-card', '[class.mat-mdc-card-outlined]': 'appearance === "outlined"', '[class.mdc-card--outlined]': 'appearance === "outlined"', + '[class.mat-mdc-card-filled]': 'appearance === "filled"', + '[class.mdc-card--filled]': 'appearance === "filled"', }, exportAs: 'matCard', encapsulation: ViewEncapsulation.None, diff --git a/src/material/core/tokens/m2/_index.scss b/src/material/core/tokens/m2/_index.scss index 85c8c7195b50..9875b56cd44c 100644 --- a/src/material/core/tokens/m2/_index.scss +++ b/src/material/core/tokens/m2/_index.scss @@ -70,6 +70,7 @@ @use './mdc/switch' as tokens-mdc-switch; @use './mdc/secondary-navigation-tab' as tokens-mdc-secondary-navigation-tab; @use './mdc/tab-indicator' as tokens-mdc-tab-indicator; +@use './mdc/filled-card' as tokens-mdc-filled-card; @use '../../theming/inspection'; /// Gets the tokens for the given theme, m2 tokens module, and theming system. @@ -182,6 +183,7 @@ _get-tokens-for-module($theme, tokens-mdc-switch), _get-tokens-for-module($theme, tokens-mdc-tab-indicator), _get-tokens-for-module($theme, tokens-mdc-secondary-navigation-tab), - _get-tokens-for-module($theme, tokens-mdc-text-button) + _get-tokens-for-module($theme, tokens-mdc-text-button), + _get-tokens-for-module($theme, tokens-mdc-filled-card), ); } diff --git a/src/material/core/tokens/m2/mdc/_filled-card.scss b/src/material/core/tokens/m2/mdc/_filled-card.scss new file mode 100644 index 000000000000..61579153466f --- /dev/null +++ b/src/material/core/tokens/m2/mdc/_filled-card.scss @@ -0,0 +1,81 @@ +@use '../../../style/elevation'; +@use '../../../theming/inspection'; +@use '../../../style/sass-utils'; +@use '../../token-definition'; + +// The prefix used to generate the fully qualified name for tokens in this file. +$prefix: (mdc, filled-card); + +// Tokens that can't be configured through Angular Material's current theming API, +// but may be in a future version of the theming API. +// +// Tokens that are available in MDC, but not used in Angular Material should be mapped to `null`. +// `null` indicates that we are intentionally choosing not to emit a slot or value for the token in +// our CSS. +@function get-unthemable-tokens() { + @return ( + // The border-radius of the card. + container-shape: 4px, + // ============================================================================================= + // = TOKENS NOT USED IN ANGULAR MATERIAL = + // ============================================================================================= + // Angular Material's card is not an interactive element, and therefore does not support states. + disabled-container-elevation: null, + disabled-outline-color: null, + disabled-outline-opacity: null, + dragged-container-elevation: null, + dragged-outline-color: null, + dragged-state-layer-color: null, + dragged-state-layer-opacity: null, + focus-container-elevation: null, + focus-outline-color: null, + focus-state-layer-color: null, + focus-state-layer-opacity: null, + hover-container-elevation: null, + hover-outline-color: null, + hover-state-layer-color: null, + hover-state-layer-opacity: null, + pressed-container-elevation: null, + pressed-outline-color: null, + pressed-state-layer-color: null, + pressed-state-layer-opacity: null, + container-shadow-color: null, + // Angular Material does not currently support surface tint. + container-surface-tint-layer-color: null, + // MDC does not seem to use these tokens. + icon-color: null, + icon-size: null, + ); +} + +// Tokens that can be configured through Angular Material's color theming API. +@function get-color-tokens($theme) { + $elevation: inspection.get-theme-color($theme, foreground, elevation); + + @return ( + // The background color of the card. + container-color: inspection.get-theme-color($theme, background, card), + container-elevation: elevation.get-box-shadow(0), + ); +} + +// Tokens that can be configured through Angular Material's typography theming API. +@function get-typography-tokens($theme) { + @return (); +} + +// Tokens that can be configured through Angular Material's density theming API. +@function get-density-tokens($theme) { + @return (); +} + +// Combines the tokens generated by the above functions into a single map with placeholder values. +// This is used to create token slots. +@function get-token-slots() { + @return sass-utils.deep-merge-all( + get-unthemable-tokens(), + get-color-tokens(token-definition.$placeholder-color-config), + get-typography-tokens(token-definition.$placeholder-typography-config), + get-density-tokens(token-definition.$placeholder-density-config) + ); +} diff --git a/src/material/core/tokens/m3/_index.scss b/src/material/core/tokens/m3/_index.scss index 4d85b20f002a..4763e83b9467 100644 --- a/src/material/core/tokens/m3/_index.scss +++ b/src/material/core/tokens/m3/_index.scss @@ -68,6 +68,7 @@ @use './mdc/switch' as tokens-mdc-switch; @use './mdc/secondary-navigation-tab' as tokens-mdc-secondary-navigation-tab; @use './mdc/tab-indicator' as tokens-mdc-tab-indicator; +@use './mdc/filled-card' as tokens-mdc-filled-card; $_module-names: ( // Custom tokens @@ -140,7 +141,8 @@ $_module-names: ( tokens-mdc-snack-bar, tokens-mdc-switch, tokens-mdc-secondary-navigation-tab, - tokens-mdc-tab-indicator + tokens-mdc-tab-indicator, + tokens-mdc-filled-card, ); /// Gets the full set of M3 tokens for the given theme object. diff --git a/src/material/core/tokens/m3/mdc/_filled-card.scss b/src/material/core/tokens/m3/mdc/_filled-card.scss new file mode 100644 index 000000000000..888b06d69d28 --- /dev/null +++ b/src/material/core/tokens/m3/mdc/_filled-card.scss @@ -0,0 +1,22 @@ +@use 'sass:map'; +@use '../../../style/elevation'; +@use '../../token-definition'; + +// The prefix used to generate the fully qualified name for tokens in this file. +$prefix: (mdc, filled-card); + +/// Generates the tokens for MDC filled-card +/// @param {Map} $systems The MDC system tokens +/// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values +/// @param {Map} $token-slots Possible token slots +/// @return {Map} A set of tokens for the MDC filled-card +@function get-tokens($systems, $exclude-hardcoded, $token-slots) { + $tokens: token-definition.get-mdc-tokens('filled-card', $systems, $exclude-hardcoded); + $elevation: map.get($tokens, container-elevation); + + @if ($elevation != null) { + $tokens: map.set($tokens, container-elevation, elevation.get-box-shadow($elevation)); + } + + @return token-definition.namespace-tokens($prefix, $tokens, $token-slots); +} diff --git a/tools/public_api_guard/material/card.md b/tools/public_api_guard/material/card.md index 576c88722df2..4938d3c17587 100644 --- a/tools/public_api_guard/material/card.md +++ b/tools/public_api_guard/material/card.md @@ -33,7 +33,7 @@ export class MatCardActions { } // @public (undocumented) -export type MatCardAppearance = 'outlined' | 'raised'; +export type MatCardAppearance = 'outlined' | 'raised' | 'filled'; // @public export class MatCardAvatar {