// // Copyright 2017 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/animation/functions'; @use '@material/animation/variables' as animation-variables; @use '@material/checkbox/functions' as checkbox-functions; @use '@material/checkbox/variables' as checkbox-variables; @use '@material/elevation/mixins' as elevation-mixins; @use '@material/density/functions' as density-functions; @use '@material/feature-targeting/feature-targeting'; @use '@material/focus-ring/focus-ring'; @use '@material/ripple/ripple'; @use '@material/ripple/ripple-theme'; @use '@material/rtl/rtl'; @use '@material/theme/theme'; @use '@material/touch-target/mixins' as touch-target-mixins; @use '@material/typography/typography'; @use '@material/shape/mixins' as shape-mixins; @use '@material/shape/functions' as shape-functions; @use './variables'; @use '@material/elevation/functions' as elevation-functions; @use '@material/theme/theme-color'; @use './trailingaction/mixins' as trailing-action-mixins; $ripple-target: '.mdc-chip__ripple'; @mixin core-styles($query: feature-targeting.all()) { @include without-ripple($query: $query); @include ripple($query: $query); } @mixin without-ripple($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 leading-icon-color(variables.$icon-color, $query: $query); @include trailing-icon-color(variables.$icon-color, $query: $query); @include leading-icon-size(variables.$leading-icon-size, $query: $query); @include trailing-icon-size(variables.$trailing-icon-size, $query: $query); @include trailing-icon-margin($query: $query); @include touch-target-mixins.wrapper($query); // COPYBARA_COMMENT_THIS_LINE // prettier-ignore @include elevation-mixins.overlay-common($query); // COPYBARA_COMMENT_THIS_LINE .mdc-chip { @include shape-radius(variables.$shape-radius, $query: $query); @include fill-color(variables.$fill-color-default, $query: $query); @include ink-color-without-ripple_( variables.$ink-color-default, $query: $query ); @include typography.typography(body2, $query: $query); @include density($density-scale: 0, $query: $query); @include leading-icon-margin($query: $query); @include elevation-mixins.overlay-surface-position($query: $query); @include elevation-mixins.overlay-dimensions(100%, $query: $query); @include feature-targeting.targets($feat-structure) { display: inline-flex; // position: relative; already set in mdc-elevation-overlay-surface-position align-items: center; box-sizing: border-box; padding: 0 variables.$horizontal-padding; border-width: 0; outline: none; cursor: pointer; -webkit-appearance: none; &::-moz-focus-inner { padding: 0; border: 0; } } &:hover { @include feature-targeting.targets($feat-color) { @include theme.property(color, on-surface); } } .mdc-chip__touch { @include touch-target-mixins.touch-target($query: $query); } } .mdc-chip--exit { @include feature-targeting.targets($feat-color) { transition: variables.$exit-transition; } @include feature-targeting.targets($feat-structure) { opacity: 0; } } .mdc-chip__overflow { @include feature-targeting.targets($feat-structure) { text-overflow: ellipsis; overflow: hidden; } } .mdc-chip__text { @include feature-targeting.targets($feat-structure) { white-space: nowrap; } } .mdc-chip__icon { @include feature-targeting.targets($feat-structure) { border-radius: 50%; outline: none; vertical-align: middle; } } .mdc-chip__checkmark { @include feature-targeting.targets($feat-structure) { height: variables.$leading-icon-size; } } .mdc-chip__checkmark-path { @include feature-targeting.targets($feat-animation) { transition: checkbox-functions.transition-exit( stroke-dashoffset, variables.$checkmark-animation-delay, variables.$checkmark-animation-duration ); } @include feature-targeting.targets($feat-structure) { stroke-width: 2px; stroke-dashoffset: checkbox-variables.$mark-path-length_; stroke-dasharray: checkbox-variables.$mark-path-length_; } } .mdc-chip__primary-action:focus { @include feature-targeting.targets($feat-structure) { outline: none; } } .mdc-chip--selected .mdc-chip__checkmark-path { @include feature-targeting.targets($feat-structure) { stroke-dashoffset: 0; } } .mdc-chip__icon--leading, .mdc-chip__icon--trailing { @include feature-targeting.targets($feat-structure) { // Make these positioned elements, such that they're stacked above the // touch target element (`mdc-chip__touch`), so that clicks reach the // icons (e.g. for removable input chips). position: relative; } } // Change color of selected choice chips .mdc-chip-set--choice { .mdc-chip { @include selected-ink-color-without-ripple_(primary, $query: $query); } .mdc-chip--selected { @include feature-targeting.targets($feat-color) { @include theme.property(background-color, surface); } } } // Add leading checkmark to filter chips with no leading icon .mdc-chip__checkmark-svg { @include feature-targeting.targets($feat-structure) { width: 0; height: variables.$leading-icon-size; } @include feature-targeting.targets($feat-animation) { transition: width variables.$width-animation-duration animation-variables.$standard-curve-timing-function; } } .mdc-chip--selected .mdc-chip__checkmark-svg { @include feature-targeting.targets($feat-structure) { width: variables.$leading-icon-size; } } // Add leading checkmark to filter chips with a leading icon .mdc-chip-set--filter { .mdc-chip__icon--leading { @include feature-targeting.targets($feat-animation) { transition: opacity variables.$opacity-animation-duration linear; transition-delay: variables.$leading-icon-delay; } @include feature-targeting.targets($feat-structure) { opacity: 1; } + .mdc-chip__checkmark { @include feature-targeting.targets($feat-animation) { transition: opacity variables.$opacity-animation-duration linear; // Delay the checkmark transition. transition-delay: variables.$checkmark-with-leading-icon-delay; } @include feature-targeting.targets($feat-structure) { opacity: 0; } .mdc-chip__checkmark-svg { @include feature-targeting.targets($feat-animation) { transition: width 0ms; } } } } .mdc-chip--selected .mdc-chip__icon--leading { @include feature-targeting.targets($feat-structure) { opacity: 0; } + .mdc-chip__checkmark { @include feature-targeting.targets($feat-structure) { // This ensures that the checkmark has zero width while the leading icon is still animating. width: 0; opacity: 1; } } } .mdc-chip__icon--leading-hidden.mdc-chip__icon--leading { @include feature-targeting.targets($feat-structure) { width: 0; } @include feature-targeting.targets($feat-structure) { // This ensures that the leading icon doesn't fade in while the checkmark is fading out. opacity: 0; } + .mdc-chip__checkmark { @include feature-targeting.targets($feat-structure) { width: variables.$leading-icon-size; } } } } } @mixin ripple($query: feature-targeting.all()) { @include ripple.common($query); // COPYBARA_COMMENT_THIS_LINE $feat-structure: feature-targeting.create-target($query, structure); .mdc-chip { @include ripple.surface($query: $query, $ripple-target: $ripple-target); @include ripple.radius-bounded( $query: $query, $ripple-target: $ripple-target ); @include ink-color-ripple_(variables.$ink-color-default, $query: $query); #{$ripple-target} { @include ripple.target-common($query: $query); @include feature-targeting.targets($feat-structure) { overflow: hidden; } } } .mdc-chip-set--choice { .mdc-chip { @include selected-ink-color-ripple_(primary, $query: $query); } } } @mixin set-core-styles($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) { @keyframes mdc-chip-entry { from { transform: scale(0.8); opacity: 0.4; } to { transform: scale(1); opacity: 1; } } } .mdc-chip-set { @include set-spacing(8px, $query: $query); @include feature-targeting.targets($feat-structure) { display: flex; flex-wrap: wrap; box-sizing: border-box; } } .mdc-chip-set--input .mdc-chip { @include feature-targeting.targets($feat-animation) { animation: mdc-chip-entry 100ms animation-variables.$deceleration-curve-timing-function; } } } @mixin shape-radius( $radius, $rtl-reflexive: false, $density-scale: variables.$density-scale, $query: feature-targeting.all() ) { $height: density-functions.prop-value( $density-config: variables.$density-config, $density-scale: $density-scale, $property-name: height, ); @include shape-mixins.radius( $radius, $rtl-reflexive, $component-height: $height, $query: $query ); #{$ripple-target} { @include shape-mixins.radius( $radius, $rtl-reflexive, $component-height: $height, $query: $query ); } } @mixin fill-color-accessible($color, $query: feature-targeting.all()) { $fill-tone: theme-color.tone($color); @include fill-color($color, $query: $query); @if ($fill-tone == 'dark') { @include ink-color(text-primary-on-dark, $query: $query); @include selected-ink-color(text-primary-on-dark, $query: $query); @include leading-icon-color(text-primary-on-dark, $query: $query); @include trailing-icon-color(text-primary-on-dark, $query: $query); } @else { @include ink-color(text-primary-on-light, $query: $query); @include selected-ink-color(text-primary-on-light, $query: $query); @include leading-icon-color(text-primary-on-light, $query: $query); @include trailing-icon-color(text-primary-on-light, $query: $query); } } @mixin fill-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 ink-color($color, $query: feature-targeting.all()) { @include ink-color-ripple_($color, $query: $query); @include ink-color-without-ripple_($color, $query: $query); } @mixin ink-color-without-ripple_($color, $query) { $feat-color: feature-targeting.create-target($query, color); @include feature-targeting.targets($feat-color) { @include theme.property(color, $color); } &:hover { @include feature-targeting.targets($feat-color) { @include theme.property(color, $color); } } } @mixin ink-color-ripple_($color, $query) { @include ripple-theme.states( $color, true, $query: $query, $ripple-target: $ripple-target ); } @mixin selected-ink-color($color, $query: feature-targeting.all()) { @include selected-ink-color-ripple_($color, $query: $query); @include selected-ink-color-without-ripple_($color, $query: $query); } @mixin selected-ink-color-without-ripple_($color, $query) { $feat-color: feature-targeting.create-target($query, color); &.mdc-chip--selected { @include feature-targeting.targets($feat-color) { @include theme.property(color, $color); } @include leading-icon-color($color, $query: $query); &:hover { @include feature-targeting.targets($feat-color) { @include theme.property(color, $color); } } } .mdc-chip__checkmark-path { @include feature-targeting.targets($feat-color) { @include theme.property(stroke, $color); } } } @mixin selected-ink-color-ripple_($color, $query) { &.mdc-chip { @include ripple-theme.states-selected( $color, $has-nested-focusable-element: true, $query: $query, $ripple-target: $ripple-target ); } } @mixin outline( $width: 1px, $style: solid, $color: theme-color.prop-value(on-surface), $query: feature-targeting.all() ) { @include outline-width($width, $query: $query); @include outline-style($style, $query: $query); @include outline-color($color, $query: $query); } @mixin outline-color($color, $query: feature-targeting.all()) { $feat-color: feature-targeting.create-target($query, color); @include feature-targeting.targets($feat-color) { @include theme.property(border-color, $color); } } @mixin outline-style($style, $query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { border-style: $style; } } @mixin outline-width( $width, $horizontal-padding: variables.$horizontal-padding, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); // Note: Adjust padding to maintain consistent width with non-outlined chips $horizontal-padding-value: math.max($horizontal-padding - $width, 0); @include feature-targeting.targets($feat-structure) { padding-right: $horizontal-padding-value; padding-left: $horizontal-padding-value; border-width: $width; } #{$ripple-target} { @include feature-targeting.targets($feat-structure) { top: -$width; left: -$width; border: $width solid transparent; } } } @mixin horizontal-padding($padding, $query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { padding-right: $padding; padding-left: $padding; } } @mixin height($height, $query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { height: $height; } } @mixin set-spacing($gap-size, $query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { padding: math.div($gap-size, 2); } .mdc-chip { @include feature-targeting.targets($feat-structure) { margin: math.div($gap-size, 2); } } .mdc-chip--touch { @include touch-target-mixins.margin( $component-height: variables.$height, $query: $query ); } } @mixin leading-icon-color( $color, $opacity: variables.$icon-opacity, $query: feature-targeting.all() ) { $feat-color: feature-targeting.create-target($query, color); .mdc-chip__icon--leading { @include feature-targeting.targets($feat-color) { color: rgba(theme-color.prop-value($color), $opacity); } } } @mixin trailing-icon-color( $color, $opacity: variables.$icon-opacity, $hover-opacity: variables.$trailing-icon-hover-opacity, $focus-opacity: variables.$trailing-icon-focus-opacity, $query: feature-targeting.all() ) { $feat-color: feature-targeting.create-target($query, color); @include trailing-action-mixins.color($color, $query: $query); // TODO(b/151980552): Remove the following block .mdc-chip__icon--trailing { @include feature-targeting.targets($feat-color) { color: rgba(theme-color.prop-value($color), $opacity); } &:hover { @include feature-targeting.targets($feat-color) { color: rgba(theme-color.prop-value($color), $hover-opacity); } } &:focus { @include feature-targeting.targets($feat-color) { color: rgba(theme-color.prop-value($color), $focus-opacity); } } } } // For customizing icon size, we need to increase specifity to ensure // overrides apply. Styles defined in the .material-icons CSS class are // loaded separately, so the order of CSS definitions is not guaranteed. @mixin leading-icon-size($size, $query: feature-targeting.all()) { .mdc-chip__icon.mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden) { @include icon-size_($size, $query: $query); } } @mixin trailing-icon-size($size, $query: feature-targeting.all()) { @include trailing-action-mixins.size($size, $query: $query); // TODO(b/151980552): Remove the following block .mdc-chip__icon.mdc-chip__icon--trailing { @include icon-size_($size, $query: $query); } } @mixin icon-size_($size, $query) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { width: $size; height: $size; font-size: $size; } } @mixin leading-icon-margin( $left-margin: variables.$leading-icon-margin-left, $right-margin: variables.$leading-icon-margin-right, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); &.mdc-chip--selected .mdc-chip__checkmark, .mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden) { @include feature-targeting.targets($feat-structure) { @include rtl.reflexive-property(margin, $left-margin, $right-margin); } } } @mixin trailing-icon-margin( $left-margin: variables.$trailing-icon-margin-left, $right-margin: variables.$trailing-icon-margin-right, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); @include trailing-action-mixins.horizontal-spacing( $left-margin, $right-margin, $query: $query ); // TODO(b/151980552): Remove the following block .mdc-chip__icon--trailing { @include feature-targeting.targets($feat-structure) { @include rtl.reflexive-property(margin, $left-margin, $right-margin); } } } @mixin elevation-transition($query: feature-targeting.all()) { $feat-animation: feature-targeting.create-target($query, animation); @include feature-targeting.targets($feat-animation) { transition: elevation-functions.transition-value(); } &.mdc-chip--exit { @include feature-targeting.targets($feat-animation) { transition: elevation-functions.transition-value(), variables.$exit-transition; } } } /// /// Sets the density scale for chips. /// /// @param {Number | String} $density-scale - Density scale value for component. /// Supported density scale values are `-2`, `-1`, `0`. /// @mixin density($density-scale, $query: feature-targeting.all()) { $height: density-functions.prop-value( $density-config: variables.$density-config, $density-scale: $density-scale, $property-name: height, ); @include height($height, $query: $query); @if $density-scale != 0 { @include touch-target-reset_($query: $query); } } /// /// Resets touch target-related styles. This is called from the density mixin to /// automatically remove the increased touch target, since dense components /// don't have the same default a11y requirements. /// @access private /// @mixin touch-target-reset_($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); // Selector is necessary here to override original specificity. &.mdc-chip--touch { @include feature-targeting.targets($feat-structure) { margin-top: 0; margin-bottom: 0; } } .mdc-chip__touch { @include feature-targeting.targets($feat-structure) { display: none; } } } @mixin high-contrast-focus { // High contrast mode focus for chips. .mdc-chip__focus-ring { display: none; } @include ripple-theme.focus() { .mdc-chip__focus-ring { z-index: 1; display: block; @include focus-ring.focus-ring(); } } }