| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- // Copyright 2016 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 '@material/elevation/elevation-theme';
- @use '@material/animation/functions' as animation-functions;
- @use '@material/feature-targeting/feature-targeting';
- @use '@material/ripple/ripple-theme';
- @use '@material/shape/mixins' as shape-mixins;
- @use '@material/theme/css';
- @use '@material/theme/custom-properties';
- @use '@material/theme/keys';
- @use '@material/theme/replace';
- @use '@material/theme/state';
- @use '@material/theme/theme';
- @use '@material/theme/theme-color';
- @use '@material/tokens/resolvers';
- @use './fab-custom-properties';
- @use 'sass:math';
- @use 'sass:list';
- @use 'sass:map';
- @use 'sass:meta';
- $height: 56px !default;
- $mini-height: 40px !default;
- $shape-radius: 50% !default;
- $ripple-target: '.mdc-fab__ripple';
- $light-theme: (
- container-color: secondary,
- container-elevation: 6,
- container-height: 56px,
- container-shadow-color: black,
- container-shape: $shape-radius,
- container-surface-tint-layer-color: null,
- container-width: 56px,
- focus-container-elevation: null,
- focus-icon-color: null,
- focus-outline-color: null,
- focus-outline-width: null,
- focus-state-layer-color: theme-color.$primary,
- focus-state-layer-opacity: null,
- hover-container-elevation: null,
- hover-icon-color: null,
- hover-state-layer-color: theme-color.$primary,
- hover-state-layer-opacity: null,
- icon-color: on-secondary,
- icon-size: 24px,
- lowered-container-elevation: null,
- lowered-focus-container-elevation: null,
- lowered-hover-container-elevation: null,
- lowered-pressed-container-elevation: null,
- pressed-container-elevation: null,
- pressed-icon-color: null,
- pressed-ripple-color: null,
- pressed-ripple-opacity: null,
- pressed-state-layer-color: theme-color.$primary,
- pressed-state-layer-opacity: null,
- );
- $custom-property-prefix: 'fab';
- ///
- /// Applies the given theme as custom properties without any selectors.
- ///
- @mixin theme($theme, $resolvers: resolvers.$material) {
- @include theme.validate-theme($light-theme, $theme);
- $resolved-theme: resolve-theme($theme, $resolvers);
- @include keys.declare-custom-properties(
- $resolved-theme,
- $prefix: $custom-property-prefix
- );
- }
- @mixin theme-styles($theme, $resolvers: resolvers.$material) {
- @include theme.validate-theme($light-theme, $theme);
- $theme: keys.create-theme-properties(
- $theme,
- $prefix: $custom-property-prefix
- );
- @include base-theme-styles($theme, $resolvers: $resolvers);
- $shape-radius: map.get($theme, container-shape);
- @if $shape-radius {
- @include shape-radius($shape-radius);
- }
- }
- ///
- /// Resolves the given theme with the given resolvers.
- ///
- @function resolve-theme($theme, $resolvers) {
- $elevation-resolver: map.get($resolvers, elevation);
- @return _resolve-theme-elevation-keys(
- $theme,
- $elevation-resolver,
- (
- container-elevation,
- hover-container-elevation,
- focus-container-elevation,
- pressed-container-elevation,
- disabled-container-elevation,
- lowered-container-elevation,
- lowered-focus-container-elevation,
- lowered-hover-container-elevation,
- lowered-pressed-container-elevation
- )
- );
- }
- ///
- /// Returns the theme with the elevation keys resolved.
- ///
- @function _resolve-theme-elevation-keys($theme, $resolver, $elevation-keys) {
- @if $resolver == null {
- @return $theme;
- }
- // Shadow color is universal for the component.
- $shadow-color: map.get($theme, container-shadow-color);
- @each $key in $elevation-keys {
- $elevation: map.get($theme, $key);
- @if $elevation != null {
- $resolved-value: meta.call(
- $resolver,
- $elevation: $elevation,
- $shadow-color: $shadow-color
- );
- // Update the key with the resolved value.
- $theme: map.set($theme, $key, $resolved-value);
- }
- }
- @return $theme;
- }
- @mixin base-theme-styles($theme, $resolvers: resolvers.$material) {
- @include container-color(map.get($theme, container-color));
- @include _container-elevation(
- map.get($resolvers, elevation),
- map.get($theme, container-shadow-color),
- map.get($theme, container-surface-tint-layer-color),
- (
- default: map.get($theme, container-elevation),
- hover: map.get($theme, hover-container-elevation),
- focus: map.get($theme, focus-container-elevation),
- pressed: map.get($theme, pressed-container-elevation),
- )
- );
- @include _container-height(map.get($theme, container-height));
- @include _container-width(map.get($theme, container-width));
- @include icon-size(map.get($theme, icon-size));
- @include _icon-color(
- (
- default: map.get($theme, icon-color),
- hover: map.get($theme, hover-icon-color),
- focus: map.get($theme, focus-icon-color),
- pressed: map.get($theme, pressed-icon-color),
- )
- );
- $opacity-map: (
- hover: map.get($theme, hover-state-layer-opacity),
- focus: map.get($theme, focus-state-layer-opacity),
- press: map.get($theme, pressed-state-layer-opacity),
- );
- $hover-state-layer-color: map.get($theme, hover-state-layer-color);
- @if $hover-state-layer-color {
- @include ripple-color($hover-state-layer-color, $opacity-map: $opacity-map);
- }
- $focus-outline-color: map.get($theme, focus-outline-color);
- @if $focus-outline-color {
- @include focus-outline-color($focus-outline-color);
- }
- $focus-outline-width: map.get($theme, focus-outline-width);
- @if $focus-outline-width {
- @include focus-outline-width($focus-outline-width);
- }
- }
- @mixin ripple-color($color, $opacity-map: (), $query: feature-targeting.all()) {
- @include ripple-theme.states(
- $color,
- $opacity-map: $opacity-map,
- $query: $query,
- $ripple-target: $ripple-target
- );
- }
- @mixin accessible($container-color, $query: feature-targeting.all()) {
- @include container-color($container-color, $query: $query);
- $fill-tone: theme-color.tone($container-color);
- @if ($fill-tone == 'dark') {
- @include ink-color(text-primary-on-dark, $query: $query);
- @include ripple-theme.states(
- text-primary-on-dark,
- $query: $query,
- $ripple-target: $ripple-target
- );
- } @else {
- @include ink-color(text-primary-on-light, $query: $query);
- @include ripple-theme.states(
- text-primary-on-light,
- $query: $query,
- $ripple-target: $ripple-target
- );
- }
- }
- @mixin container-color($color, $query: feature-targeting.all()) {
- $feat-color: feature-targeting.create-target($query, color);
- @include feature-targeting.targets($feat-color) {
- @include theme.property(background-color, $color);
- }
- }
- @mixin icon-size($width, $height: $width, $query: feature-targeting.all()) {
- $feat-structure: feature-targeting.create-target($query, structure);
- .mdc-fab__icon {
- @include feature-targeting.targets($feat-structure) {
- @include theme.property('width', $width);
- @include theme.property('height', $height);
- @include theme.property('font-size', $height);
- }
- }
- }
- @mixin ink-color($color, $query: feature-targeting.all()) {
- $feat-color: feature-targeting.create-target($query, color);
- @include feature-targeting.targets($feat-color) {
- &,
- &:not(:disabled) .mdc-fab__icon,
- &:not(:disabled) .mdc-fab__label,
- &:disabled .mdc-fab__icon,
- &:disabled .mdc-fab__label {
- @include theme.property(color, $color);
- }
- }
- }
- @mixin _container-height($height) {
- @include theme.property('height', $height);
- }
- @mixin _container-width($width) {
- @include theme.property('width', $width);
- }
- @mixin _icon-color($color-or-map) {
- &:not(:disabled) {
- @include _set-icon-color(state.get-default-state($color-or-map));
- &:hover {
- @include _set-icon-color(state.get-hover-state($color-or-map));
- }
- &:focus {
- @include _set-icon-color(state.get-focus-state($color-or-map));
- }
- &:active {
- @include _set-icon-color(state.get-pressed-state($color-or-map));
- }
- }
- &:disabled {
- @include _set-icon-color(state.get-disabled-state($color-or-map));
- }
- }
- @mixin _set-icon-color($color) {
- .mdc-fab__icon {
- @include theme.property(color, $color);
- }
- }
- @mixin _container-elevation($resolver, $shadow-color, $container-color, $map) {
- &:not(:disabled) {
- @include elevation-theme.with-resolver(
- $resolver,
- $elevation: state.get-default-state($map),
- $shadow-color: $shadow-color
- );
- @include elevation-theme.overlay-container-color($container-color);
- &:hover {
- @include elevation-theme.with-resolver(
- $resolver,
- $elevation: state.get-hover-state($map),
- $shadow-color: $shadow-color
- );
- @include elevation-theme.overlay-container-color($container-color);
- }
- &:focus {
- @include elevation-theme.with-resolver(
- $resolver,
- $elevation: state.get-focus-state($map),
- $shadow-color: $shadow-color
- );
- @include elevation-theme.overlay-container-color($container-color);
- }
- &:active {
- @include elevation-theme.with-resolver(
- $resolver,
- $elevation: state.get-pressed-state($map),
- $shadow-color: $shadow-color
- );
- @include elevation-theme.overlay-container-color($container-color);
- }
- }
- &:disabled {
- // FAB does not have disabled state. Use default state's elevation.
- @include elevation-theme.with-resolver(
- $resolver,
- $elevation: state.get-default-state($map),
- $shadow-color: $shadow-color
- );
- }
- }
- ///
- /// Sets outline width only when button is in focus. Also sets padding to
- /// include outline on focus (Helps prevent size jump on focus).
- /// @param {Number} $width - Outline (border) width.
- /// @param {Number|List} $padding [0] - Padding when button is not in focus.
- /// Offsets padding based on given outline width on focus.
- ///
- @mixin focus-outline-width(
- $width,
- $padding: 0,
- $query: feature-targeting.all()
- ) {
- $feat-structure: feature-targeting.create-target($query, structure);
- $padding: css.unpack-value($padding);
- $padding-fallbacks: (0 0 0 0);
- $is-padding-custom-prop: (false false false false);
- $is-width-custom-prop: custom-properties.is-custom-prop($width);
- $width-fallback: if(
- custom-properties.is-custom-prop($width),
- custom-properties.get-fallback($width),
- $width
- );
- $width: if(
- custom-properties.is-custom-prop($width),
- custom-properties.get-declaration-value($width),
- $width
- );
- // conform padding values and extract custom property metadata from them
- @for $i from 1 through list.length($padding) {
- $value: list.nth($padding, $i);
- $value-is-custom-prop: custom-properties.is-custom-prop($value);
- // css max will fail to compare a bare 0 to a px value
- $value: if($value == 0, 0px, $value);
- $value-fallback: if(
- $value-is-custom-prop,
- custom-properties.get-fallback($value),
- $value
- );
- $value: if(
- $value-is-custom-prop,
- custom-properties.get-declaration-value($value),
- $value
- );
- $padding: list.set-nth($padding, $i, $value);
- $padding-fallbacks: list.set-nth($padding-fallbacks, $i, $value-fallback);
- $is-padding-custom-prop: list.set-nth(
- $is-padding-custom-prop,
- $i,
- $value-is-custom-prop
- );
- }
- // Padding should include outline width which will be set on focus.
- // sass math required for IE since IE doesn't support css max
- $padding-top-fallback: math.max(
- list.nth($padding-fallbacks, 1),
- $width-fallback
- );
- $padding-right-fallback: math.max(
- list.nth($padding-fallbacks, 2),
- $width-fallback
- );
- $padding-bottom-fallback: math.max(
- list.nth($padding-fallbacks, 3),
- $width-fallback
- );
- $padding-left-fallback: math.max(
- list.nth($padding-fallbacks, 4),
- $width-fallback
- );
- $padding-top: replace.replace-string(
- 'max(paddingval, width)',
- (
- paddingval: list.nth($padding, 1),
- width: $width,
- )
- );
- $padding-right: replace.replace-string(
- 'max(paddingval, width)',
- (
- paddingval: list.nth($padding, 2),
- width: $width,
- )
- );
- $padding-bottom: replace.replace-string(
- 'max(paddingval, width)',
- (
- paddingval: list.nth($padding, 3),
- width: $width,
- )
- );
- $padding-left: replace.replace-string(
- 'max(paddingval, width)',
- (
- paddingval: list.nth($padding, 4),
- width: $width,
- )
- );
- $top-has-custom-prop: $is-width-custom-prop or
- list.nth($is-padding-custom-prop, 1);
- @include css.declaration(padding-top, $padding-top-fallback);
- @if $top-has-custom-prop {
- @include css.declaration(
- padding-top,
- $padding-top,
- $gss: (alternate: $top-has-custom-prop)
- );
- }
- $right-has-custom-prop: $is-width-custom-prop or
- list.nth($is-padding-custom-prop, 2);
- @include css.declaration(padding-right, $padding-right-fallback);
- @if $right-has-custom-prop {
- @include css.declaration(
- padding-right,
- $padding-right,
- $gss: (alternate: $right-has-custom-prop)
- );
- }
- $bottom-has-custom-prop: $is-width-custom-prop or
- list.nth($is-padding-custom-prop, 3);
- @include css.declaration(padding-bottom, $padding-bottom-fallback);
- @if $bottom-has-custom-prop {
- @include css.declaration(
- padding-bottom,
- $padding-bottom,
- $gss: (alternate: $bottom-has-custom-prop)
- );
- }
- $left-has-custom-prop: $is-width-custom-prop or
- list.nth($is-padding-custom-prop, 4);
- @include css.declaration(padding-left, $padding-left-fallback);
- @if $left-has-custom-prop {
- @include css.declaration(
- padding-left,
- $padding-left,
- $gss: (alternate: $left-has-custom-prop)
- );
- }
- &:not(:disabled) {
- @include ripple-theme.focus() {
- @include feature-targeting.targets($feat-structure) {
- border-style: solid;
- @include theme.property(border-width, $width);
- // sass math required for IE since IE doesn't support css max
- $padding-top-fallback: math.abs(
- list.nth($padding-fallbacks, 1) - $width-fallback
- );
- $padding-right-fallback: math.abs(
- list.nth($padding-fallbacks, 2) - $width-fallback
- );
- $padding-bottom-fallback: math.abs(
- list.nth($padding-fallbacks, 3) - $width-fallback
- );
- $padding-left-fallback: math.abs(
- list.nth($padding-fallbacks, 4) - $width-fallback
- );
- // max(a, calc(a * -1)) is equivalent to math.abs
- $padding-top: replace.replace-string(
- 'max(paddingcalc, calc(paddingcalc * -1))',
- (
- paddingcalc: 'calc(paddingval - width)',
- paddingval: list.nth($padding, 1),
- width: $width,
- )
- );
- $padding-right: replace.replace-string(
- 'max(paddingcalc, calc(paddingcalc * -1))',
- (
- paddingcalc: 'calc(paddingval - width)',
- paddingval: list.nth($padding, 2),
- width: $width,
- )
- );
- $padding-bottom: replace.replace-string(
- 'max(paddingcalc, calc(paddingcalc * -1))',
- (
- paddingcalc: 'calc(paddingval - width)',
- paddingval: list.nth($padding, 3),
- width: $width,
- )
- );
- $padding-left: replace.replace-string(
- 'max(paddingcalc, calc(paddingcalc * -1))',
- (
- paddingcalc: 'calc(paddingval - width)',
- paddingval: list.nth($padding, 4),
- width: $width,
- )
- );
- @include css.declaration(padding-top, $padding-top-fallback);
- @if $top-has-custom-prop {
- @include css.declaration(
- padding-top,
- $padding-top,
- $gss: (alternate: $top-has-custom-prop)
- );
- }
- @include css.declaration(padding-right, $padding-right-fallback);
- @if $right-has-custom-prop {
- @include css.declaration(
- padding-right,
- $padding-right,
- $gss: (alternate: $right-has-custom-prop)
- );
- }
- @include css.declaration(padding-bottom, $padding-bottom-fallback);
- @if $bottom-has-custom-prop {
- @include css.declaration(
- padding-bottom,
- $padding-bottom,
- $gss: (alternate: $bottom-has-custom-prop)
- );
- }
- @include css.declaration(padding-left, $padding-left-fallback);
- @if $left-has-custom-prop {
- @include css.declaration(
- padding-left,
- $padding-left,
- $gss: (alternate: $left-has-custom-prop)
- );
- }
- }
- }
- }
- }
- ///
- /// Sets outline color only when button is in focus. Use `focus-outline-width()`
- /// to set appropriate outline width.
- /// @param {Color} $color - Outline (border) color.
- ///
- @mixin focus-outline-color($color, $query: feature-targeting.all()) {
- $feat-color: feature-targeting.create-target($query, color);
- &:not(:disabled) {
- @include ripple-theme.focus() {
- @include feature-targeting.targets($feat-color) {
- @include theme.property(border-color, $color);
- }
- }
- }
- }
- @mixin shape-radius(
- $radius,
- $rtl-reflexive: false,
- $query: feature-targeting.all()
- ) {
- &:not(.mdc-fab--extended) {
- // Do not specify $component-height for shape radius. FABs are circular,
- // which means they can use percentage border radius without resolving to
- // a component height.
- @include shape-mixins.radius($radius, $rtl-reflexive, $query: $query);
- #{$ripple-target} {
- @include shape-mixins.radius($radius, $rtl-reflexive, $query: $query);
- }
- }
- }
|