// // Copyright 2018 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 '@material/density/functions' as density-functions; @use '@material/elevation/mixins' as elevation-mixins; @use '@material/feature-targeting/feature-targeting'; @use '@material/ripple/ripple'; @use '@material/ripple/ripple-theme'; @use '@material/rtl/rtl'; @use '@material/theme/theme'; @use './functions'; @use './variables'; @use '@material/dom/mixins' as dom-mixins; // Temporary suffix to add to shared selectors between versions for migration $deprecated-suffix: '-deprecated' !default; // // Public // @mixin core-styles($query: feature-targeting.all()) { @include without-ripple($query); @include ripple($query); } // This API is intended for use by frameworks that may want to separate the ripple-related styles from the other // switch styles. It is recommended that most users use `mdc-switch-core-styles` instead. @mixin without-ripple($query: feature-targeting.all()) { // postcss-bem-linter: define switch $feat-animation: feature-targeting.create-target($query, animation); $feat-structure: feature-targeting.create-target($query, structure); @include density(variables.$density-scale, $query: $query); .mdc-switch#{$deprecated-suffix} { @include feature-targeting.targets($feat-structure) { @include base_; } @include toggled-on-track-color(variables.$baseline-theme-color, $query); @include toggled-on-thumb-color(variables.$baseline-theme-color, $query); @include toggled-off-track-color( variables.$toggled-off-track-color, $query ); @include toggled-off-thumb-color( variables.$toggled-off-thumb-color, $query ); } .mdc-switch__native-control { @include feature-targeting.targets($feat-structure) { @include native-control_; } @include feature-targeting.targets($feat-animation) { transition: functions.transition(transform); } } .mdc-switch__track#{$deprecated-suffix} { @include track_($query); } .mdc-switch__thumb-underlay { @include thumb-underlay_($query); } .mdc-switch__thumb { @include thumb_($query); } .mdc-switch--checked { @include feature-targeting.targets($feat-structure) { .mdc-switch__track#{$deprecated-suffix} { @include track-checked_; } .mdc-switch__thumb-underlay { @include thumb-underlay-checked_; } .mdc-switch__native-control { @include native-control-checked_; } } } .mdc-switch--disabled { @include feature-targeting.targets($feat-structure) { @include disabled-base_; .mdc-switch__thumb { @include thumb-disabled_; } .mdc-switch__native-control { @include native-control-disabled_; } } } // postcss-bem-linter: end } // This API is intended for use by frameworks that may want to separate the ripple-related styles from the other // switch styles. It is recommended that most users use `mdc-switch-core-styles` instead. @mixin ripple($query: feature-targeting.all()) { @include ripple.common($query); // COPYBARA_COMMENT_THIS_LINE .mdc-switch#{$deprecated-suffix} { @include toggled-off-ripple-color( variables.$toggled-off-ripple-color, $query ); } .mdc-switch__thumb-underlay { @include ripple.surface($query); @include ripple.radius-unbounded(100%, $query); @include ripple-theme.states( variables.$baseline-theme-color, false, $query ); } } @mixin toggled-on-color($color, $query: feature-targeting.all()) { @include toggled-on-track-color($color, $query); @include toggled-on-thumb-color($color, $query); @include toggled-on-ripple-color($color, $query); } @mixin toggled-off-color($color, $query: feature-targeting.all()) { @include toggled-off-track-color($color, $query); @include toggled-off-thumb-color($color, $query); @include toggled-off-ripple-color($color, $query); } @mixin toggled-on-track-color($color, $query: feature-targeting.all()) { $feat-color: feature-targeting.create-target($query, color); &.mdc-switch--checked .mdc-switch__track#{$deprecated-suffix} { @include feature-targeting.targets($feat-color) { @include theme.property(background-color, $color); } } } @mixin toggled-on-thumb-color($color, $query: feature-targeting.all()) { $feat-color: feature-targeting.create-target($query, color); &.mdc-switch--checked .mdc-switch__thumb { @include feature-targeting.targets($feat-color) { @include theme.property(background-color, $color); @include theme.property(border-color, $color); } } } @mixin toggled-on-ripple-color($color, $query: feature-targeting.all()) { &.mdc-switch--checked .mdc-switch__thumb-underlay { @include ripple-theme.states($color, false, $query); } } @mixin toggled-off-track-color($color, $query: feature-targeting.all()) { $feat-color: feature-targeting.create-target($query, color); &:not(.mdc-switch--checked) .mdc-switch__track#{$deprecated-suffix} { @include feature-targeting.targets($feat-color) { @include theme.property(background-color, $color); } } } @mixin toggled-off-thumb-color($color, $query: feature-targeting.all()) { $feat-color: feature-targeting.create-target($query, color); &:not(.mdc-switch--checked) .mdc-switch__thumb { @include feature-targeting.targets($feat-color) { @include theme.property(background-color, $color); @include theme.property(border-color, $color); } } } @mixin toggled-off-ripple-color($color, $query: feature-targeting.all()) { &:not(.mdc-switch--checked) .mdc-switch__thumb-underlay { @include ripple-theme.states($color, false, $query); } } /// /// Sets density scale for switch. /// /// @param {Number | String} $density-scale - Density scale value for component. /// Supported density scale values are `-5`, `-4`, `-3`, `-2`, `-1`, /// `0` (default). /// @mixin density($density-scale, $query: feature-targeting.all()) { $size: density-functions.prop-value( $density-config: variables.$density-config, $density-scale: $density-scale, $property-name: size, ); @include ripple-size($size, $query: $query); } @mixin ripple-size($ripple-size, $query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); // Position for the tap target that contains the thumb to align the thumb // correctly offset from the track. $tap-target-initial-position: math.div(-$ripple-size, 2) + math.div(variables.$thumb-diameter, 2); // Value to cover the whole switch area (including the ripple) with the // native control. $native-control-width: variables.$track-width + ($ripple-size - variables.$thumb-diameter); .mdc-switch__thumb-underlay { @include feature-targeting.targets($feat-structure) { @include rtl.reflexive-position(left, $tap-target-initial-position); // Ensures the knob is centered on the track. top: -(math.div($ripple-size - variables.$track-height, 2)); width: $ripple-size; height: $ripple-size; } } .mdc-switch__native-control { @include feature-targeting.targets($feat-structure) { width: $native-control-width; height: $ripple-size; } } } /// /// Customizes ripple opacities surrounding the thumb in `hover`, `focus`, or `press` states /// The customizations apply to both on and off switches to ensure symmetry /// @param {map} $opacity-map - map specifying custom opacity of zero or more states /// @mixin ripple-states-opacity( $opacity-map: (), $query: feature-targeting.all() ) { // Ensure sufficient specificity to override base state opacities &.mdc-switch#{$deprecated-suffix} .mdc-switch__thumb-underlay { @include ripple-theme.states-opacities($opacity-map, $query: $query); } } // // Private // // Structure @mixin base_ { display: inline-block; position: relative; outline: none; user-select: none; } @mixin track_($query: feature-targeting.all()) { $feat-animation: feature-targeting.create-target($query, animation); $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { box-sizing: border-box; width: variables.$track-width; height: variables.$track-height; border: 1px solid transparent; border-radius: math.div(variables.$track-height, 2); opacity: 0.38; } @include feature-targeting.targets($feat-animation) { transition: functions.transition(opacity), functions.transition(background-color), functions.transition(border-color); } } @mixin thumb-underlay_($query: feature-targeting.all()) { $feat-animation: feature-targeting.create-target($query, animation); $feat-color: feature-targeting.create-target($query, color); $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { display: flex; position: absolute; align-items: center; justify-content: center; transform: translateX(0); } @include feature-targeting.targets($feat-animation) { transition: functions.transition(transform), functions.transition(background-color), functions.transition(border-color); } } @mixin native-control_ { @include rtl.reflexive-position(left, 0); position: absolute; top: 0; margin: 0; opacity: 0; cursor: pointer; pointer-events: auto; } @mixin thumb_($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); $feat-color: feature-targeting.create-target($query, color); @include elevation-mixins.elevation($z-value: 2, $query: $query); @include feature-targeting.targets($feat-structure) { box-sizing: border-box; width: variables.$thumb-diameter; height: variables.$thumb-diameter; border: math.div(variables.$thumb-diameter, 2) solid; border-radius: 50%; // Allow events to go through to the native control, necessary for IE and Edge. pointer-events: none; z-index: 1; } } // Checked state @mixin track-checked_ { opacity: 0.54; } @mixin thumb-underlay-checked_ { transform: translateX(variables.$thumb-active-margin); @include rtl.rtl { transform: translateX(-(variables.$thumb-active-margin)); } } @mixin native-control-checked_ { // Translate the native control the opposite direction so that the tap target stays the same. transform: translateX(-(variables.$thumb-active-margin)); @include rtl.rtl { transform: translateX(variables.$thumb-active-margin); } } // Disabled state @mixin disabled-base_ { opacity: 0.38; pointer-events: none; } @mixin thumb-disabled_ { border-width: 1px; // In high contrast mode, only show outline of knob. } @mixin native-control-disabled_ { cursor: default; pointer-events: none; } /// /// Includes ad-hoc high contrast mode support. /// @mixin high-contrast-mode-shim($query: feature-targeting.all()) { & .mdc-ripple-upgraded--background-focused .mdc-switch__thumb::before { @include dom-mixins.transparent-border($border-width: 3px); } }