// // 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 'sass:list'; @use 'sass:meta'; @use 'sass:map'; @use '@material/animation/animation'; @use '@material/theme/css'; @use '@material/density/functions' as density-functions; @use '@material/dom/dom'; @use '@material/floating-label/mixins' as floating-label-mixins; @use '@material/floating-label/variables' as floating-label-variables; @use '@material/line-ripple/mixins' as line-ripple-mixins; @use '@material/notched-outline/mixins' as notched-outline-mixins; @use '@material/notched-outline/variables' as notched-outline-variables; @use '@material/ripple/ripple'; @use '@material/ripple/ripple-theme'; @use '@material/theme/custom-properties'; @use '@material/theme/theme'; @use '@material/shape/mixins' as shape-mixins; @use '@material/shape/functions' as shape-functions; @use '@material/feature-targeting/feature-targeting'; @use '@material/typography/typography'; @use 'helper-text/mixins' as helper-text-mixins; @use 'character-counter/mixins' as character-counter-mixins; @use 'icon/mixins' as icon-mixins; @use 'icon/variables' as icon-variables; @use './variables'; @use '@material/rtl/rtl'; $_density-config: map.merge( variables.$density-config, ( minimum: math.min(variables.$minimum-height, 36px), ) ); @mixin core-styles($query: feature-targeting.all()) { @include ripple($query); @include static-styles($query); @include helper-text-mixins.helper-text-core-styles($query); @include character-counter-mixins.character-counter-core-styles($query); @include icon-mixins.icon-core-styles($query); } /// @deprecated Use static-styles() instead. @mixin without-ripple($query: feature-targeting.all()) { @include static-styles($query); } @mixin static-styles($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); // Baseline // postcss-bem-linter: define text-field .mdc-text-field { @include _base($query); } .mdc-text-field__input { @include _input($query); @include placeholder-selector_ { @include _input-placeholder($query); } // Always show placeholder for text fields that has no // label and show only on focused state when label is present. .mdc-text-field--no-label &, .mdc-text-field--focused & { @include placeholder-selector_ { @include _input-placeholder-visible($query); } } } .mdc-text-field__affix { @include _affix($query: $query); .mdc-text-field--label-floating &, .mdc-text-field--no-label & { @include _affix-visible($query: $query); } // Safari only @supports (-webkit-hyphens: none) { .mdc-text-field--outlined & { @include _centered-affix-safari-support($query: $query); } } } .mdc-text-field__affix--prefix { @include _prefix($query: $query); .mdc-text-field--end-aligned & { @include _prefix-end-aligned($query: $query); } } .mdc-text-field__affix--suffix { @include _suffix($query: $query); .mdc-text-field--end-aligned & { @include _suffix-end-aligned($query: $query); } } // Variants .mdc-text-field--filled { @include _filled($query); &.mdc-text-field--no-label { @include filled-no-label($query); } } .mdc-text-field--outlined { @include outlined_($query); .mdc-notched-outline { @include _outlined-notched-outline($query); } } // Other Variations .mdc-text-field--textarea { @include textarea_($query); .mdc-text-field__input { @include _textarea-input($query); } &.mdc-text-field--filled { @include _textarea-filled($query); .mdc-text-field__input { @include _textarea-filled-input($query); } &.mdc-text-field--no-label { .mdc-text-field__input { @include _textarea-filled-no-label-input($query); } } } &.mdc-text-field--outlined { @include _textarea-outlined($query); .mdc-text-field__input { @include _textarea-outlined-input($query); } .mdc-floating-label { @include _textarea-outlined-floating-label($query); } } &.mdc-text-field--with-internal-counter { .mdc-text-field__input { @include _textarea-input-with-internal-counter($query); } .mdc-text-field-character-counter { @include _textarea-internal-counter($query); } } } // Resizer element does not need to be under mdc-text-field--textarea, that // just adds specificity .mdc-text-field__resizer { @include _textarea-resizer($query); .mdc-text-field--filled & { @include _textarea-filled-resizer($query); .mdc-text-field__input, .mdc-text-field-character-counter { @include _textarea-filled-resizer-children($query); } } .mdc-text-field--outlined & { @include _textarea-outlined-resizer($query); .mdc-text-field__input, .mdc-text-field-character-counter { @include _textarea-outlined-resizer-children($query); } } } .mdc-text-field--with-leading-icon { @include _padding-horizontal-with-leading-icon($query); &.mdc-text-field--filled { @include with-leading-icon_($query); } &.mdc-text-field--outlined { @include outlined-with-leading-icon_($query); } } .mdc-text-field--with-trailing-icon { @include _padding-horizontal-with-trailing-icon($query); &.mdc-text-field--filled { @include _with-trailing-icon($query); } &.mdc-text-field--outlined { @include _outlined-with-trailing-icon($query); } } .mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon { @include _padding-horizontal-with-both-icons($query); &.mdc-text-field--filled { @include _with-leading-and-trailing-icon($query); } } // postcss-bem-linter: define text-field-helper-text .mdc-text-field-helper-line { @include feature-targeting.targets($feat-structure) { display: flex; justify-content: space-between; box-sizing: border-box; } .mdc-text-field + & { @include feature-targeting.targets($feat-structure) { padding-right: variables.$helper-line-padding; padding-left: variables.$helper-line-padding; } } } // postcss-bem-linter: end // mdc-form-field tweaks to align text field label correctly // stylelint-disable selector-max-type -- // TODO: document why this disable is neccessary .mdc-form-field > .mdc-text-field + label { @include feature-targeting.targets($feat-structure) { align-self: flex-start; } } // stylelint-enable selector-max-type // States .mdc-text-field--focused { @include focused_($query); &.mdc-text-field--outlined { @include _focused-outlined($query); &.mdc-text-field--textarea { @include _focused-outlined-textarea($query); } } } .mdc-text-field--invalid { @include invalid_($query); } .mdc-text-field--disabled { @include disabled_($query); &.mdc-text-field--filled { @include _disabled-filled($query); } .mdc-text-field__input { @include _disabled-input($query); } } .mdc-text-field--end-aligned { @include end-aligned_($query); } .mdc-text-field--ltr-text { @include _ltr-text($query); &.mdc-text-field--end-aligned { @include _ltr-text-end-aligned($query); } } } // This API is intended for use by frameworks that may want to separate the ripple-related styles // from the other text field styles. It is recommended that most users use `mdc-text-field-core-styles` instead. @mixin ripple($query: feature-targeting.all()) { @include ripple.common($query); // COPYBARA_COMMENT_THIS_LINE .mdc-text-field--filled { @include ripple.surface( $query: $query, $ripple-target: variables.$ripple-target ); @include ripple.radius-bounded( $query: $query, $ripple-target: variables.$ripple-target ); } #{variables.$ripple-target} { @include ripple.target-common($query: $query); } } /// /// Sets density scale for default text field variant. /// /// @param {Number | String} $density-scale - Density scale value for component. Supported density scale values `-4`, /// `-3`, `-2`, `-1`, `0`. Default is `0`. /// @param {Number} $minimum-height-for-filled-label Sets the minimum height for /// filled textfields at which to allow floating labels. /// @mixin density( $density-scale, $minimum-height-for-filled-label: variables.$minimum-height-for-filled-label, $query: feature-targeting.all() ) { $height: density-functions.prop-value( $density-config: $_density-config, $density-scale: $density-scale, $property-name: height, ); @include height( $height, $minimum-height-for-filled-label: $minimum-height-for-filled-label, $query: $query ); // TODO(b/151839219): resize icons and adjust label position // @if $density-scale < 0 { // @include icon-mixins.size(icon-variables.$dense-icon-size); // } } /// /// Sets density scale for outlined text field (Excluding outlined text field with leading icon). /// /// @param {Number | String} $density-scale - Density scale value for component. Supported density scale values `-4`, /// `-3`, `-2`, `-1`, `0`. Default is `0`. /// @mixin outlined-density($density-scale, $query: feature-targeting.all()) { $height: density-functions.prop-value( $density-config: $_density-config, $density-scale: $density-scale, $property-name: height, ); @include outlined-height($height, $query: $query); // TODO(b/151839219): resize icons and adjust label position // @if $density-scale < 0 { // @include icon-mixins.size(icon-variables.$dense-icon-size); // } } /// /// Sets density scale for outlined text field with leading icon. /// /// @param {Number | String} $density-scale - Density scale value for component. Supported density scale values `-4`, /// `-3`, `-2`, `-1`, `0`. Default is `0`. /// @mixin outlined-with-leading-icon-density( $density-scale, $query: feature-targeting.all() ) { $height: density-functions.prop-value( $density-config: $_density-config, $density-scale: $density-scale, $property-name: height, ); @include outlined-with-leading-icon-height($height, $query: $query); // TODO(b/151839219): resize icons and adjust label position // @if $density-scale < 0 { // @include icon-mixins.size(icon-variables.$dense-icon-size); // } } /// /// Sets density scale for filled textarea. /// /// @param {Number | String} $density-scale - Density scale value for component. Supported density scale values `-4`, /// `-3`, `-2`, `-1`, `0`. Default is `0`. /// @mixin filled-textarea-density( $density-scale, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); $textfield-height: density-functions.prop-value( $density-config: $_density-config, $density-scale: $density-scale, $property-name: height, ); $no-label-margin-top: density-functions.prop-value( $density-config: variables.$textarea-filled-no-label-density-config, $density-scale: math.div($density-scale, 2), $property-name: margin-top, ); $no-label-margin-bottom: density-functions.prop-value( $density-config: variables.$textarea-filled-no-label-density-config, $density-scale: math.div($density-scale, 2), $property-name: margin-bottom, ); // Textarea mixins require two modifier classes since two are used internally // for styles (textarea and filled). An extra class is added for the public // mixin so that only a single public class is needed for specificity. &.mdc-text-field--filled { .mdc-text-field__resizer { @include feature-targeting.targets($feat-structure) { min-height: $textfield-height; } } @if $density-scale >= -1 { $keyframe-suffix: text-field-filled-#{$density-scale}; $label-top: density-functions.prop-value( $density-config: variables.$textarea-filled-label-density-config, $density-scale: math.div($density-scale, 2), $property-name: top, ); // Adjust the floating position and animation/keyframes of the floating // label by the new position of the resting label $label-top-difference: variables.$textarea-outlined-label-top - $label-top; // Floating label position @include floating-label-mixins.float-position( variables.$textarea-filled-label-position-y - $label-top-difference, $query: $query ); // Floating label animation @include floating-label-mixins.shake-animation( $keyframe-suffix, $query: $query ); @at-root { @include floating-label-mixins.shake-keyframes( $keyframe-suffix, variables.$textarea-filled-label-position-y - $label-top-difference, 0%, $query: $query ); } // Resting label position .mdc-floating-label { @include feature-targeting.targets($feat-structure) { top: $label-top; } } $margin-bottom: density-functions.prop-value( $density-config: variables.$textarea-filled-density-config, $density-scale: $density-scale, $property-name: margin-bottom, ); .mdc-text-field__input { @include feature-targeting.targets($feat-structure) { margin-bottom: $margin-bottom; } } } @else { // The textarea is too dense to show a floating label .mdc-floating-label { @include feature-targeting.targets($feat-structure) { display: none; } } .mdc-text-field__input { @include feature-targeting.targets($feat-structure) { margin-top: $no-label-margin-top; margin-bottom: $no-label-margin-bottom; } } } &.mdc-text-field--no-label { .mdc-text-field__input { @include feature-targeting.targets($feat-structure) { margin-top: $no-label-margin-top; margin-bottom: $no-label-margin-bottom; } } } &.mdc-text-field--with-internal-counter { .mdc-text-field__input { // Space between textarea and internal counter should not be affected @include _textarea-input-with-internal-counter($query); } } } } /// /// Sets density scale for outlined textarea. /// /// @param {Number | String} $density-scale - Density scale value for component. Supported density scale values `-4`, /// `-3`, `-2`, `-1`, `0`. Default is `0`. /// @mixin outlined-textarea-density( $density-scale, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); $keyframe-suffix: text-field-outlined-#{$density-scale}; $label-top: density-functions.prop-value( $density-config: variables.$textarea-outlined-label-density-config, $density-scale: math.div($density-scale, 2), $property-name: top, ); $textfield-height: density-functions.prop-value( $density-config: $_density-config, $density-scale: $density-scale, $property-name: height, ); $margin-top: density-functions.prop-value( $density-config: variables.$textarea-outlined-density-config, $density-scale: math.div($density-scale, 2), $property-name: margin-top, ); $margin-bottom: density-functions.prop-value( $density-config: variables.$textarea-outlined-density-config, $density-scale: math.div($density-scale, 2), $property-name: margin-bottom, ); // Textarea mixins require two modifier classes since two are used internally // for styles (textarea and outlined). An extra class is added for the public // mixin so that only a single public class is needed for specificity. &.mdc-text-field--outlined { // Adjust the floating position and animation/keyframes of the floating // label by the new position of the resting label $label-top-difference: variables.$textarea-outlined-label-top - $label-top; // Floating label position @include notched-outline-mixins.floating-label-float-position-absolute( variables.$textarea-outlined-label-position-y - $label-top-difference, $query: $query ); // Floating label animation @include floating-label-mixins.shake-animation( $keyframe-suffix, $query: $query ); @at-root { @include floating-label-mixins.shake-keyframes( $keyframe-suffix, variables.$textarea-outlined-label-position-y - $label-top-difference, 0%, $query: $query ); } // Resting label position .mdc-floating-label { @include feature-targeting.targets($feat-structure) { top: $label-top; } } .mdc-text-field__resizer { @include feature-targeting.targets($feat-structure) { min-height: $textfield-height; } } .mdc-text-field__input { @include feature-targeting.targets($feat-structure) { margin-top: $margin-top; margin-bottom: $margin-bottom; } } &.mdc-text-field--with-internal-counter { .mdc-text-field__input { // Space between textarea and internal counter should not be affected @include _textarea-input-with-internal-counter($query); } } } } /// /// Sets the minimum number of rows for a textarea a textarea may be resized to. /// /// For IE11 this mixin can be used instead of the rows attribute. /// /// @param {Number} $rows - The minimum number of rows for a textarea. /// @param {Number} $line-height - The line-height of the textarea. /// @mixin textarea-min-rows( $rows, $line-height: variables.$textarea-line-height, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); .mdc-text-field__input { @include feature-targeting.targets($feat-structure) { min-height: $rows * $line-height; } } } /// /// Sets height of default text field variant. /// /// @param {Number} $height /// @param {Number} $minimum-height-for-filled-label Sets the minimum height for /// filled textfields at which to allow floating labels. /// @access public /// @mixin height( $height, $minimum-height-for-filled-label: variables.$minimum-height-for-filled-label, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { @include theme.property(height, $height); } // We can only hide the label (when there's not enough vertical space for it) // if we know the container height at compilation time. // That's not the case when $height is a custom property. @if not custom-properties.is-custom-prop($height) { @if $height < $minimum-height-for-filled-label { @include filled-no-label($query: $query); } } } /// /// Sets height of outlined text field variant (Excluding outlined text field with leading icon). /// /// @param {Number} $height /// @param {String} $keyframe-suffix - Optional suffix to use for generated /// floating label keyframes /// @access public /// @mixin outlined-height( $height, $keyframe-suffix: text-field-outlined-#{$height}, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); $positionY: variables.get-outlined-label-position-y($height); // Floating label position @include notched-outline-mixins.floating-label-float-position-absolute( $positionY, $query: $query ); // Floating label animation @include floating-label-mixins.shake-animation( $keyframe-suffix, $query: $query ); @at-root { @include floating-label-mixins.shake-keyframes( $keyframe-suffix, $positionY, $query: $query ); } @include feature-targeting.targets($feat-structure) { height: $height; } } /// /// Sets height of outlined text field with leading icon variant. /// /// @param {Number} $height /// @param {String} $keyframe-suffix - Optional suffix to use for generated /// floating label keyframes /// @access public /// @mixin outlined-with-leading-icon-height( $height, $keyframe-suffix: null, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); // This extra specificity is needed because textfield applies the below mixin // already to two selectors (outlined + with-leading-icon). To override // them with a new label position and animation, another selector is needed. &.mdc-text-field--outlined { @include _outlined-with-leading-icon-floating-label-position-animation( $height, $keyframe-suffix, $query ); } @include feature-targeting.targets($feat-structure) { height: $height; } } // Mixin that sets the floating label position and animations for a given height. // This mixin is separate to allow outlined-with-leading-icon-height() to // provide greater specificity over the default mixin that adds styles for // outlined with leading icons. @mixin _outlined-with-leading-icon-floating-label-position-animation( $height, $keyframe-suffix: text-field-outlined-with-leading-icon-#{$height}, $query: feature-targeting.all() ) { $positionY: variables.get-outlined-label-position-y($height); // Floating label position @include notched-outline-mixins.floating-label-float-position-absolute( $positionY, variables.$outlined-with-leading-icon-label-position-x, $query: $query ); // Floating label animation @include floating-label-mixins.shake-animation( $keyframe-suffix, $query: $query ); @at-root { @include floating-label-mixins.shake-keyframes( $keyframe-suffix, $positionY, variables.$outlined-with-leading-icon-label-position-x, $query: $query ); } $keyframe-suffix-rtl: #{$keyframe-suffix}-rtl; @include rtl.rtl { @include floating-label-mixins.shake-animation( $keyframe-suffix, $query: $query ); } @at-root { @include floating-label-mixins.shake-keyframes( $keyframe-suffix-rtl, $positionY, -(variables.$outlined-with-leading-icon-label-position-x), $query: $query ); } } /// /// Sets shape radius of default text field variant. /// /// @param {Number} $radius Shape radius value in `px` or in percentage. /// @param {Number} $text-field-height Height of default text field variant. Required only when `$radius` is in /// percentage unit and if text field has custom height. Defaults to `variables.$height`. /// @param {Boolean} $rtl-reflexive Set to true to flip shape radius in RTL context. Defaults to `false`. /// @mixin shape-radius( $radius, $density-scale: variables.$density-scale, $rtl-reflexive: false, $query: feature-targeting.all() ) { @if (meta.type-of($radius) == 'list') and (list.length($radius) > 2) and (list.nth($radius, 3) != 0 or list.nth($radius, 4) != 0) { @error "mdc-textfield: Invalid radius #{$radius}. Only top-left and top-right corners may be customized."; } $height: density-functions.prop-value( $density-config: $_density-config, $density-scale: $density-scale, $property-name: height, ); $masked-radius: shape-functions.mask-radius($radius, 1 1 0 0); $fallback: if( custom-properties.is-custom-prop($radius), custom-properties.get-fallback($radius), null ); @if meta.type-of($fallback) == 'list' { $fallback: css.unpack-value($fallback); $first: list.nth($masked-radius, 1); $second: list.nth($masked-radius, 2); $third: list.nth($masked-radius, 3); $fourth: list.nth($masked-radius, 4); $masked-radius: ( if( custom-properties.is-custom-prop($first), custom-properties.set-fallback($first, list.nth($fallback, 1)), $first ), if( custom-properties.is-custom-prop($second), custom-properties.set-fallback($second, list.nth($fallback, 2)), $second ), if( custom-properties.is-custom-prop($third), custom-properties.set-fallback($third, list.nth($fallback, 3)), $third ), if( custom-properties.is-custom-prop($fourth), custom-properties.set-fallback($fourth, list.nth($fallback, 4)), $fourth ) ); } @include shape-mixins.radius( $masked-radius, $rtl-reflexive, $component-height: $height, $query: $query ); } @mixin textarea-shape-radius( $radius, $rtl-reflexive: false, $query: feature-targeting.all() ) { @include notched-outline-mixins.shape-radius( $radius, $rtl-reflexive, $query: $query ); } /// /// Customizes the color of the text entered into an enabled text field. /// @param {Color} $color - The desired input text color. /// @mixin ink-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include ink-color_($color, $query: $query); } } /// /// Customizes the color of the entered text in a disabled text field. /// @param {Color} $color - The desired input text color. /// @mixin disabled-ink-color($color, $query: feature-targeting.all()) { @include if-disabled_ { @include ink-color_($color, $query: $query); } } /// /// Customizes the color of the placeholder in an enabled text field. /// @param {Color} $color - The desired placeholder text color. /// @mixin placeholder-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include placeholder-color_($color, $query: $query); } } /// /// Customizes the color of the placeholder in a disabled text field. /// @param {Color} $color - The desired placeholder text color. /// @mixin disabled-placeholder-color($color, $query: feature-targeting.all()) { @include if-disabled_ { @include placeholder-color_($color, $query: $query); } } /// /// Customizes the background color of the text field or textarea when enabled. /// @param {Color} $color - The desired background color. /// @mixin fill-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include fill-color_($color, $query: $query); } } /// /// Customizes the background color of the text field or textarea when disabled. /// @param {Color} $color - The desired background color. /// @mixin disabled-fill-color($color, $query: feature-targeting.all()) { @include if-disabled_ { @include fill-color_($color, $query: $query); } } /// /// Customizes the text field bottom line color for the filled variant. /// @param {Color} $color - The desired bottom line color. /// @mixin bottom-line-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include bottom-line-color_($color, $query: $query); } } /// /// Customizes the disabled text field bottom line color for the filled variant. /// @param {Color} $color - The desired bottom line color. /// @mixin disabled-bottom-line-color($color, $query: feature-targeting.all()) { @include if-disabled_ { @include bottom-line-color_($color, $query: $query); } } /// /// Customizes the hover text field bottom line color for the filled variant. /// @param {Color} $color - The desired bottom line color. /// @mixin hover-bottom-line-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include hover-bottom-line-color_($color, $query: $query); } } /// /// Customizes the color of the default line ripple of the text field. /// @param {Color} $color - The desired line ripple color. /// @mixin line-ripple-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include line-ripple-color_($color, $query: $query); } } /// /// Customizes the text color of the label in an enabled text field. /// @param {Color} $color - The desired label text color. /// @mixin label-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include label-ink-color_($color, $query: $query); } } /// /// Customizes the text color of the label in a disabled text field. /// @param {Color} $color - The desired label text color. /// @mixin disabled-label-color($color, $query: feature-targeting.all()) { @include if-disabled_ { @include label-ink-color_($color, $query: $query); } } /// /// Customizes the border color of the outlined text field or textarea. /// @param {Color} $color - The desired outline border color. /// @mixin outline-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include notched-outline-mixins.color($color, $query: $query); } } /// /// Customizes the outline border color when the text field or textarea is hovered. /// @param {Color} $color - The desired outline border color. /// @mixin hover-outline-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include hover-outline-color_($color, $query: $query); } } /// /// Customizes the outline border color when the text field or textarea is focused. /// @param {Color} $color - The desired outline border color. /// @mixin focused-outline-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include focused-outline-color_($color, $query: $query); } } /// /// Customizes the outline border color when the text field or textarea is disabled. /// @param {Color} $color - The desired outline border color. /// @mixin disabled-outline-color($color, $query: feature-targeting.all()) { @include if-disabled_ { @include notched-outline-mixins.color($color, $query: $query); } } /// /// Customizes the caret color of the text field or textarea. /// @param {Color} $color - The desired caret color. /// @mixin caret-color($color, $query: feature-targeting.all()) { $feat-color: feature-targeting.create-target($query, color); .mdc-text-field__input { @include feature-targeting.targets($feat-color) { @include theme.property(caret-color, $color); } } } /// /// Customizes the color of the prefix text for an enabled text field. /// @param {Color} $color - The desired prefix text color. /// @mixin prefix-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include _prefix-color($color, $query: $query); } } /// /// Customizes the color of the prefix text for a disabled text field. /// @param {Color} $color - The desired prefix text color. /// @mixin disabled-prefix-color($color, $query: feature-targeting.all()) { @include if-disabled_ { @include _prefix-color($color, $query: $query); } } /// /// Customizes the color of the suffix text for an enabled text field. /// @param {Color} $color - The desired suffix text color. /// @mixin suffix-color($color, $query: feature-targeting.all()) { @include if-enabled_ { @include _suffix-color($color, $query: $query); } } /// /// Customizes the color of the suffix text for a disabled text field. /// @param {Color} $color - The desired suffix text color. /// @mixin disabled-suffix-color($color, $query: feature-targeting.all()) { @include if-disabled_ { @include _suffix-color($color, $query: $query); } } /// /// Sets shape radius of outlined text field variant. /// /// @param {Number} $radius Shape radius value in `px` or in percentage. /// @param {Number} $text-field-height Height of outlined text field variant. Required only when `$radius` is in /// percentage unit and if text field has custom height. Defaults to `variables.$height`. /// @param {Boolean} $rtl-reflexive Set to true to flip shape radius in RTL context. Defaults to `false`. /// @mixin outline-shape-radius( $radius, $density-scale: variables.$density-scale, $rtl-reflexive: false, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); $height: density-functions.prop-value( $density-config: $_density-config, $density-scale: $density-scale, $property-name: height, ); .mdc-notched-outline { @include notched-outline-mixins.shape-radius( $radius, $rtl-reflexive, $component-height: $height, $query: $query ); } $resolved-radius: shape-functions.resolve-radius( $radius, $component-height: $height ); $unpacked-radius: shape-functions.unpack-radius($resolved-radius); $top-left-radius: list.nth($unpacked-radius, 1); $top-left-is-custom-prop: custom-properties.is-custom-prop($top-left-radius); $top-left-radius-px: $top-left-radius; @if ($top-left-is-custom-prop) { $top-left-radius-px: custom-properties.get-fallback($top-left-radius); } $top-right-radius: list.nth($unpacked-radius, 2); $top-right-is-custom-prop: custom-properties.is-custom-prop( $top-right-radius ); @if ( $top-left-is-custom-prop or $top-right-is-custom-prop or $top-left-radius-px > notched-outline-variables.$leading-width ) { // The horizontal padding only needs to be overriden from the base padding // if the radius is a custom property, or if the top-left radius is a value // that is large than that default notched outline's leading width. @include _outline-shape-radius-horizontal-padding( $top-left-radius, $top-right-radius, $query: $query ); + .mdc-text-field-helper-line { @include _outline-shape-radius-horizontal-padding( $top-left-radius, $top-right-radius, $query: $query ); } // Ensure that leading/trailing icon padding is overriden. Even if the // top left/right isn't a custom property or the leading isn't larger, we // still need to override. The above left/right padding rules have more // specificty than the original leading/trailing icon rules, so we need to // re-apply them. // Additionally, if the top left/right radii _are_ custom properties, we // should use those instead. &.mdc-text-field--with-leading-icon { @if ($top-right-is-custom-prop) { @include feature-targeting.targets($feat-structure) { @include rtl.ignore-next-line(); padding-left: 0; } @include _apply-outline-shape-padding( padding-right, $top-right-radius, $query: $query ); @include rtl.rtl { @include _apply-outline-shape-padding( padding-left, $top-right-radius, $query: $query ); @include feature-targeting.targets($feat-structure) { @include rtl.ignore-next-line(); padding-right: 0; } } } @else { @include _padding-horizontal-with-leading-icon($query); } } &.mdc-text-field--with-trailing-icon { @if ( $top-left-is-custom-prop or $top-left-radius-px > notched-outline-variables.$leading-width ) { @include _apply-outline-shape-padding( padding-left, $top-left-radius, $add-label-padding: true, $query: $query ); @include feature-targeting.targets($feat-structure) { @include rtl.ignore-next-line(); padding-right: 0; } @include rtl.rtl { @include feature-targeting.targets($feat-structure) { @include rtl.ignore-next-line(); padding-left: 0; } @include _apply-outline-shape-padding( padding-right, $top-left-radius, $add-label-padding: true, $query: $query ); } } @else { @include _padding-horizontal-with-trailing-icon($query); } } &.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon { @include _padding-horizontal-with-both-icons($query); } } } @mixin _outline-shape-radius-horizontal-padding( $top-left-radius, $top-right-radius, $query: feature-targeting.all() ) { @include _apply-outline-shape-padding( padding-left, $top-left-radius, $add-label-padding: true, $query: $query ); @include _apply-outline-shape-padding( padding-right, $top-right-radius, $query: $query ); $top-left-is-custom-prop: custom-properties.is-custom-prop($top-left-radius); $top-left-radius-px: $top-left-radius; @if ($top-left-is-custom-prop) { $top-left-radius-px: custom-properties.get-fallback($top-left-radius); } $top-right-is-custom-prop: custom-properties.is-custom-prop( $top-right-radius ); $top-right-radius-px: $top-right-radius; @if ($top-right-is-custom-prop) { $top-right-radius-px: custom-properties.get-fallback($top-right-radius); } @if ( ( $top-left-is-custom-prop and $top-right-is-custom-prop and not custom-properties.are-equal($top-left-radius, $top-right-radius) ) or $top-left-radius-px != $top-right-radius-px ) { // Normally base horizontal padding doesn't need RTL, but if the values // are different or they are two different custom properties, they need to // be reversed. @include rtl.rtl { @include _apply-outline-shape-padding( padding-right, $top-left-radius, $add-label-padding: true, $query: $query ); @include _apply-outline-shape-padding( padding-left, $top-right-radius, $query: $query ); } } } @mixin _apply-outline-shape-padding( $property, $padding, $add-label-padding: false, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); $padding-is-custom-prop: custom-properties.is-custom-prop($padding); $padding-px: $padding; @if ($padding-is-custom-prop) { $padding-px: custom-properties.get-fallback($padding); } @include feature-targeting.targets($feat-structure) { // The shape should only change the padding if the radius becomes greater // than the default padding. That means we need to add more padding. @if ($padding-px > variables.$padding-horizontal) { // Set a px value if it's greater. This is either the only value (if // we're given an exact value), or an IE11 fallback if we're given a // custom property and the fallback value is greater than the padding. $value: $padding-px; @if ($add-label-padding) { // If this is for the top-left leading, add the notched outline padding // to keep it aligned with the label $value: $padding-px + notched-outline-variables.$padding; } @include rtl.ignore-next-line(); #{$property}: $value; @if ($padding-is-custom-prop) { // Add an alternate GSS tag b/c this was an IE11 fallback and we're // going to add another property with the var() value /* @alternate */ } } @if ($padding-is-custom-prop) { // If it's a custom property, always add it since the value may change // to be greater than the padding at runtime, even if the fallback is // not currently greater than the default padding. $value: custom-properties.create-var($padding); @if ($add-label-padding) { $value: calc(#{$value} + #{notched-outline-variables.$padding}); } // Interpolation is a workaround for sass/sass#3259. @supports (top: max(#{0%})) { // A max() function makes this runtime dynamic. The padding will be // whichever is greater: the default horizontal padding, or the calculated // custom property plus extra padding. @include rtl.ignore-next-line(); #{$property}: max(#{variables.$padding-horizontal}, #{$value}); } } } } /// /// Sets the CSS transition for the floating label's 'float' animation. /// /// @param {Number} $duration-ms - Duration (in ms) of the animation. /// @param {String} $timing-function - Optionally overrides the default animation timing function. /// @mixin floating-label-float-transition( $duration-ms, $timing-function: null, $query: feature-targeting.all() ) { .mdc-floating-label { @include floating-label-mixins.float-transition( $duration-ms, $timing-function, $query: $query ); } } /// /// Sets custom font size of the input. /// /// @param {number} $font-size - Overrides the font size. /// @mixin input-font-size($font-size, $query: feature-targeting.all()) { $feat-typography: feature-targeting.create-target($query, typography); .mdc-text-field__input, .mdc-text-field__affix--suffix, .mdc-text-field__affix--prefix { @include feature-targeting.targets($feat-typography) { font-size: $font-size; } } } /// /// Sets custom font family of the input. /// /// @param {String} $font-family - Selected font family. /// @mixin input-font-family($font-family, $query: feature-targeting.all()) { $feat-typography: feature-targeting.create-target($query, typography); .mdc-text-field__input { @include feature-targeting.targets($feat-typography) { font-family: $font-family; } } } // Private mixins // Base shared styles @mixin _base($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); // Shape @include shape-radius(variables.$shape-radius, $query: $query); // Colors @include label-color(variables.$label, $query: $query); @include ink-color(variables.$ink-color, $query: $query); @include placeholder-color(variables.$placeholder-ink-color, $query: $query); @include caret-color(primary, $query: $query); @include helper-text-mixins.helper-text-color( variables.$helper-text-color, $query: $query ); @include character-counter-mixins.character-counter-color( variables.$helper-text-color, $query: $query ); @include icon-mixins.leading-icon-color( variables.$icon-color, $query: $query ); @include icon-mixins.trailing-icon-color( variables.$icon-color, $query: $query ); @include prefix-color(variables.$affix-color, $query: $query); @include suffix-color(variables.$affix-color, $query: $query); // Floating Label @include floating-label_($query); @include feature-targeting.targets($feat-structure) { // display and align-items are necessary to make the text field participate // in baseline alignment, even though some variants are 'centered'. Those // variants should use the _baseline-center-aligned() mixin display: inline-flex; align-items: baseline; padding: 0 variables.$padding-horizontal; position: relative; box-sizing: border-box; overflow: hidden; /* @alternate */ will-change: opacity, transform, color; } } // This mixin adds styles to visually center the text within the text field. // Sibling text will align to the baseline and appear centered next to the // text field. @mixin _baseline-center-aligned($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { // In order for a flexbox container to participate in baseline alignment, // it follows these rules to determine where its baseline is: // https://www.w3.org/TR/css-flexbox-1/#flex-baselines // // In order to avoid leading icons 'controlling' the baseline (since they // are the first child), flexbox will generate a baseline from any child // flex items that participate in baseline alignment. // // Icons are set to "align-self: center", while all other children are // aligned to baseline. The next problem is deciding which child is // used to determine the baseline. // // According to spec, the item with the largest distance between its // baseline and the edge of the cross axis is placed flush with that edge, // making it the baseline of the container. // https://www.w3.org/TR/css-flexbox-1/#baseline-participation // // For the filled variant, the pseudo ::before strut is the 'largest' // child since the input has a height of 28px and the strut is 40px. We // can emulate center alignment and force the baseline to use the input // text by making the input the full height of the container and removing // the baseline strut. // IE11 does not respect this, and makes the leading icon (if present) the // baseline. This is a gap with IE11 that we have accepted. .mdc-text-field__input { height: 100%; } } } @mixin _padding-horizontal-with-leading-icon($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { @include rtl.reflexive-property(padding, 0, variables.$padding-horizontal); } } @mixin _padding-horizontal-with-trailing-icon($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { @include rtl.reflexive-property(padding, variables.$padding-horizontal, 0); } } @mixin _padding-horizontal-with-both-icons($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { @include rtl.ignore-next-line(); padding-left: 0; @include rtl.ignore-next-line(); padding-right: 0; } } @mixin floating-label_($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); .mdc-floating-label { @include feature-targeting.targets($feat-structure) { top: 50%; transform: translateY(-50%); pointer-events: none; } } } // Filled @mixin _filled($query: feature-targeting.all()) { // Text Field intentionally omits press ripple, so each state needs to be specified individually. @include ripple-theme.states-base-color( variables.$ink-color, $query: $query, $ripple-target: variables.$ripple-target ); @include ripple-theme.states-hover-opacity( ripple-theme.states-opacity(variables.$ink-color, hover), $query: $query, $ripple-target: variables.$ripple-target ); @include ripple-theme.states-focus-opacity( ripple-theme.states-opacity(variables.$ink-color, focus), $query: $query, $ripple-target: variables.$ripple-target ); @include height(variables.$height, $query: $query); @include typography.baseline-top( variables.$filled-baseline-top, $query: $query ); @include fill-color(variables.$background, $query: $query); @include bottom-line-color(variables.$bottom-line-idle, $query: $query); @include hover-bottom-line-color( variables.$bottom-line-hover, $query: $query ); @include line-ripple-color_(primary, $query: $query); @include _filled-floating-label($query); } @mixin _filled-floating-label($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); .mdc-floating-label { @include feature-targeting.targets($feat-structure) { @include rtl.reflexive-position(left, variables.$label-offset); } } @include floating-label-mixins.float-position( variables.$label-position-y, $query: $query ); } // Filled variant with no label. This variant centers the text elements and // hides the label and is used with there is explicitly no label provided or // when the height of the text field is too small for a label to be allowed. @mixin filled-no-label($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include _baseline-center-aligned($query); @include feature-targeting.targets($feat-structure) { .mdc-floating-label { display: none; } &::before { // Remove baseline-top strut display: none; } } // Safari only @supports (-webkit-hyphens: none) { .mdc-text-field__affix { @include _centered-affix-safari-support($query: $query); } } } // Outlined @mixin outlined_($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include outlined-height( $height: variables.$height, $keyframe-suffix: text-field-outlined, $query: $query ); @include _baseline-center-aligned($query: $query); @include outline-color(variables.$outlined-idle-border, $query: $query); @include hover-outline-color( variables.$outlined-hover-border, $query: $query ); @include focused-outline-color(primary, $query: $query); @include outline-shape-radius(variables.$shape-radius, $query: $query); @include notched-outline-mixins.notch-offset( notched-outline-variables.$border-width, $query: $query ); @include ripple-theme.states-base-color( transparent, $query: $query, $ripple-target: variables.$ripple-target ); @include _outlined-floating-label($query); @include feature-targeting.targets($feat-structure) { overflow: visible; } .mdc-text-field__input { @include feature-targeting.targets($feat-structure) { // TODO(b/154349735): Investigate the neccessity of these styles display: flex; // stylelint-disable-next-line declaration-no-important -- // FF adds unwanted border in HC mode on windows. border: none !important; background-color: transparent; } } } @mixin _outlined-floating-label($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); .mdc-floating-label { @include feature-targeting.targets($feat-structure) { @include rtl.reflexive-position(left, notched-outline-variables.$padding); } } } @mixin _outlined-notched-outline($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { // Force the outline to appear "above" the textfield elements, even though // it is absolutely positioned and comes before the input in the DOM. This // is primarily for the textarea scrollbar and resize elements, which may // clip with with outline border. z-index: 1; } } // States @mixin disabled_($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include ink-color_(variables.$disabled-ink-color, $query: $query); @include placeholder-color_( variables.$disabled-placeholder-ink-color, $query: $query ); @include label-ink-color_(variables.$disabled-label-color, $query: $query); @include helper-text-mixins.helper-text-color_( variables.$disabled-helper-text-color, $query: $query ); @include character-counter-mixins.character-counter-color_( variables.$disabled-helper-text-color, $query: $query ); @include icon-mixins.leading-icon-color_( variables.$disabled-icon, $query: $query ); @include icon-mixins.trailing-icon-color_( variables.$disabled-icon, $query: $query ); @include _prefix-color(variables.$disabled-affix-color, $query: $query); @include _suffix-color(variables.$disabled-affix-color, $query: $query); // Mixins that are ok to include since they target variant-specific elements @include bottom-line-color_(variables.$disabled-border, $query: $query); @include notched-outline-mixins.color( variables.$outlined-disabled-border, $query: $query ); @include dom.forced-colors-mode { @include placeholder-color_(GrayText, $query: $query); @include label-ink-color_(GrayText, $query: $query); @include helper-text-mixins.helper-text-color_(GrayText, $query: $query); @include character-counter-mixins.character-counter-color_( GrayText, $query: $query ); @include icon-mixins.leading-icon-color_(GrayText, $query: $query); @include icon-mixins.trailing-icon-color_(GrayText, $query: $query); @include _prefix-color(GrayText, $query: $query); @include _suffix-color(GrayText, $query: $query); // Mixins that are ok to include since they target variant-specific elements @include bottom-line-color_(GrayText, $query: $query); @include notched-outline-mixins.color(GrayText, $query: $query); } @include dom.forced-colors-mode($exclude-ie11: true) { .mdc-text-field__input { @include feature-targeting.targets($feat-structure) { background-color: Window; } } .mdc-floating-label { @include feature-targeting.targets($feat-structure) { z-index: 1; } } } @include feature-targeting.targets($feat-structure) { pointer-events: none; } .mdc-floating-label { @include feature-targeting.targets($feat-structure) { cursor: default; } } } @mixin _disabled-input($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { // disabled inputs should still allow users to interact with them to select // text and scroll for textareas pointer-events: auto; } } @mixin _disabled-filled($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include fill-color_(variables.$disabled-background, $query: $query); #{variables.$ripple-target} { @include feature-targeting.targets($feat-structure) { // prevent ripple from displaying on hover when some interactible // elements like input and resize handles are hovered display: none; } } } @mixin invalid_($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include hover-bottom-line-color(variables.$error, $query: $query); @include line-ripple-color(variables.$error, $query: $query); @include label-color(variables.$error, $query: $query); @include helper-text-mixins.helper-text-validation-color( variables.$error, $query: $query ); @include caret-color(variables.$error, $query: $query); @include icon-mixins.trailing-icon-color(variables.$error, $query: $query); // Mixins that are ok to include since they target variant-specific elements @include bottom-line-color(variables.$error, $query: $query); @include outline-color(variables.$error, $query: $query); @include hover-outline-color(variables.$error, $query: $query); @include focused-outline-color(variables.$error, $query: $query); + .mdc-text-field-helper-line .mdc-text-field-helper-text--validation-msg { @include feature-targeting.targets($feat-structure) { opacity: 1; } } } @mixin focused_($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include label-color(variables.$focused-label-color, $query: $query); // Mixins that are ok to include since they target variant-specific elements @include notched-outline-mixins.stroke-width( variables.$outlined-stroke-width, $query: $query ); + .mdc-text-field-helper-line .mdc-text-field-helper-text:not( .mdc-text-field-helper-text--validation-msg ) { @include feature-targeting.targets($feat-structure) { opacity: 1; } } } @mixin _focused-outlined($query: feature-targeting.all()) { @include notched-outline-mixins.notch-offset( variables.$outlined-stroke-width, $query: $query ); } @mixin _focused-outlined-textarea($query: feature-targeting.all()) { @include notched-outline-mixins.notch-offset(0, $query: $query); } // Icons @mixin with-leading-icon_($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); $icon-padding: icon-variables.$leading-icon-padding-left + icon-variables.$icon-size + icon-variables.$leading-icon-padding-right; .mdc-floating-label { @include _truncate-floating-label-max-width($icon-padding, $query: $query); @include feature-targeting.targets($feat-structure) { @include rtl.reflexive-position(left, $icon-padding); } } $truncation: $icon-padding + variables.$padding-horizontal; .mdc-floating-label--float-above { @include _truncate-floating-label-floated-max-width( $truncation, $query: $query ); } } @mixin _with-trailing-icon($query: feature-targeting.all()) { $truncation: icon-variables.$trailing-icon-padding-left + icon-variables.$icon-size + icon-variables.$trailing-icon-padding-right + variables.$label-offset; .mdc-floating-label { @include _truncate-floating-label-max-width($truncation, $query: $query); } .mdc-floating-label--float-above { @include _truncate-floating-label-floated-max-width( $truncation, $query: $query ); } } @mixin _with-leading-and-trailing-icon($query: feature-targeting.all()) { $leading-icon: icon-variables.$leading-icon-padding-left + icon-variables.$icon-size + icon-variables.$leading-icon-padding-right; $trailing-icon: icon-variables.$trailing-icon-padding-left + icon-variables.$icon-size + icon-variables.$trailing-icon-padding-right; $truncation: $leading-icon + $trailing-icon; .mdc-floating-label { @include _truncate-floating-label-max-width($truncation, $query: $query); } .mdc-floating-label--float-above { @include _truncate-floating-label-floated-max-width( $truncation, $query: $query ); } } @mixin outlined-with-leading-icon_($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); // Resting label position $icon-padding: icon-variables.$leading-icon-padding-left + icon-variables.$icon-size + icon-variables.$leading-icon-padding-right; $left-spacing: $icon-padding - notched-outline-variables.$leading-width; .mdc-floating-label { @include feature-targeting.targets($feat-structure) { @include rtl.reflexive-position(left, $left-spacing); } } // Notch width $notch-truncation: $icon-padding + notched-outline-variables.$leading-width; @include _truncate-notched-outline-max-width( $notch-truncation, $query: $query ); // Floating label position and animation @include _outlined-with-leading-icon-floating-label-position-animation( $height: variables.$height, $keyframe-suffix: text-field-outlined-leading-icon, $query: $query ); } /// /// Applied to the outlined text field with a trailing icon /// @mixin _outlined-with-trailing-icon($query: feature-targeting.all()) { // Resting label position $icon-padding: icon-variables.$trailing-icon-padding-left + icon-variables.$icon-size + icon-variables.$trailing-icon-padding-right; // Notch width $notch-truncation: $icon-padding + notched-outline-variables.$leading-width; @include _truncate-notched-outline-max-width( $notch-truncation, $query: $query ); } /// /// Truncates the max-width of the notched outline by the given amount /// /// @param {Number} $truncation - Amount to truncate the notched outline max-width /// @mixin _truncate-notched-outline-max-width( $truncation, $query: feature-targeting.all() ) { @include notched-outline-mixins.notch-max-width( calc(100% - #{$truncation}), $query: $query ); } /// /// Truncates the max-width of the floating label by the given amount /// /// @param {Number} $truncation - Amount to truncate the floating label max-width /// @mixin _truncate-floating-label-max-width( $truncation, $query: feature-targeting.all() ) { @include floating-label-mixins.max-width( calc(100% - #{$truncation}), $query: $query ); } /// /// Truncates the max-width of the floating label by the given amount while scaling by the given scale value /// /// @param {Number} $truncation - Amount to truncate the floating label max-width /// @mixin _truncate-floating-label-floated-max-width( $truncation, $query: feature-targeting.all() ) { $scale: floating-label-variables.$float-scale; @include floating-label-mixins.max-width( calc(100% / #{$scale} - #{$truncation} / #{$scale}), $query: $query ); } // Textarea @mixin textarea_($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); $feat-animation: feature-targeting.create-target($query, animation); @include _textarea-floating-label($query); @include feature-targeting.targets($feat-structure) { flex-direction: column; align-items: center; width: auto; height: auto; padding: 0; // see below for explanation } @include feature-targeting.targets($feat-animation) { transition: none; } } @mixin _textarea-resizer($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { align-self: stretch; display: inline-flex; flex-direction: column; flex-grow: 1; max-height: 100%; max-width: 100%; min-height: variables.$height; // 'stretch' is the preferred rule here. It will allow the textarea to grow // to the min/max width of the container, but if an explicit width is set, // it cannot be resized horizontally. // Stretch is still a working draft. Chrome and Firefox have it implemented // with 'available' prefixes. fit-content is another good target for // Safari since it works in almost all use cases except when an explicit // width is set (the user can make the textarea smaller than the container). // None of this matters for IE11, which doesn't support resize. min-width: fit-content; /* @alternate */ min-width: -moz-available; /* @alternate */ min-width: -webkit-fill-available; overflow: hidden; resize: both; } } @mixin _textarea-filled-resizer($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); // Shift the resizer element up by a margin amount to make space for the // resize handle. For filled elements, the resize handle directly touches // the bottom line and is hard to see. // Using a margin affects the width and positioning of the overall component // and underlying textarea, which is why a transform is used instead. $y: -1 * variables.$textarea-input-handle-margin; @include feature-targeting.targets($feat-structure) { transform: translateY($y); } } @mixin _textarea-filled-resizer-children($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); // See above. After shifting the resize wrapper element, all of its children // should be shifted in the opposite direction (down) to compensate. $y: variables.$textarea-input-handle-margin; @include feature-targeting.targets($feat-structure) { transform: translateY($y); } } @mixin _textarea-outlined-resizer($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); // Shift the resizer element left/up by a margin amount to make space for the // resize handle. For outlined elements, the resize handle directly touches // the outline and is hard to see. // Using a margin affects the width and positioning of the overall component // and underlying textarea, which is why a transform is used instead. $x: -1 * variables.$textarea-input-handle-margin; $y: -1 * variables.$textarea-input-handle-margin; @include feature-targeting.targets($feat-structure) { @include rtl.ignore-next-line(); transform: translateX($x) translateY($y); @include rtl.rtl { // Flip the horizontal shifting direction for RTL @include rtl.ignore-next-line(); transform: translateX(-1 * $x) translateY($y); } } } @mixin _textarea-outlined-resizer-children($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); // See above. After shifting the resize wrapper element, all of its children // should be shifted in the opposite direction (right and down) to compensate. $x: variables.$textarea-input-handle-margin; $y: variables.$textarea-input-handle-margin; @include feature-targeting.targets($feat-structure) { @include rtl.ignore-next-line(); transform: translateX($x) translateY($y); @include rtl.rtl { // Flip the horizontal shifting direction for RTL @include rtl.ignore-next-line(); transform: translateX(-1 * $x) translateY($y); } } } @mixin _textarea-floating-label($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); // Resting label position .mdc-floating-label { @include feature-targeting.targets($feat-structure) { top: variables.$textarea-label-top; } // Resets center aligning the floating label. &:not(.mdc-floating-label--float-above) { @include feature-targeting.targets($feat-structure) { transform: none; } } } } @mixin _textarea-input($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); $feat-typography: feature-targeting.create-target($query, typography); @include feature-targeting.targets($feat-structure) { flex-grow: 1; height: auto; min-height: variables.$textarea-line-height; overflow-x: hidden; // https://bugzilla.mozilla.org/show_bug.cgi?id=33654 overflow-y: auto; box-sizing: border-box; resize: none; // Textarea has horizontal padding instead of the container. This allows the // resize handle to extend to the edge of the container. padding: 0 variables.$padding-horizontal; } @include feature-targeting.targets($feat-typography) { line-height: variables.$textarea-line-height; } } @mixin _textarea-internal-counter($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include typography.baseline-bottom( variables.$textarea-internal-counter-baseline-bottom, $query: $query ); @include feature-targeting.targets($feat-structure) { align-self: flex-end; // Needed since padding is on the textarea and not the container padding: 0 variables.$padding-horizontal; &::before { // Remove baseline-top display: none; } } } @mixin _textarea-input-with-internal-counter($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { margin-bottom: variables.$textarea-internal-counter-input-margin-bottom; } } @mixin _textarea-filled($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); @include feature-targeting.targets($feat-structure) { &::before { //