// // Copyright 2023 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:list'; @use 'sass:meta'; @use '@material/theme/custom-properties'; @use '@material/theme/theme'; @use '@material/shape/functions' as shape-functions; @use '@material/shape/mixins' as shape-mixins; @use '@material/floating-label/mixins' as floating-label-mixins; @use '@material/feature-targeting/feature-targeting'; @use '@material/rtl/rtl'; // Keep this in sync with constants.numbers.MIN_LEADING_STROKE_EDGE_POSITION $min-leading-stroke-edge-position: 12px !default; // The gap between the stroke end and floating label // Keep this in sync with constants.numbers.NOTCH_GUTTER_SIZE $notch-gutter-size: 4px !default; $border-width: 1px !default; $leading-width: 12px !default; $padding: 4px !default; // This variable keeps the before/after JS label centered in the notch when the font-size is changed. $label-adjust: 14% !default; /// Label box height when it is floating above for notched upgraded. This value is used to put the label vertically in /// the middle when it is notched. $label-box-height: 13.5px !default; /// Label adjust offset applied to floating label when it is notched. Since notch without upgraded has different font /// size we add additional offset value. $label-adjust-absolute: 2.5px !default; @mixin theme-styles($query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); .mdc-notched-outline { &__leading, &__notch, &__trailing { @include feature-targeting.targets($feat-structure) { border-top: $border-width solid; border-bottom: $border-width solid; } } &__leading { @include feature-targeting.targets($feat-structure) { @include rtl.reflexive-property(border, $border-width solid, none); width: $leading-width; } } &__trailing { @include feature-targeting.targets($feat-structure) { @include rtl.reflexive-property(border, none, $border-width solid); } } &__notch { @include feature-targeting.targets($feat-structure) { max-width: calc(100% - #{$leading-width} * 2); } } } } @mixin color($color, $query: feature-targeting.all()) { $feat-color: feature-targeting.create-target($query, color); .mdc-notched-outline__leading, .mdc-notched-outline__notch, .mdc-notched-outline__trailing { @include feature-targeting.targets($feat-color) { @include theme.property(border-color, $color); } } } @mixin stroke-width($width, $query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); .mdc-notched-outline__leading, .mdc-notched-outline__notch, .mdc-notched-outline__trailing { @include feature-targeting.targets($feat-structure) { @include theme.property(border-width, $width); } } } /// /// Adds top offset to compensate for border width box size when it is notched. /// Use this when floating label is aligned to center to prevent label jump on focus. /// @param {Number} $stroke-width Stroke width of notched outline that needs to be offset. /// @mixin notch-offset($stroke-width, $query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); .mdc-notched-outline--notched .mdc-notched-outline__notch { @include feature-targeting.targets($feat-structure) { padding-top: $stroke-width; } } } @mixin shape-radius( $radius, $rtl-reflexive: false, $component-height: null, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); // Resolve the radius relative to the notched outline component's height. The // component should provide its current height from its density. The resolved // radius allows percentage radii to be converted to pixels. $resolved-radius: shape-functions.resolve-radius( $radius, $component-height: $component-height ); // Grab the top-left radius. We'll need it to adjust the leading for the // label notch if the resulting radius shape is larger than the default // leading. $top-left-radius: list.nth( shape-functions.unpack-radius($resolved-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); } .mdc-notched-outline__leading { // mask the leading to apply the top-left and bottom-left corners @include shape-mixins.radius( shape-functions.mask-radius($radius, 1 0 0 1), $rtl-reflexive: true, $component-height: $component-height, $query: $query ); @include feature-targeting.targets($feat-structure) { @if ($top-left-radius-px > $leading-width) { // If the radius is bigger than the default leading width, we need to // increase the leading width width: $top-left-radius-px; @if ($top-left-is-custom-prop) { // The radius may be a custom property, in which case the above width // is the IE11 fallback value. /* @alternate */ } } @if ($top-left-is-custom-prop) { // If the top-left radius is dynamic, the width of the leading is // the max of whichever is larger: the default leading width or the // value of the custom property. $var: custom-properties.create-var($top-left-radius); // Interpolation is a workaround for sass/sass#3259. @supports (top: max(#{0%})) { width: max(#{$leading-width}, #{$var}); } } } } // Similar to above, adjust the max-width of the notch if we adjusted the // leading's width. .mdc-notched-outline__notch { @include feature-targeting.targets($feat-structure) { @if ($top-left-radius-px > $leading-width) { max-width: calc(100% - #{$top-left-radius-px} * 2); @if ($top-left-is-custom-prop) { /* @alternate */ } } @if ($top-left-is-custom-prop) { $var: custom-properties.create-var($top-left-radius); // Interpolation is a workaround for sass/sass#3259. @supports (top: max(#{0%})) { max-width: calc(100% - max(#{$leading-width}, #{$var}) * 2); } } } } .mdc-notched-outline__trailing { // mask the leading to apply the top-right and bottom-right corners @include shape-mixins.radius( shape-functions.mask-radius($radius, 0 1 1 0), $rtl-reflexive: true, $component-height: $component-height, $query: $query ); } } @mixin floating-label-float-position( $positionY, $positionX: 0%, $scale: 0.75, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); @include floating-label-mixins.float-position( $positionY + $label-adjust, $positionX, 1, $query: $query ); .mdc-floating-label--float-above { @include feature-targeting.targets($feat-structure) { font-size: ($scale * 1rem); } } // Two selectors to ensure we select the appropriate class when applied from this component or a parent component. &.mdc-notched-outline--upgraded, .mdc-notched-outline--upgraded { @include floating-label-mixins.float-position( $positionY, $positionX, $scale, $query: $query ); .mdc-floating-label--float-above { @include feature-targeting.targets($feat-structure) { font-size: 1rem; } } } } /// /// Sets floating label position in notched outline when label is afloat. /// /// @param {Number} $positionY Absolute Y-axis position in `px`. /// @param {Number} $positionX Absolute X-axis position in `px`. Defaults to `0`. /// @param {Number} $scale Defaults to `.75`. /// /// @todo Replace mixin `mdc-notched-outline-floating-label-float-position` with this mixin when floating label is /// center aligned in all the places. /// @mixin floating-label-float-position-absolute( $positionY, $positionX: 0, $scale: 0.75, $query: feature-targeting.all() ) { $feat-structure: feature-targeting.create-target($query, structure); $float-positionY: if( meta.type-of($positionY) == 'calculation', calc($positionY + $label-adjust-absolute), $positionY + $label-adjust-absolute ); @include floating-label-mixins.float-position( $float-positionY, $positionX, $scale: 1, $query: $query ); .mdc-floating-label--float-above { @include feature-targeting.targets($feat-structure) { font-size: ($scale * 1rem); } } // Two selectors to ensure we select the appropriate class when applied from this component or a parent component. &.mdc-notched-outline--upgraded, .mdc-notched-outline--upgraded { @include floating-label-mixins.float-position( $positionY, $positionX, $scale, $query: $query ); .mdc-floating-label--float-above { @include feature-targeting.targets($feat-structure) { font-size: 1rem; } } } } /// /// Sets the max-width for the notch /// /// @param {Number} $max-width Max-width for the notch /// @mixin notch-max-width($max-width, $query: feature-targeting.all()) { $feat-structure: feature-targeting.create-target($query, structure); :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch { @include feature-targeting.targets($feat-structure) { max-width: $max-width; } } }