Skip to content

Commit

Permalink
feat(material/button): change icon-button to use MDC's token API (#26824
Browse files Browse the repository at this point in the history
)
  • Loading branch information
andrewseguin committed Mar 31, 2023
1 parent 475de3c commit 2604f15
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 47 deletions.
100 changes: 70 additions & 30 deletions src/material/button/_icon-button-theme.scss
@@ -1,41 +1,66 @@
@use 'sass:map';
@use '@material/density/functions' as mdc-density-functions;
@use '@material/icon-button/mixins' as mdc-icon-button;
@use '@material/icon-button/icon-button-theme' as mdc-icon-button-theme;
@use '@material/theme/theme-color' as mdc-theme-color;
@use '../core/tokens/m2/mdc/icon-button' as tokens-mdc-icon-button;

@use './button-theme-private';
@use '../core/mdc-helpers/mdc-helpers';
@use '../core/theming/theming';
@use '../core/typography/typography';

@mixin _ripple-color($color) {
--mat-mdc-button-persistent-ripple-color: #{$color};
--mat-mdc-button-ripple-color: #{rgba($color, 0.1)};
}

@function _variable-safe-contrast-tone($value, $is-dark) {
@if ($value == 'dark' or $value == 'light' or type-of($value) == 'color') {
@return mdc-theme-color.contrast-tone($value);
}

@return if($is-dark, 'light', 'dark');
}


@mixin color($config-or-theme) {
$config: theming.get-color-config($config-or-theme);
@include mdc-helpers.using-mdc-theme($config) {
$is-dark: map.get($config, is-dark);
$on-surface: mdc-theme-color.prop-value(on-surface);
$disabled-color: rgba($on-surface, if($is-dark, 0.5, 0.38));

.mat-mdc-icon-button {
@include button-theme-private.ripple-theme-styles($config, false);

&.mat-primary {
@include mdc-icon-button-theme.theme((icon-color: mdc-theme-color.prop-value(primary)));
}

&.mat-accent {
@include mdc-icon-button-theme.theme((icon-color: mdc-theme-color.prop-value(secondary)));
}

&.mat-warn {
@include mdc-icon-button-theme.theme((icon-color: (mdc-theme-color.prop-value(error))));
}

@include button-theme-private.apply-disabled-style() {
@include mdc-icon-button-theme.theme((
icon-color: $disabled-color,
disabled-icon-color: $disabled-color,
));
}
$color-tokens: tokens-mdc-icon-button.get-color-tokens($config);
$background-palette: map.get($config, background);
$surface: theming.get-color-from-palette($background-palette, card);
$is-dark: map.get($config, is-dark);
$on-surface: if(_variable-safe-contrast-tone($surface, $is-dark) == 'dark', #000, #fff);

.mat-mdc-icon-button {
@include button-theme-private.ripple-theme-styles($config, false);
@include mdc-icon-button-theme.theme($color-tokens);
@include _ripple-color($on-surface);

&.mat-primary {
$color: theming.get-color-from-palette(map.get($config, primary));
@include mdc-icon-button-theme.theme((icon-color: $color));
@include _ripple-color($color);
}

&.mat-accent {
$color: theming.get-color-from-palette(map.get($config, accent));
@include mdc-icon-button-theme.theme((icon-color: $color));
@include _ripple-color($color);
}

&.mat-warn {
$color: theming.get-color-from-palette(map.get($config, warn));
@include mdc-icon-button-theme.theme((icon-color: $color));
@include _ripple-color($color);
}

@include button-theme-private.apply-disabled-style() {
$disabled-color: rgba($on-surface, if($is-dark, 0.5, 0.38));
@include mdc-icon-button-theme.theme((
icon-color: $disabled-color,
disabled-icon-color: $disabled-color,
));
}
}
}
Expand All @@ -50,10 +75,25 @@

@mixin density($config-or-theme) {
$density-scale: theming.get-density-config($config-or-theme);
// Use `mat-mdc-button-base` to increase the specificity over the button's structural styles.
.mat-mdc-icon-button.mat-mdc-button-base {
@include mdc-icon-button.density($density-scale, $query: mdc-helpers.$mdc-base-styles-query);
@include button-theme-private.touch-target-density($density-scale);

.mat-mdc-icon-button {
// Manually apply the expected density theming, and include the padding
// as it was applied before
$calculated-size: mdc-density-functions.prop-value(
$density-config: (
size: (
default: 48px,
maximum: 48px,
minimum: 28px,
),
),
$density-scale: $density-scale,
$property-name: size,
);

@include mdc-icon-button-theme.theme((
state-layer-size: $calculated-size,
));
}
}

Expand Down
41 changes: 24 additions & 17 deletions src/material/button/icon-button.scss
@@ -1,29 +1,36 @@
@use 'sass:map';
@use '@material/icon-button/icon-button' as mdc-icon-button;
@use '@material/icon-button/icon-button-theme' as mdc-icon-button-theme;
@use '@material/theme/custom-properties' as mdc-custom-properties;

@use '../core/tokens/m2/mdc/icon-button' as m2-mdc-icon-button;

@use './button-base';
@use '../core/mdc-helpers/mdc-helpers';
@use '../core/style/private';

@include mdc-helpers.disable-mdc-fallback-declarations {
@include mdc-icon-button.without-ripple($query: mdc-helpers.$mdc-base-styles-query);
// 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) {
$token-slots: m2-mdc-icon-button.get-token-slots();

// Add the MDC component static styles.
@include mdc-icon-button.static-styles();

.mat-mdc-icon-button {
// Add the official slots for the MDC component.
@include mdc-icon-button-theme.theme-styles($token-slots);

// Add default values for tokens that aren't outputted by the theming API.
@include mdc-icon-button-theme.theme(m2-mdc-icon-button.get-unthemable-tokens());
}
}

.mat-mdc-icon-button {
@include mdc-helpers.disable-mdc-fallback-declarations {
$theme-overrides: button-base.mat-private-button-remove-ripple((
icon-color: inherit,
// We don't change the color on focus/hover so exclude
// these styles both to reduce bundle size and specificity.
focus-icon-color: null,
hover-icon-color: null,
pressed-icon-color: null,
));

@include mdc-icon-button-theme.theme-styles(
map.merge(mdc-icon-button-theme.$light-theme, $theme-overrides));
}
// Not all applications import the theming which would apply a default padding.
// TODO: Determine how to enforce theming exists, otherwise padding will be unset.
padding: 12px;

// Icon size used to be placed on the host element. Now, in `theme-styles` it is placed on
// the unused `.mdc-button__icon` class. Explicitly set the font-size here.
font-size: var(--mdc-icon-button-icon-size);

// Border radius is inherited by ripple to know its shape. Set to 50% so the ripple is round.
border-radius: 50%;
Expand Down
80 changes: 80 additions & 0 deletions src/material/core/tokens/m2/mdc/_icon-button.scss
@@ -0,0 +1,80 @@
@use 'sass:map';
@use '../../token-utils';

// The prefix used to generate the fully qualified name for tokens in this file.
$prefix: (mdc, icon-button);

// 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 (
// =============================================================================================
// = TOKENS THAT SHOULD NOT BE CUSTOMIZABLE =
// =============================================================================================
// Determines the size of the icon. Name is inaccurate - applies to the whole component,
// not just the state layer.
state-layer-size: 48px,

// MDC's icon size applied to svg and img elements inside the component
icon-size: 24px,

// Only applies to :disabled icons, but Angular Components uses [disabled] since :disabled
// wouldn't work on <a> tags.
disabled-icon-color: black,

// Angular version applies an opacity 1 with a color change, and this only applies with
// :disabled anyways.
disabled-icon-opacity: 0.38,

// =============================================================================================
// = TOKENS NOT USED IN ANGULAR MATERIAL =
// =============================================================================================
// State layer is unused
focus-icon-color: null,
focus-state-layer-color: null,
focus-state-layer-opacity: null,
hover-icon-color: null,
hover-state-layer-color: null,
hover-state-layer-opacity: null,
pressed-icon-color: null,
pressed-state-layer-color: null,
pressed-state-layer-opacity: null,

);
}

// Tokens that can be configured through Angular Material's color theming API.
@function get-color-tokens($config) {
@return (
icon-color: inherit,
);
}

// Tokens that can be configured through Angular Material's typography theming API.
@function get-typography-tokens($config) {
@return ();
}

// Tokens that can be configured through Angular Material's density theming API.
@function get-density-tokens($config) {
@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 map.merge(
get-unthemable-tokens(),
map.merge(
get-color-tokens(token-utils.$placeholder-color-config),
map.merge(
get-typography-tokens(token-utils.$placeholder-typography-config),
get-density-tokens(token-utils.$placeholder-density-config)
)
)
);
}
7 changes: 7 additions & 0 deletions src/material/core/tokens/tests/test-validate-tokens.scss
Expand Up @@ -5,12 +5,14 @@
@use '@material/card/outlined-card-theme' as mdc-outlined-card-theme;
@use '@material/checkbox/checkbox-theme' as mdc-checkbox-theme;
@use '@material/circular-progress/circular-progress-theme' as mdc-circular-progress-theme;
@use '@material/icon-button/icon-button-theme' as mdc-icon-button-theme;
@use '@material/linear-progress/linear-progress-theme' as mdc-linear-progress-theme;
@use '@material/list/list-theme' as mdc-list-theme;
@use '@material/theme/validate' as mdc-validate;

@use '../m2/mdc/circular-progress' as tokens-mdc-circular-progress;
@use '../m2/mdc/elevated-card' as tokens-mdc-elevated-card;
@use '../m2/mdc/icon-button' as tokens-mdc-icon-button;
@use '../m2/mdc/checkbox' as tokens-mdc-checkbox;
@use '../m2/mdc/linear-progress' as tokens-mdc-linear-progress;
@use '../m2/mdc/list' as tokens-mdc-list;
Expand Down Expand Up @@ -38,6 +40,11 @@
$slots: tokens-mdc-circular-progress.get-token-slots(),
$reference: mdc-circular-progress-theme.$light-theme
);
@include validate-slots(
$component: 'm2.mdc.icon-button',
$slots: tokens-mdc-icon-button.get-token-slots(),
$reference: mdc-icon-button-theme.$light-theme
);
@include validate-slots(
$component: 'm2.mdc.elevated-card',
$slots: tokens-mdc-elevated-card.get-token-slots(),
Expand Down

0 comments on commit 2604f15

Please sign in to comment.