// // Copyright 2021 Google Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // stylelint-disable selector-class-pattern -- // Selector '.mdc-*' should only be used in this project. @use 'sass:math'; @use 'sass:map'; @use 'sass:meta'; @use '@material/density/functions' as density-functions; @use '@material/density/variables' as density-variables; @use '@material/elevation/elevation-theme'; @use '@material/feature-targeting/feature-targeting'; @use '@material/focus-ring/focus-ring'; @use '@material/ripple/ripple-theme'; @use '@material/rtl/rtl'; @use '@material/dom/dom'; @use '@material/theme/keys'; @use '@material/theme/state'; @use '@material/theme/theme'; @use '@material/theme/theme-color'; @use '@material/touch-target/mixins' as touch-target-mixins; $ripple-target: '.mdc-icon-button__ripple'; $icon-size: 24px !default; $size: 48px !default; $minimum-height: 28px !default; $maximum-height: $size !default; $container-shape: 50%; $density-scale: density-variables.$default-scale !default; $density-config: ( size: ( default: $size, maximum: $maximum-height, minimum: $minimum-height, ), ) !default; $_custom-property-prefix: 'icon-button'; $light-theme: ( disabled-icon-color: theme-color.$on-surface, disabled-icon-opacity: 0.38, icon-color: theme-color.$primary, icon-size: $icon-size, focus-icon-color: theme-color.$primary, focus-state-layer-color: theme-color.$primary, focus-state-layer-opacity: 0.12, hover-icon-color: theme-color.$primary, hover-state-layer-color: theme-color.$primary, hover-state-layer-opacity: 0.08, pressed-icon-color: theme-color.$primary, pressed-state-layer-color: theme-color.$primary, pressed-state-layer-opacity: 0.12, state-layer-size: $size, ); @mixin theme($theme) { @include theme.validate-theme($light-theme, $theme); @include keys.declare-custom-properties( $theme, $prefix: $_custom-property-prefix ); } @mixin theme-styles($theme) { @include theme.validate-theme-styles($light-theme, $theme); $theme: keys.create-theme-properties( $theme, $prefix: $_custom-property-prefix ); @include _state-layer-size($size: map.get($theme, state-layer-size)); @include _icon-size(map.get($theme, icon-size)); @include _disabled-icon-opacity(map.get($theme, disabled-icon-opacity)); @include _icon-color-with-map( ( default: map.get($theme, icon-color), disabled: map.get($theme, disabled-icon-color), focus: map.get($theme, focus-icon-color), hover: map.get($theme, hover-icon-color), pressed: map.get($theme, pressed-icon-color), ) ); // States styles @include ripple-theme.theme-styles( ( focus-state-layer-color: map.get($theme, focus-state-layer-color), focus-state-layer-opacity: map.get($theme, focus-state-layer-opacity), hover-state-layer-color: map.get($theme, hover-state-layer-color), hover-state-layer-opacity: map.get($theme, hover-state-layer-opacity), pressed-state-layer-color: map.get($theme, pressed-state-layer-color), pressed-state-layer-opacity: map.get($theme, pressed-state-layer-opacity), ), $ripple-target: $ripple-target ); } /// /// Sets the density scale for icon button. /// /// @param {Number | String} $density-scale - Density scale value for component. /// Supported density scale values range from `-5` to `0`, with `0` being the default. /// @mixin density($density-scale, $query: feature-targeting.all()) { $size: density-functions.prop-value( $density-config: $density-config, $density-scale: $density-scale, $property-name: size, ); @include size($size, $query: $query); } /// /// Sets the size of the icon-button. /// /// @param {Number} $size - Size value for icon-button. /// Size will set the width, height, and padding for the overall component. /// @mixin size($size, $query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { width: $size; height: $size; padding: calc(($size - $icon-size) / 2); } .mdc-icon-button__focus-ring { @include feature-targeting.targets($feat-structure) { max-height: $size; max-width: $size; } } &.mdc-icon-button--reduced-size { $component-size: $size; // Icon button ripple size is capped at 40px for icon buttons with // densities -1 and 0 (icon buttons with sizes 44x44 and 48x48px). // See http://b/192353968 for more info. @if math.unit($size) == 'px' and ($size >= 40px and $size <= 48px) { $component-size: 40px; } .mdc-icon-button__ripple { @include feature-targeting.targets($feat-structure) { width: $component-size; height: $component-size; } @include touch-target-mixins.margin( $component-height: $component-size, $component-width: $component-size, $touch-target-height: $size, $touch-target-width: $size, $query: $query ); } .mdc-icon-button__focus-ring { @include feature-targeting.targets($feat-structure) { max-height: $component-size; max-width: $component-size; } } } .mdc-icon-button__touch { @include touch-target-mixins.touch-target( $set-width: true, $query: $query, $height: $size, $width: $size ); } } /// /// Sets the width, height and padding of icon button. Also changes the size of /// the icon itself based on button size. /// /// @param {Number} $width - Width value for icon-button. /// @param {Number} $height - Height value for icon-button. (default: $width) /// @param {Number} $padding - Padding value for icon-button. (default: max($width, $height) / 2) /// @deprecated /// This mixin provides too much of low level customization. /// Please use mdc-icon-button-size instead. /// @mixin icon-size( $width, $height: $width, $padding: math.div(math.max($width, $height), 2), $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); $component-width: $width + $padding * 2; $component-height: $height + $padding * 2; @include feature-targeting.targets($feat-structure) { width: $component-width; height: $component-height; padding: $padding; font-size: math.max($width, $height); } svg, img { @include feature-targeting.targets($feat-structure) { width: $width; height: $height; } } } /// /// Sets the font color and the ripple color to the provided color value. /// @param {Color} $color - The desired font and ripple color. /// @mixin ink-color($color, $query: feature-targeting.all()) { @if $color { @include ink-color_($color, $query: $query); @include ripple-theme.states( $color, $query: $query, $ripple-target: $ripple-target ); } } /// /// Flips icon only in RTL context. /// @mixin flip-icon-in-rtl($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); .mdc-button__icon { @include rtl.rtl { @include feature-targeting.targets($feat-structure) { @include rtl.ignore-next-line(); transform: rotate(180deg); } } } } /// /// Sets the font color to the provided color value for a disabled icon button. /// @param {Color} $color - The desired font color. /// @mixin disabled-ink-color($color, $query: feature-targeting.all()) { @include if-disabled_ { @include ink-color_($color, $query: $query); } } /// /// Includes ad-hoc high contrast mode support. /// @mixin high-contrast-mode-shim($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { // TODO(b/175806874): Use the DOM border mixin after the ripple is moved // away from :before to a dedicated element. outline: solid 3px transparent; &:focus { outline: double 5px transparent; } } } /// /// Sets the font color to the provided color value. This can be wrapped in /// a state qualifier such as `mdc-icon-button-if-disabled_`. /// @access private /// @mixin ink-color_($color, $query: feature-targeting.all()) { $feat-color: feature-targeting.create-target($query, color); @include feature-targeting.targets($feat-color) { @include theme.property(color, $color); } } @mixin _state-layer-size($size) { @include theme.property(height, $size); @include theme.property(width, $size); } @mixin _icon-size($size) { .mdc-button__icon { @include theme.property(font-size, $size); } svg, img { @include theme.property(width, $size); @include theme.property(height, $size); } } /// /// Sets the icon opacity to the given opacity. /// @access private /// @mixin _disabled-icon-opacity($opacity) { &:disabled { @include theme.property(opacity, $opacity); } } /// /// Sets the icon color to the given color. /// @param {map} $color-map - The desired icon color, specified as a map of /// colors with states {default, disabled, focus, hover, pressed} as keys. /// @access private /// @mixin _icon-color-with-map($color-map) { @include ink-color_(state.get-default-state($color-map)); $focus: state.get-focus-state($color-map); @if $focus { @include ripple-theme.focus { @include ink-color_($focus); } } $hover: state.get-hover-state($color-map); @if $hover { &:hover { @include ink-color_($hover); } } $pressed: state.get-pressed-state($color-map); @if $pressed { @include ripple-theme.active { @include ink-color_($pressed); } } $disabled: state.get-disabled-state($color-map); @if $disabled { &:disabled { @include ink-color_($disabled); } } } @mixin _states-colors($color-map) { // TODO(b/191298796): support focused & pressed key colors. $hover: map.get($color-map, hover); @if $hover { @include ripple-theme.states-base-color( $color: $hover, $ripple-target: $ripple-target ); } } /// /// Helps style the icon button in its disabled state. /// @access private /// @mixin if-disabled_ { &:disabled { @content; } }