// // 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 'sass:map'; @use 'sass:math'; @use '@material/animation/functions' as functions2; @use '@material/animation/variables' as animation-variables; @use '@material/density/functions' as density-functions; @use '@material/dom/dom'; @use '@material/feature-targeting/feature-targeting'; @use '@material/focus-ring/focus-ring'; @use '@material/rtl/rtl'; @use '@material/ripple/ripple'; @use '@material/ripple/ripple-theme'; @use '@material/touch-target/mixins' as touch-target-mixins; @use '@material/theme/theme-color'; @use './checkbox-custom-properties'; @use '@material/theme/theme'; @use '@material/theme/color-custom-properties'; @use '@material/theme/custom-properties'; @use '@material/touch-target/variables' as touch-target-variables; @use './checkbox-theme'; /// /// Checkbox and ripple styles. /// @mixin core-styles($query: feature-targeting.all()) { @include without-ripple($query); @include ripple-styles($query); } /// Checkbox styles (Excluding ripple styles). /// /// NOTE: This API is intended for use by frameworks that may want to separate the ripple-related styles from the other /// checkbox styles. It is recommended that most users use `mdc-checkbox-core-styles` instead. // TODO(b/162887560): Rename to `checkbox-without-ripple-styles()` @mixin without-ripple($query: feature-targeting.all()) { // TODO(b/165005345): Include theme-styles() after static-styles(). @include theme-styles($query: $query); @include static-styles($query: $query); } /// Checkbox static styles. /// Checkbox styles that are not customizable should go here. @mixin static-styles($query: feature-targeting.all()) { $feat-animation: feature-targeting.create-target($query, animation); $feat-structure: feature-targeting.create-target($query, structure); @include touch-target-mixins.wrapper($query); // COPYBARA_COMMENT_THIS_LINE @include feature-targeting.targets($feat-animation) { @include mark-keyframes_; } .mdc-checkbox { @include feature-targeting.targets($feat-structure) { @include base_; } @include ripple-theme.focus { .mdc-checkbox__focus-ring { @include focus-ring.focus-ring( $query: $query, $container-outer-padding-vertical: 0, $container-outer-padding-horizontal: 0 ); } } // Turn off focus ring for IE when HCM is turn off. For some reason this // adds space to the bottom on the focused checkbox inside a dialog. @media all and (-ms-high-contrast: none) { .mdc-checkbox__focus-ring { display: none; } } } @include dom.forced-colors-mode { .mdc-checkbox__mixedmark { @include feature-targeting.targets($feat-structure) { margin: 0 1px; // Extra horizontal space around mixedmark symbol. } } } // Needed to disable hover effects on CSS-only (non-JS) checkboxes .mdc-checkbox--disabled { @include feature-targeting.targets($feat-structure) { @include disabled_; } } .mdc-checkbox__background { @include background_($query); } .mdc-checkbox__checkmark { @include checkmark_($query); } .mdc-checkbox__checkmark-path { @include checkmark-path_($query); } .mdc-checkbox__mixedmark { @include mixedmark_($query); } .mdc-checkbox--anim { @include feature-targeting.targets($feat-animation) { @include anim_; } } .mdc-checkbox__native-control:checked ~ .mdc-checkbox__background, .mdc-checkbox__native-control:indeterminate ~ .mdc-checkbox__background, .mdc-checkbox__native-control[data-indeterminate='true'] ~ .mdc-checkbox__background { @include feature-targeting.targets($feat-animation) { @include background--marked_; } .mdc-checkbox__checkmark-path { @include feature-targeting.targets($feat-structure) { @include checkmark-path--marked_; } } } .mdc-checkbox__native-control { @include feature-targeting.targets($feat-structure) { @include native-control_; } &:disabled { @include feature-targeting.targets($feat-structure) { @include disabled_; } } } .mdc-checkbox--touch { @include checkbox-theme.touch-target( custom-properties.create( checkbox-state-layer-size, touch-target-variables.$height ), custom-properties.create( checkbox-state-layer-size, checkbox-theme.$ripple-size ), $query: $query ); } .mdc-checkbox__native-control:checked ~ .mdc-checkbox__background { .mdc-checkbox__checkmark { @include checkmark--checked_($query); } .mdc-checkbox__mixedmark { @include feature-targeting.targets($feat-structure) { @include mixedmark--checked_; } } } .mdc-checkbox__native-control:indeterminate ~ .mdc-checkbox__background, .mdc-checkbox__native-control[data-indeterminate='true'] ~ .mdc-checkbox__background { .mdc-checkbox__checkmark { @include checkmark--indeterminate_($query); } .mdc-checkbox__mixedmark { @include feature-targeting.targets($feat-structure) { @include mixedmark--indeterminate_; } } } // JS checkbox .mdc-checkbox.mdc-checkbox--upgraded { .mdc-checkbox__background, .mdc-checkbox__checkmark, .mdc-checkbox__checkmark-path, .mdc-checkbox__mixedmark { @include feature-targeting.targets($feat-animation) { @include child--upgraded_; } } } } /// Checkbox theme styles. /// Checkbox styles that are customizable should go here. @mixin theme-styles($query: feature-targeting.all()) { .mdc-checkbox { @include checkbox-theme.theme-deprecated( checkbox-theme.$light-theme-deprecated, $query: $query ); } } /// Checkbox's ripple styles. /// /// NOTE: This API is intended for use by frameworks that may want to separate the ripple-related styles from the other /// checkbox styles. It is recommended that most users use `mdc-checkbox-core-styles` instead. @mixin ripple-styles($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include ripple.common($query); // COPYBARA_COMMENT_THIS_LINE .mdc-checkbox { @include ripple.surface( $query: $query, $ripple-target: checkbox-theme.$ripple-target ); @include ripple.radius-unbounded( $query: $query, $ripple-target: checkbox-theme.$ripple-target ); @include ripple-theme.behind-content( checkbox-theme.$ripple-target, $query: $query ); } #{checkbox-theme.$ripple-target} { @include ripple.target-common($query: $query); } } @mixin base_ { display: inline-block; &[hidden] { display: none; } position: relative; flex: 0 0 checkbox-theme.$icon-size; box-sizing: content-box; width: checkbox-theme.$icon-size; height: checkbox-theme.$icon-size; line-height: 0; white-space: nowrap; cursor: pointer; vertical-align: bottom; } @mixin disabled_ { cursor: default; pointer-events: none; } @mixin child--upgraded_ { transition: none; } // Animation @mixin anim_ { $mdc-checkbox-indeterminate-change-duration_: 500ms; // stylelint-disable no-unknown-animations -- allow unknown animations &-unchecked-checked, &-unchecked-indeterminate, &-checked-unchecked, &-indeterminate-unchecked { .mdc-checkbox__background { animation-duration: checkbox-theme.$transition-duration * 2; animation-timing-function: linear; } } &-unchecked-checked { .mdc-checkbox__checkmark-path { // Instead of delaying the animation, we simply multiply its length by 2 and begin the // animation at 50% in order to prevent a flash of styles applied to a checked checkmark // as the background is fading in before the animation begins. animation: mdc-checkbox-unchecked-checked-checkmark-path checkbox-theme.$transition-duration * 2 linear 0s; transition: none; } } &-unchecked-indeterminate { .mdc-checkbox__mixedmark { animation: mdc-checkbox-unchecked-indeterminate-mixedmark checkbox-theme.$transition-duration linear 0s; transition: none; } } &-checked-unchecked { .mdc-checkbox__checkmark-path { animation: mdc-checkbox-checked-unchecked-checkmark-path checkbox-theme.$transition-duration linear 0s; transition: none; } } &-checked-indeterminate { .mdc-checkbox__checkmark { animation: mdc-checkbox-checked-indeterminate-checkmark checkbox-theme.$transition-duration linear 0s; transition: none; } .mdc-checkbox__mixedmark { animation: mdc-checkbox-checked-indeterminate-mixedmark checkbox-theme.$transition-duration linear 0s; transition: none; } } &-indeterminate-checked { .mdc-checkbox__checkmark { animation: mdc-checkbox-indeterminate-checked-checkmark $mdc-checkbox-indeterminate-change-duration_ linear 0s; transition: none; } .mdc-checkbox__mixedmark { animation: mdc-checkbox-indeterminate-checked-mixedmark $mdc-checkbox-indeterminate-change-duration_ linear 0s; transition: none; } } &-indeterminate-unchecked { .mdc-checkbox__mixedmark { animation: mdc-checkbox-indeterminate-unchecked-mixedmark $mdc-checkbox-indeterminate-change-duration_ * 0.6 linear 0s; transition: none; } } // stylelint-enable no-unknown-animations } @mixin background_($query: feature-targeting.all()) { $feat-animation: feature-targeting.create-target($query, animation); $feat-structure: feature-targeting.create-target($query, structure); $feat-color: feature-targeting.create-target($query, color); @include feature-targeting.targets($feat-structure) { display: inline-flex; position: absolute; align-items: center; justify-content: center; box-sizing: border-box; width: checkbox-theme.$icon-size; height: checkbox-theme.$icon-size; // border-color is overridden by the mdc-checkbox-unmarked-stroke-color() mixin border: checkbox-theme.$border-width solid currentColor; border-radius: 2px; background-color: transparent; pointer-events: none; will-change: background-color, border-color; } @include feature-targeting.targets($feat-animation) { transition: transition-exit(background-color), transition-exit(border-color); } } @mixin background--marked_ { transition: transition-enter(border-color), transition-enter(background-color); } // stylelint-disable block-no-empty -- For backward compatibility. @mixin focus-indicator_($query: feature-targeting.all()) { } @mixin focus-indicator--focused_($query: feature-targeting.all()) { } // stylelint-enable block-no-empty // Native input @mixin native-control_ { position: absolute; margin: 0; padding: 0; opacity: 0; cursor: inherit; } // Check mark @mixin checkmark_($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) { position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100%; opacity: 0; } @include feature-targeting.targets($feat-animation) { transition: transition-exit( opacity, 0ms, checkbox-theme.$transition-duration * 2 ); } .mdc-checkbox--upgraded & { @include feature-targeting.targets($feat-structure) { opacity: 1; } } } @mixin checkmark--checked_($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-animation) { transition: transition-enter( opacity, 0ms, checkbox-theme.$transition-duration * 2 ), transition-enter(transform, 0ms, checkbox-theme.$transition-duration * 2); } @include feature-targeting.targets($feat-structure) { opacity: 1; } } @mixin checkmark--indeterminate_($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) { @include rtl.ignore-next-line(); transform: rotate(45deg); opacity: 0; } @include feature-targeting.targets($feat-animation) { transition: transition-exit( opacity, 0ms, checkbox-theme.$transition-duration ), transition-exit(transform, 0ms, checkbox-theme.$transition-duration); } } // Check mark path @mixin checkmark-path_($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-animation) { transition: transition-exit( stroke-dashoffset, 0ms, checkbox-theme.$transition-duration * 2 ); } @include feature-targeting.targets($feat-structure) { stroke: currentColor; stroke-width: checkbox-theme.$mark-stroke-size * 1.3; stroke-dashoffset: $mark-path-length_; stroke-dasharray: $mark-path-length_; } } @mixin checkmark-path--marked_ { stroke-dashoffset: 0; } // Mixed mark @mixin mixedmark_($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) { width: 100%; height: 0; @include rtl.ignore-next-line(); transform: scaleX(0) rotate(0deg); border-width: math.div(math.floor(checkbox-theme.$mark-stroke-size), 2); border-style: solid; opacity: 0; } @include feature-targeting.targets($feat-animation) { transition: transition-exit(opacity), transition-exit(transform); } } @mixin mixedmark--checked_ { @include rtl.ignore-next-line(); transform: scaleX(1) rotate(-45deg); } @mixin mixedmark--indeterminate_ { @include rtl.ignore-next-line(); transform: scaleX(1) rotate(0deg); opacity: 1; } @function transition-enter( $property, $delay: 0ms, $duration: checkbox-theme.$transition-duration ) { @return functions2.enter($property, $duration, $delay); } @function transition-exit( $property, $delay: 0ms, $duration: checkbox-theme.$transition-duration ) { @return functions2.exit-temporary($property, $duration, $delay); } // Manual calculation done on SVG $mark-path-length_: 29.7833385 !default; $indeterminate-checked-easing-function_: cubic-bezier(0.14, 0, 0, 1) !default; @mixin mark-keyframes_ { @keyframes mdc-checkbox-unchecked-checked-checkmark-path { 0%, 50% { stroke-dashoffset: $mark-path-length_; } 50% { animation-timing-function: animation-variables.$deceleration-curve-timing-function; } 100% { stroke-dashoffset: 0; } } @keyframes mdc-checkbox-unchecked-indeterminate-mixedmark { 0%, 68.2% { transform: scaleX(0); } 68.2% { animation-timing-function: cubic-bezier(0, 0, 0, 1); } 100% { transform: scaleX(1); } } @keyframes mdc-checkbox-checked-unchecked-checkmark-path { from { animation-timing-function: animation-variables.$acceleration-curve-timing-function; opacity: 1; stroke-dashoffset: 0; } to { opacity: 0; stroke-dashoffset: $mark-path-length_ * -1; } } @keyframes mdc-checkbox-checked-indeterminate-checkmark { from { animation-timing-function: animation-variables.$deceleration-curve-timing-function; @include rtl.ignore-next-line(); transform: rotate(0deg); opacity: 1; } to { @include rtl.ignore-next-line(); transform: rotate(45deg); opacity: 0; } } @keyframes mdc-checkbox-indeterminate-checked-checkmark { from { animation-timing-function: $indeterminate-checked-easing-function_; @include rtl.ignore-next-line(); transform: rotate(45deg); opacity: 0; } to { @include rtl.ignore-next-line(); transform: rotate(360deg); opacity: 1; } } @keyframes mdc-checkbox-checked-indeterminate-mixedmark { from { animation-timing-function: mdc-animation-deceleration-curve-timing-function; @include rtl.ignore-next-line(); transform: rotate(-45deg); opacity: 0; } to { @include rtl.ignore-next-line(); transform: rotate(0deg); opacity: 1; } } @keyframes mdc-checkbox-indeterminate-checked-mixedmark { from { animation-timing-function: $indeterminate-checked-easing-function_; @include rtl.ignore-next-line(); transform: rotate(0deg); opacity: 1; } to { @include rtl.ignore-next-line(); transform: rotate(315deg); opacity: 0; } } @keyframes mdc-checkbox-indeterminate-unchecked-mixedmark { 0% { animation-timing-function: linear; transform: scaleX(1); opacity: 1; } 32.8%, 100% { transform: scaleX(0); opacity: 0; } } }