From 9f0cba80d368b4289e3c682c571b3562c6c0159f Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 12 Jun 2023 13:41:44 +0200 Subject: [PATCH] refactor(material/table): switch to tokens API Reworks the Material table to use the new tokens API. --- src/material/core/tokens/_token-utils.scss | 13 ++- src/material/core/tokens/m2/mat/_table.scss | 97 ++++++++++++++++++ src/material/table/_table-theme.scss | 64 +++++------- src/material/table/table.scss | 108 +++++++++++++++++--- 4 files changed, 227 insertions(+), 55 deletions(-) create mode 100644 src/material/core/tokens/m2/mat/_table.scss diff --git a/src/material/core/tokens/_token-utils.scss b/src/material/core/tokens/_token-utils.scss index 254e0c97e09b..a1ac307350b4 100644 --- a/src/material/core/tokens/_token-utils.scss +++ b/src/material/core/tokens/_token-utils.scss @@ -71,12 +71,21 @@ $_component-prefix: null; // Emits a slot for the given token, provided that it has a non-null value in the token map passed // to `use-tokens`. -@mixin create-token-slot($property, $token) { +@mixin create-token-slot($property, $token, $emit-fallback: false) { @if $_component-prefix == null or $_tokens == null { @error '`create-token-slot` must be used within `use-tokens`'; } @if map.get($_tokens, $token) != null { - $value: mdc-custom-properties.create('#{$_component-prefix}-#{$token}'); + $fallback: null; + + @if ($emit-fallback == true) { + $fallback: map.get($_tokens, $token); + } + @else if ($emit-fallback) { + $fallback: $emit-fallback; + } + + $value: mdc-custom-properties.create('#{$_component-prefix}-#{$token}', $fallback: $fallback); @include mdc-theme.property($property, $value); } } diff --git a/src/material/core/tokens/m2/mat/_table.scss b/src/material/core/tokens/m2/mat/_table.scss new file mode 100644 index 000000000000..cd9f442e338d --- /dev/null +++ b/src/material/core/tokens/m2/mat/_table.scss @@ -0,0 +1,97 @@ +@use 'sass:map'; +@use '../../../theming/theming'; +@use '../../../typography/typography-utils'; +@use '../../token-utils'; +@use '../../../style/sass-utils'; + +// The prefix used to generate the fully qualified name for tokens in this file. +$prefix: (mat, table); + +// Tokens that can't be configured through Angular Material's current theming API, +// but may be in a future version of the theming API. +@function get-unthemable-tokens() { + @return ( + row-item-outline-width: 1px, + ); +} + +// Tokens that can be configured through Angular Material's color theming API. +@function get-color-tokens($config) { + $foreground: map.get($config, foreground); + $background: map.get($config, background); + + @return ( + background-color: theming.get-color-from-palette($background, 'card'), + + header-headline-color: theming.get-color-from-palette($foreground, text), + row-item-label-text-color: theming.get-color-from-palette($foreground, text), + row-item-outline-color: theming.get-color-from-palette($foreground, divider), + ); +} + +// Tokens that can be configured through Angular Material's typography theming API. +@function get-typography-tokens($config) { + $fallback-font: typography-utils.font-family($config); + $cell-font-family: typography-utils.font-family($config, body-2) or $fallback-font; + $cell-line-height: typography-utils.line-height($config, body-2); + $cell-font-size: typography-utils.font-size($config, body-2); + $cell-font-weight: typography-utils.font-weight($config, body-2); + $cell-letter-spacing: typography-utils.letter-spacing($config, body-2); + + @return ( + header-headline-font: typography-utils.font-family($config, subtitle-2) or $fallback-font, + header-headline-line-height: typography-utils.line-height($config, subtitle-2), + header-headline-size: typography-utils.font-size($config, subtitle-2), + header-headline-weight: typography-utils.font-weight($config, subtitle-2), + header-headline-tracking: typography-utils.letter-spacing($config, subtitle-2), + + // Plain cells and footer cells have the same typography. + row-item-label-text-font: $cell-font-family, + row-item-label-text-line-height: $cell-line-height, + row-item-label-text-size: $cell-font-size, + row-item-label-text-weight: $cell-font-weight, + row-item-label-text-tracking: $cell-letter-spacing, + + footer-supporting-text-font: $cell-font-family, + footer-supporting-text-line-height: $cell-line-height, + footer-supporting-text-size: $cell-font-size, + footer-supporting-text-weight: $cell-font-weight, + footer-supporting-text-tracking: $cell-letter-spacing, + ); +} + +// Tokens that can be configured through Angular Material's density theming API. +@function get-density-tokens($config) { + $scale: theming.clamp-density($config, -4); + $header-scale: ( + 0: 56px, + -1: 52px, + -2: 48px, + -3: 44px, + -4: 40px + ); + $cell-scale: ( + 0: 52px, + -1: 48px, + -2: 44px, + -3: 40px, + -4: 36px + ); + + @return ( + header-container-height: map.get($header-scale, $scale), + footer-container-height: map.get($cell-scale, $scale), + row-item-container-height: map.get($cell-scale, $scale), + ); +} + +// Combines the tokens generated by the above functions into a single map with placeholder values. +// This is used to create token slots. +@function get-token-slots() { + @return sass-utils.deep-merge-all( + get-unthemable-tokens(), + get-color-tokens(token-utils.$placeholder-color-config), + get-typography-tokens(token-utils.$placeholder-typography-config), + get-density-tokens(token-utils.$placeholder-density-config) + ); +} diff --git a/src/material/table/_table-theme.scss b/src/material/table/_table-theme.scss index c0bbb235ef50..cc15290349ce 100644 --- a/src/material/table/_table-theme.scss +++ b/src/material/table/_table-theme.scss @@ -1,60 +1,44 @@ -@use 'sass:map'; -@use '@material/theme/theme-color' as mdc-theme-color; -@use '@material/data-table/data-table' as mdc-data-table; -@use '@material/data-table' as mdc-data-table-theme; +@use '../core/tokens/m2/mat/table' as tokens-mat-table; @use '../core/theming/theming'; -@use '../core/mdc-helpers/mdc-helpers'; @use '../core/typography/typography'; +@use '../core/tokens/token-utils'; -@mixin color($config-or-theme) { - $config: theming.get-color-config($config-or-theme); - // Save original values of MDC global variables. We need to save these so we can restore the - // variables to their original values and prevent unintended side effects from using this mixin. - $orig-selected-row-fill-color: mdc-data-table-theme.$selected-row-fill-color; - $orig-divider-color: mdc-data-table-theme.$divider-color; - $orig-row-hover-fill-color: mdc-data-table-theme.$row-hover-fill-color; - $orig-header-row-text-color: mdc-data-table-theme.$header-row-text-color; - $orig-row-text-color: mdc-data-table-theme.$row-text-color; - $orig-stroke-color: mdc-data-table-theme.$stroke-color; - - @include mdc-helpers.using-mdc-theme($config) { - mdc-data-table-theme.$selected-row-fill-color: rgba(mdc-theme-color.prop-value(primary), 0.04); - mdc-data-table-theme.$divider-color: rgba(mdc-theme-color.prop-value(on-surface), 0.12); - mdc-data-table-theme.$row-hover-fill-color: rgba(mdc-theme-color.prop-value(on-surface), 0.04); - mdc-data-table-theme.$header-row-text-color: rgba(mdc-theme-color.prop-value(on-surface), 0.87); - mdc-data-table-theme.$row-text-color: rgba(mdc-theme-color.prop-value(on-surface), 0.87); - mdc-data-table-theme.$stroke-color: rgba(mdc-theme-color.prop-value(on-surface), 0.12); - - @include mdc-data-table.table-styles($query: mdc-helpers.$mdc-theme-styles-query); +@mixin _output-tokens { + @if (&) { + @content; + } + @else { + html { + @content; + } } +} - // Restore original values of MDC global variables. - mdc-data-table-theme.$selected-row-fill-color: $orig-selected-row-fill-color; - mdc-data-table-theme.$divider-color: $orig-divider-color; - mdc-data-table-theme.$row-hover-fill-color: $orig-row-hover-fill-color; - mdc-data-table-theme.$header-row-text-color: $orig-header-row-text-color; - mdc-data-table-theme.$row-text-color: $orig-row-text-color; - mdc-data-table-theme.$stroke-color: $orig-stroke-color; +@mixin color($config-or-theme) { + $config: theming.get-color-config($config-or-theme); - .mat-mdc-table { - $background: map.get($config, background); - background: theming.get-color-from-palette($background, 'card'); + @include _output-tokens { + @include token-utils.create-token-values(tokens-mat-table.$prefix, + tokens-mat-table.get-color-tokens($config)); } } @mixin typography($config-or-theme) { $config: typography.private-typography-to-2018-config( theming.get-typography-config($config-or-theme)); - @include mdc-helpers.using-mdc-typography($config) { - @include mdc-data-table.table-styles($query: mdc-helpers.$mdc-typography-styles-query); + + @include _output-tokens { + @include token-utils.create-token-values(tokens-mat-table.$prefix, + tokens-mat-table.get-typography-tokens($config)); } } @mixin density($config-or-theme) { $density-scale: theming.get-density-config($config-or-theme); - .mat-mdc-table { - @include mdc-data-table-theme.density($density-scale, - $query: mdc-helpers.$mdc-base-styles-query); + + @include _output-tokens { + @include token-utils.create-token-values(tokens-mat-table.$prefix, + tokens-mat-table.get-density-tokens($density-scale)); } } diff --git a/src/material/table/table.scss b/src/material/table/table.scss index c7e5c7d56c15..44a852dcba79 100644 --- a/src/material/table/table.scss +++ b/src/material/table/table.scss @@ -1,13 +1,13 @@ @use '@material/data-table/data-table' as mdc-data-table; +@use '@material/data-table/data-table-cell' as mdc-data-table-cell; +@use '@material/data-table/data-table-header-cell' as mdc-data-table-header-cell; +@use '@material/data-table' as mdc-data-table-theme; +@use '@material/typography/typography' as mdc-typography; @use '../core/mdc-helpers/mdc-helpers'; +@use '../core/tokens/token-utils'; +@use '../core/tokens/m2/mat/table' as tokens-mat-table; @use './table-flex-styles'; -@include mdc-helpers.disable-mdc-fallback-declarations { - @include mdc-data-table.table-styles( - $query: mdc-helpers.$mdc-base-styles-without-animation-query); - @include table-flex-styles.private-table-flex-styles(); -} - .mat-mdc-table-sticky { // Note that the table can either set this class or an inline style to make something sticky. // We set the style as `!important` so that we get an identical specificity in both cases @@ -15,7 +15,26 @@ position: sticky !important; } +@mixin _cell-border { + @include token-utils.create-token-slot(border-bottom-color, row-item-outline-color, true); + @include token-utils.create-token-slot(border-bottom-width, row-item-outline-width, true); + border-bottom-style: solid; +} + +@include mdc-data-table.static-styles($query: mdc-helpers.$mdc-base-styles-query); +@include mdc-data-table-cell.static-styles($query: mdc-helpers.$mdc-base-styles-query); +@include mdc-data-table-header-cell.static-styles($query: mdc-helpers.$mdc-base-styles-query); +@include mdc-data-table-theme.cell-padding( + $leading-padding: mdc-data-table-theme.$cell-leading-padding, + $trailing-padding: mdc-data-table-theme.$cell-trailing-padding, + $query: mdc-helpers.$mdc-base-styles-query +); +@include table-flex-styles.private-table-flex-styles(); + .mat-mdc-table { + @include token-utils.create-token-values( + tokens-mat-table.$prefix, tokens-mat-table.get-unthemable-tokens()); + // MDC Table applies `table-layout: fixed`, but this is a backwards incompatible // change since the table did not previously apply it. // TODO: Add a mixin to MDC to set the layout instead of including this override, @@ -25,6 +44,76 @@ // The MDC table does not allow text to wrap within the cell. This allows for text to // wrap when the cell reaches its maximum width. white-space: normal; + + @include token-utils.use-tokens(tokens-mat-table.$prefix, tokens-mat-table.get-token-slots()) { + @include token-utils.create-token-slot(background-color, background-color); + } +} + +@include mdc-helpers.disable-mdc-fallback-declarations { + @include token-utils.use-tokens(tokens-mat-table.$prefix, tokens-mat-table.get-token-slots()) { + // TODO(crisbeto): these tokens have default values in order to make the initial token + // work easier to land in g3. Eventually we should remove them. + .mat-mdc-header-row { + @include mdc-typography.smooth-font(); + @include token-utils.create-token-slot(height, header-container-height, 56px); + @include token-utils.create-token-slot(color, header-headline-color, true); + @include token-utils.create-token-slot(font-family, header-headline-font, true); + @include token-utils.create-token-slot(line-height, header-headline-line-height); + @include token-utils.create-token-slot(font-size, header-headline-size, 14px); + @include token-utils.create-token-slot(font-weight, header-headline-weight, 500); + } + + .mat-mdc-row { + @include token-utils.create-token-slot(height, row-item-container-height, 52px); + @include token-utils.create-token-slot(color, row-item-label-text-color, true); + } + + // Note that while it's redundant to apply the typography both to the row + // and the content element since the cell inherit from both of them, + // applying it only to one results in sub-pixel differences in the + // letter spacing which leads to a lot of internal screenshot diffs. + .mat-mdc-row, + .mdc-data-table__content { + @include mdc-typography.smooth-font(); + @include token-utils.create-token-slot(font-family, row-item-label-text-font, true); + @include token-utils.create-token-slot(line-height, row-item-label-text-line-height); + @include token-utils.create-token-slot(font-size, row-item-label-text-size, 14px); + @include token-utils.create-token-slot(font-weight, row-item-label-text-weight); + } + + .mat-mdc-footer-row { + @include mdc-typography.smooth-font(); + @include token-utils.create-token-slot(height, footer-container-height, 52px); + @include token-utils.create-token-slot(color, row-item-label-text-color, true); + @include token-utils.create-token-slot(font-family, footer-supporting-text-font, true); + @include token-utils.create-token-slot(line-height, footer-supporting-text-line-height); + @include token-utils.create-token-slot(font-size, footer-supporting-text-size, 14px); + @include token-utils.create-token-slot(font-weight, footer-supporting-text-weight); + @include token-utils.create-token-slot(letter-spacing, footer-supporting-text-tracking); + } + + .mat-mdc-header-cell { + @include _cell-border; + @include token-utils.create-token-slot(letter-spacing, header-headline-tracking); + font-weight: inherit; + line-height: inherit; + } + + .mat-mdc-cell { + @include _cell-border; + @include token-utils.create-token-slot(letter-spacing, row-item-label-text-tracking); + line-height: inherit; + + .mdc-data-table__row:last-child & { + border-bottom: none; + } + } + + .mat-mdc-footer-cell { + @include token-utils.create-token-slot(letter-spacing, row-item-label-text-tracking); + } + } } // MDC table rows are styled with a top border, whereas our legacy flex table styles rows with @@ -43,13 +132,6 @@ mat-row.mat-mdc-row, mat-header-row.mat-mdc-header-row, mat-footer-row.mat-mdc-f background: inherit; } -// Disable hover styling while MDC uses an opacity for its color. -// When the hover style is used with sticky cells, the opacity shows the cells overlapping. -.mat-mdc-table .mat-mdc-row:hover, -.mat-mdc-table .mat-mdc-footer-row:hover { - background-color: inherit; -} - // Flex rows should not set a definite height, but instead stretch to the height of their // children. Otherwise, the cells grow larger than the row and the layout breaks. .mat-mdc-table mat-header-row.mat-mdc-header-row,