diff --git a/goldens/material/card/index.api.md b/goldens/material/card/index.api.md index 345ac1081a53..5bcc72ee089b 100644 --- a/goldens/material/card/index.api.md +++ b/goldens/material/card/index.api.md @@ -32,7 +32,7 @@ export class MatCardActions { } // @public (undocumented) -export type MatCardAppearance = 'outlined' | 'raised'; +export type MatCardAppearance = 'outlined' | 'raised' | 'filled'; // @public export class MatCardAvatar { diff --git a/src/components-examples/material/card/card-overview/card-overview-example.html b/src/components-examples/material/card/card-overview/card-overview-example.html index 6805d4fb8214..e91eecb2db93 100644 --- a/src/components-examples/material/card/card-overview/card-overview-example.html +++ b/src/components-examples/material/card/card-overview/card-overview-example.html @@ -1,3 +1,11 @@ + + Elevated + + - Simple card + Outlined + + + Filled + \ No newline at end of file diff --git a/src/dev-app/card/BUILD.bazel b/src/dev-app/card/BUILD.bazel index 8c8b14bf433c..5d420cda0d55 100644 --- a/src/dev-app/card/BUILD.bazel +++ b/src/dev-app/card/BUILD.bazel @@ -15,7 +15,7 @@ ng_project( "//:node_modules/@angular/forms", "//src/material/button", "//src/material/card", - "//src/material/checkbox", + "//src/material/radio", ], ) diff --git a/src/dev-app/card/card-demo.html b/src/dev-app/card/card-demo.html index a09abf454fc9..d4de4196bf48 100644 --- a/src/dev-app/card/card-demo.html +++ b/src/dev-app/card/card-demo.html @@ -1,5 +1,9 @@
- Use outlined cards + + Raised + Outlined + Filled + diff --git a/src/dev-app/card/card-demo.scss b/src/dev-app/card/card-demo.scss index 6038ba0385ae..945be0598362 100644 --- a/src/dev-app/card/card-demo.scss +++ b/src/dev-app/card/card-demo.scss @@ -16,3 +16,7 @@ text-transform: uppercase; } } + +mat-radio-group { + margin-bottom: 10px; +} diff --git a/src/dev-app/card/card-demo.ts b/src/dev-app/card/card-demo.ts index 4db09dd68157..7202b53a81e1 100644 --- a/src/dev-app/card/card-demo.ts +++ b/src/dev-app/card/card-demo.ts @@ -10,14 +10,14 @@ import {ChangeDetectionStrategy, Component, ViewEncapsulation} from '@angular/co import {FormsModule} from '@angular/forms'; import {MatButtonModule} from '@angular/material/button'; import {MatCardAppearance, MatCardModule} from '@angular/material/card'; -import {MatCheckboxModule} from '@angular/material/checkbox'; +import {MatRadioModule} from '@angular/material/radio'; @Component({ selector: 'card-demo', templateUrl: 'card-demo.html', styleUrl: 'card-demo.css', encapsulation: ViewEncapsulation.None, - imports: [MatCardModule, MatButtonModule, MatCheckboxModule, FormsModule], + imports: [MatCardModule, MatButtonModule, MatRadioModule, FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, }) export class CardDemo { @@ -28,7 +28,4 @@ export class CardDemo { As of some one gently rapping, rapping at my chamber door. “’Tis some visitor,” I muttered, “tapping at my chamber door— Only this and nothing more.”`; - toggleAppearance() { - this.appearance = this.appearance == 'raised' ? 'outlined' : 'raised'; - } } diff --git a/src/material/card/_card-theme.scss b/src/material/card/_card-theme.scss index 41a8338648a1..3acc0358c741 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/mat/elevated-card' as tokens-mat-elevated-card; @use '../core/tokens/m2/mat/outlined-card' as tokens-mat-outlined-card; +@use '../core/tokens/m2/mat/filled-card' as tokens-mat-filled-card; @mixin base($theme) { @if inspection.get-theme-version($theme) == 1 { @@ -22,6 +23,10 @@ tokens-mat-outlined-card.$prefix, tokens-mat-outlined-card.get-unthemable-tokens() ); + @include token-utils.create-token-values-mixed( + tokens-mat-filled-card.$prefix, + tokens-mat-filled-card.get-unthemable-tokens() + ); @include token-utils.create-token-values-mixed( tokens-mat-card.$prefix, tokens-mat-card.get-unthemable-tokens() @@ -43,6 +48,10 @@ tokens-mat-outlined-card.$prefix, tokens-mat-outlined-card.get-color-tokens($theme) ); + @include token-utils.create-token-values-mixed( + tokens-mat-filled-card.$prefix, + tokens-mat-filled-card.get-color-tokens($theme) + ); @include token-utils.create-token-values-mixed( tokens-mat-card.$prefix, tokens-mat-card.get-color-tokens($theme) @@ -64,6 +73,10 @@ tokens-mat-outlined-card.$prefix, tokens-mat-outlined-card.get-typography-tokens($theme) ); + @include token-utils.create-token-values-mixed( + tokens-mat-filled-card.$prefix, + tokens-mat-filled-card.get-typography-tokens($theme) + ); @include token-utils.create-token-values-mixed( tokens-mat-card.$prefix, tokens-mat-card.get-typography-tokens($theme) @@ -85,6 +98,10 @@ tokens-mat-outlined-card.$prefix, tokens-mat-outlined-card.get-density-tokens($theme) ); + @include token-utils.create-token-values-mixed( + tokens-mat-filled-card.$prefix, + tokens-mat-filled-card.get-density-tokens($theme) + ); @include token-utils.create-token-values-mixed( tokens-mat-card.$prefix, tokens-mat-card.get-density-tokens($theme) @@ -110,6 +127,11 @@ tokens: tokens-mat-outlined-card.get-token-slots(), prefix: 'outlined-', ), + ( + namespace: tokens-mat-filled-card.$prefix, + tokens: tokens-mat-filled-card.get-token-slots(), + prefix: 'filled-', + ) ); } @@ -149,6 +171,10 @@ tokens-mat-outlined-card.$prefix, map.get($tokens, tokens-mat-outlined-card.$prefix) ); + @include token-utils.create-token-values( + tokens-mat-filled-card.$prefix, + map.get($tokens, tokens-mat-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 e392ffc162cc..82b654e6c394 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/mat/elevated-card' as tokens-mat-elevated-card; @use '../core/tokens/m2/mat/outlined-card' as tokens-mat-outlined-card; +@use '../core/tokens/m2/mat/filled-card' as tokens-mat-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-mat-filled-card.$prefix, + tokens-mat-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 80c58b639d8b..a0197b3c48ca 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 b44a30b53f0c..42f540fd132e 100644 --- a/src/material/core/tokens/m2/_index.scss +++ b/src/material/core/tokens/m2/_index.scss @@ -7,6 +7,7 @@ @use './mat/text-button' as tokens-mat-text-button; @use './mat/protected-button' as tokens-mat-protected-button; @use './mat/filled-button' as tokens-mat-filled-button; +@use './mat/filled-card' as tokens-mat-filled-card; @use './mat/outlined-button' as tokens-mat-outlined-button; @use './mat/dialog' as tokens-mat-dialog; @use './mat/bottom-sheet' as tokens-mat-bottom-sheet; @@ -114,6 +115,7 @@ _get-tokens-for-module($theme, tokens-mat-fab), _get-tokens-for-module($theme, tokens-mat-fab-small), _get-tokens-for-module($theme, tokens-mat-filled-button), + _get-tokens-for-module($theme, tokens-mat-filled-card), _get-tokens-for-module($theme, tokens-mat-form-field), _get-tokens-for-module($theme, tokens-mat-grid-list), _get-tokens-for-module($theme, tokens-mat-icon-button), diff --git a/src/material/core/tokens/m2/mat/_filled-card.scss b/src/material/core/tokens/m2/mat/_filled-card.scss new file mode 100644 index 000000000000..0bccc53969a3 --- /dev/null +++ b/src/material/core/tokens/m2/mat/_filled-card.scss @@ -0,0 +1,50 @@ +@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: (mat, 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 ( + container-shape: 4px, + ); +} + +// 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 ( + 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 f6e664f48dc7..ea6c1e73aef9 100644 --- a/src/material/core/tokens/m3/_index.scss +++ b/src/material/core/tokens/m3/_index.scss @@ -16,6 +16,7 @@ @use './mat/fab'; @use './mat/fab-small'; @use './mat/filled-button'; +@use './mat/filled-card'; @use './mat/filled-text-field'; @use './mat/form-field'; @use './mat/full-pseudo-checkbox'; @@ -78,6 +79,7 @@ fab.get-tokens($systems, $exclude-hardcoded, $token-slots), fab-small.get-tokens($systems, $exclude-hardcoded, $token-slots), filled-button.get-tokens($systems, $exclude-hardcoded, $token-slots), + filled-card.get-tokens($systems, $exclude-hardcoded, $token-slots), filled-text-field.get-tokens($systems, $exclude-hardcoded, $token-slots), form-field.get-tokens($systems, $exclude-hardcoded, $token-slots), full-pseudo-checkbox.get-tokens($systems, $exclude-hardcoded, $token-slots), diff --git a/src/material/core/tokens/m3/mat/_filled-card.scss b/src/material/core/tokens/m3/mat/_filled-card.scss new file mode 100644 index 000000000000..275c685859a8 --- /dev/null +++ b/src/material/core/tokens/m3/mat/_filled-card.scss @@ -0,0 +1,27 @@ +@use 'sass:map'; +@use '../../../style/elevation'; +@use '../../token-definition'; + +// The prefix used to generate the fully qualified name for tokens in this file. +$prefix: (mat, 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: ( + container-color: map.get($systems, md-sys-color, surface-container-highest), + container-elevation: map.get($systems, md-sys-elevation, level0), + container-shape: map.get($systems, md-sys-shape, corner-medium), + ); + + $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); +}