From eb093eb8198942f36c2f9694328bf8dc6609ca98 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 10 Jul 2024 20:06:22 +0000 Subject: [PATCH] refactor(material/form-field): simplify structural styles (#29400) Reworks the form field styles to make them smaller and easier to mainta. (cherry picked from commit d3a95ab8e9b1fc706d88e34b5b0cebf2b089237c) --- .../core/tokens/m2/mat/_form-field.scss | 5 +- .../core/tokens/m2/mat/_paginator.scss | 3 +- src/material/form-field/BUILD.bazel | 1 + .../form-field/_form-field-subscript.scss | 4 +- .../form-field/_form-field-theme.scss | 40 +- .../_mdc-text-field-density-overrides.scss | 4 +- .../_mdc-text-field-structure-overrides.scss | 15 +- .../form-field/_mdc-text-field-structure.scss | 610 ++++++++++++++++++ .../directives/notched-outline.html | 6 +- src/material/form-field/form-field.scss | 45 +- 10 files changed, 645 insertions(+), 88 deletions(-) create mode 100644 src/material/form-field/_mdc-text-field-structure.scss diff --git a/src/material/core/tokens/m2/mat/_form-field.scss b/src/material/core/tokens/m2/mat/_form-field.scss index bfee8de634c8..5ea5f1e5f496 100644 --- a/src/material/core/tokens/m2/mat/_form-field.scss +++ b/src/material/core/tokens/m2/mat/_form-field.scss @@ -1,6 +1,5 @@ @use 'sass:math'; @use 'sass:map'; -@use '@material/textfield' as mdc-textfield; @use '../../token-utils'; @use '../../../style/sass-utils'; @use '../../../theming/theming'; @@ -114,14 +113,14 @@ $prefix: (mat, form-field); -5: 36px, ); $height: map.get($size-scale, $density-scale); - $hide-label: $height < mdc-textfield.$minimum-height-for-filled-label; + $hide-label: $height < 52px; // We computed the desired height of the form-field using the density configuration. The // spec only describes vertical spacing/alignment in non-dense mode. This means that we // cannot update the spacing to explicit numbers based on the density scale. Instead, we // determine the height reduction and equally subtract it from the default `top` and `bottom` // padding that is provided by the Material Design specification. - $vertical-deduction: math.div(mdc-textfield.$height - $height, 2); + $vertical-deduction: math.div(56px - $height, 2); // Note: these calculations are trivial enough that we could do them at runtime with `calc` // and the value of the `height` token. The problem is that because we need to hide the label diff --git a/src/material/core/tokens/m2/mat/_paginator.scss b/src/material/core/tokens/m2/mat/_paginator.scss index b0cde30bda2a..8d07dc80ffc4 100644 --- a/src/material/core/tokens/m2/mat/_paginator.scss +++ b/src/material/core/tokens/m2/mat/_paginator.scss @@ -1,6 +1,5 @@ @use 'sass:math'; @use 'sass:map'; -@use '@material/textfield' as mdc-textfield; @use '../../token-utils'; @use '../../../theming/theming'; @use '../../../theming/inspection'; @@ -66,7 +65,7 @@ $prefix: (mat, paginator); // cannot update the spacing to explicit numbers based on the density scale. Instead, we // determine the height reduction and equally subtract it from the default `top` and `bottom` // padding that is provided by the Material Design specification. - $form-field-vertical-deduction: math.div(mdc-textfield.$height - $form-field-height, 2); + $form-field-vertical-deduction: math.div(56px - $form-field-height, 2); $form-field-vertical-padding: 16px - $form-field-vertical-deduction; @return ( diff --git a/src/material/form-field/BUILD.bazel b/src/material/form-field/BUILD.bazel index 2f5dd654738e..5d7396f4eb57 100644 --- a/src/material/form-field/BUILD.bazel +++ b/src/material/form-field/BUILD.bazel @@ -57,6 +57,7 @@ sass_library( "_form-field-native-select.scss", "_form-field-subscript.scss", "_mdc-text-field-density-overrides.scss", + "_mdc-text-field-structure.scss", "_mdc-text-field-structure-overrides.scss", "_mdc-text-field-textarea-overrides.scss", "_user-agent-overrides.scss", diff --git a/src/material/form-field/_form-field-subscript.scss b/src/material/form-field/_form-field-subscript.scss index 84edca21a142..2f17b7950980 100644 --- a/src/material/form-field/_form-field-subscript.scss +++ b/src/material/form-field/_form-field-subscript.scss @@ -1,5 +1,3 @@ -@use '@material/textfield/variables' as mdc-textfield-variables; - @use '../core/tokens/m2/mat/form-field' as tokens-mat-form-field; @use '../core/style/vendor-prefixes'; @use '../core/tokens/token-utils'; @@ -18,7 +16,7 @@ top: 0; left: 0; right: 0; - padding: 0 mdc-textfield-variables.$padding-horizontal; + padding: 0 16px; } .mat-mdc-form-field-subscript-dynamic-size { diff --git a/src/material/form-field/_form-field-theme.scss b/src/material/form-field/_form-field-theme.scss index ac8becb37734..7ce5cb3acc9b 100644 --- a/src/material/form-field/_form-field-theme.scss +++ b/src/material/form-field/_form-field-theme.scss @@ -1,5 +1,3 @@ -@use '@material/textfield/filled-text-field-theme' as mdc-filled-text-field-theme; -@use '@material/textfield/outlined-text-field-theme' as mdc-outlined-text-field-theme; @use '../core/tokens/m2/mdc/filled-text-field' as tokens-mdc-filled-text-field; @use '../core/tokens/m2/mdc/outlined-text-field' as tokens-mdc-outlined-text-field; @use '../core/tokens/m2/mat/form-field' as tokens-mat-form-field; @@ -43,28 +41,28 @@ } @else { @include sass-utils.current-selector-or-root() { - @include mdc-filled-text-field-theme.theme( - tokens-mdc-filled-text-field.get-color-tokens($theme)); - @include mdc-outlined-text-field-theme.theme( - tokens-mdc-outlined-text-field.get-color-tokens($theme)); + @include token-utils.create-token-values(tokens-mdc-filled-text-field.$prefix, + tokens-mdc-filled-text-field.get-color-tokens($theme)); + @include token-utils.create-token-values(tokens-mdc-outlined-text-field.$prefix, + tokens-mdc-outlined-text-field.get-color-tokens($theme)); @include token-utils.create-token-values(tokens-mat-form-field.$prefix, tokens-mat-form-field.get-color-tokens($theme)); } .mat-mdc-form-field.mat-accent { - @include mdc-filled-text-field-theme.theme( - tokens-mdc-filled-text-field.private-get-color-palette-color-tokens($theme, accent)); - @include mdc-outlined-text-field-theme.theme( - tokens-mdc-outlined-text-field.private-get-color-palette-color-tokens($theme, accent)); + @include token-utils.create-token-values(tokens-mdc-filled-text-field.$prefix, + tokens-mdc-filled-text-field.private-get-color-palette-color-tokens($theme, accent)); + @include token-utils.create-token-values(tokens-mdc-outlined-text-field.$prefix, + tokens-mdc-outlined-text-field.private-get-color-palette-color-tokens($theme, accent)); @include token-utils.create-token-values(tokens-mat-form-field.$prefix, tokens-mat-form-field.private-get-color-palette-color-tokens($theme, accent)); } .mat-mdc-form-field.mat-warn { - @include mdc-filled-text-field-theme.theme( - tokens-mdc-filled-text-field.private-get-color-palette-color-tokens($theme, warn)); - @include mdc-outlined-text-field-theme.theme( - tokens-mdc-outlined-text-field.private-get-color-palette-color-tokens($theme, warn)); + @include token-utils.create-token-values(tokens-mdc-filled-text-field.$prefix, + tokens-mdc-filled-text-field.private-get-color-palette-color-tokens($theme, warn)); + @include token-utils.create-token-values(tokens-mdc-outlined-text-field.$prefix, + tokens-mdc-outlined-text-field.private-get-color-palette-color-tokens($theme, warn)); @include token-utils.create-token-values(tokens-mat-form-field.$prefix, tokens-mat-form-field.private-get-color-palette-color-tokens($theme, warn)); } @@ -79,10 +77,10 @@ } @else { @include sass-utils.current-selector-or-root() { - @include mdc-filled-text-field-theme.theme( - tokens-mdc-filled-text-field.get-typography-tokens($theme)); - @include mdc-outlined-text-field-theme.theme( - tokens-mdc-outlined-text-field.get-typography-tokens($theme)); + @include token-utils.create-token-values(tokens-mdc-filled-text-field.$prefix, + tokens-mdc-filled-text-field.get-typography-tokens($theme)); + @include token-utils.create-token-values(tokens-mdc-outlined-text-field.$prefix, + tokens-mdc-outlined-text-field.get-typography-tokens($theme)); @include token-utils.create-token-values(tokens-mat-form-field.$prefix, tokens-mat-form-field.get-typography-tokens($theme)); } @@ -152,7 +150,9 @@ token-utils.get-tokens-for($tokens, tokens-mdc-outlined-text-field.$prefix, $options...); $mat-form-field-tokens: token-utils.get-tokens-for($tokens, tokens-mat-form-field.$prefix, $options...); - @include mdc-filled-text-field-theme.theme($mdc-filled-text-field-tokens); - @include mdc-outlined-text-field-theme.theme($mdc-outlined-text-field-tokens); + @include token-utils.create-token-values(tokens-mdc-filled-text-field.$prefix, + $mdc-filled-text-field-tokens); + @include token-utils.create-token-values(tokens-mdc-outlined-text-field.$prefix, + $mdc-outlined-text-field-tokens); @include token-utils.create-token-values(tokens-mat-form-field.$prefix, $mat-form-field-tokens); } diff --git a/src/material/form-field/_mdc-text-field-density-overrides.scss b/src/material/form-field/_mdc-text-field-density-overrides.scss index 41ca017072d8..12b58dbc8e0c 100644 --- a/src/material/form-field/_mdc-text-field-density-overrides.scss +++ b/src/material/form-field/_mdc-text-field-density-overrides.scss @@ -1,4 +1,3 @@ -@use '@material/textfield' as mdc-textfield; @use '../core/tokens/m2/mat/form-field' as tokens-mat-form-field; @use '../core/tokens/token-utils'; @@ -51,7 +50,8 @@ .mdc-floating-label--float-above { // Needs to be in a string form to work around an internal check that incorrectly flags this // interpolation in `calc` as unnecessary. If we don't have it, Sass won't evaluate it. - $translate: 'calc(#{mdc-textfield.get-outlined-label-position-y(var(#{$height}))} * -1)'; + $offset: 'calc(6.75px + var(#{$height}) / 2)'; + $translate: 'calc(#{$offset} * -1)'; --mat-mdc-form-field-label-transform: translateY(#{$translate}) scale(var(--mat-mdc-form-field-floating-label-scale, 0.75)); transform: var(--mat-mdc-form-field-label-transform); diff --git a/src/material/form-field/_mdc-text-field-structure-overrides.scss b/src/material/form-field/_mdc-text-field-structure-overrides.scss index f4a85b1953fd..772c1c0476fe 100644 --- a/src/material/form-field/_mdc-text-field-structure-overrides.scss +++ b/src/material/form-field/_mdc-text-field-structure-overrides.scss @@ -1,4 +1,3 @@ -@use '@material/textfield/variables' as mdc-textfield-variables; @use '../core/tokens/m2/mat/form-field' as tokens-mat-form-field; @use '../core/tokens/token-utils'; @use '../core/style/vendor-prefixes'; @@ -101,8 +100,8 @@ $_enable-form-field-will-change-reset: true; [dir='rtl'] { // Undo the above padding removals which only apply in LTR languages. .mat-mdc-text-field-wrapper { - padding-left: mdc-textfield-variables.$padding-horizontal; - padding-right: mdc-textfield-variables.$padding-horizontal; + padding-left: 16px; + padding-right: 16px; } // ...and apply the correct padding resets for RTL languages. .mat-mdc-form-field-has-icon-suffix .mat-mdc-text-field-wrapper { @@ -164,16 +163,6 @@ $_enable-form-field-will-change-reset: true; padding-top: 0; } - // Unset the baseline adjustment styles that are applied to the `.mdc-text-field` before - // pseudo element. We control the vertical alignment of form field controls using infix - // spacing since we support custom form-field controls. Those don't necessarily have an - // explicit height that matches with the Material Design specification. If the height isn't - // explicitly set to a specific value by MDC, the control will not align correctly vertically. - // e.g. No vertical spacing to the bottom-line if the control is too large. - .mat-mdc-text-field-wrapper::before { - content: none; - } - // This fixes an issue where the notch appears to be thicker than the rest of the outline when // zoomed in on Chrome. The border inconsistency seems to be some kind of rendering artifact // that arises from a combination of the fact that the notch contains text, while the leading diff --git a/src/material/form-field/_mdc-text-field-structure.scss b/src/material/form-field/_mdc-text-field-structure.scss new file mode 100644 index 000000000000..6d1dca997bf1 --- /dev/null +++ b/src/material/form-field/_mdc-text-field-structure.scss @@ -0,0 +1,610 @@ +@use '@angular/cdk'; +@use '../core/style/vendor-prefixes'; +@use '../core/tokens/token-utils'; +@use '../core/tokens/m2/mdc/filled-text-field' as tokens-mdc-filled-text-field; +@use '../core/tokens/m2/mdc/outlined-text-field' as tokens-mdc-outlined-text-field; + +// Includes the structural styles for the form field inherited from MDC. +@mixin private-text-field-structure { + $filled-slots: (tokens-mdc-filled-text-field.$prefix, + tokens-mdc-filled-text-field.get-token-slots()); + $outlined-slots: (tokens-mdc-outlined-text-field.$prefix, + tokens-mdc-outlined-text-field.get-token-slots()); + + .mdc-text-field { + display: inline-flex; + align-items: baseline; + padding: 0 16px; + position: relative; + box-sizing: border-box; + overflow: hidden; + will-change: opacity, transform, color; + + // TODO(crisbeto): The filled form field overrides these while the outlined doesn't. + // The correct thing to do would be to remove them from here and have the one based on the + // token in the outlined appearance. We keep them as is for now to avoid screenshot diffs. + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + } + + .mdc-text-field__input { + width: 100%; + min-width: 0; + border: none; + border-radius: 0; + background: none; + padding: 0; + -moz-appearance: none; + -webkit-appearance: none; + + // TODO(crisbeto): this height gets overwritten eventually, but there are some internal + // tests that depend on this being here in weird ways so we're keeping it around for now. + height: 28px; + + // Note that while this style and the `-ms-clear` are identical, we can't combine + // them because if one of them isn't supported, it'll invalidate the whole rule. + &::-webkit-calendar-picker-indicator { + display: none; + } + + &::-ms-clear { + display: none; + } + + &:focus { + outline: none; + } + + &:invalid { + box-shadow: none; + } + + @include vendor-prefixes.input-placeholder { + opacity: 0; + } + + .mdc-text-field--no-label &, + .mdc-text-field--focused & { + @include vendor-prefixes.input-placeholder { + opacity: 1; + } + } + + .mdc-text-field--outlined &, + .mdc-text-field--filled.mdc-text-field--no-label & { + height: 100%; + } + + .mdc-text-field--outlined & { + display: flex; + border: none !important; + background-color: transparent; + } + + .mdc-text-field--disabled & { + pointer-events: auto; + } + + @include token-utils.use-tokens($filled-slots...) { + @include _input-tokens('.mdc-text-field--filled'); + } + + @include token-utils.use-tokens($outlined-slots...) { + @include _input-tokens('.mdc-text-field--outlined'); + } + + @include cdk.high-contrast(active, off) { + .mdc-text-field--disabled & { + background-color: Window; + } + } + } + + .mdc-text-field--filled { + height: 56px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + + @include token-utils.use-tokens($filled-slots...) { + @include token-utils.create-token-slot(border-top-left-radius, container-shape); + @include token-utils.create-token-slot(border-top-right-radius, container-shape); + + &:not(.mdc-text-field--disabled) { + @include token-utils.create-token-slot(background-color, container-color); + } + + &.mdc-text-field--disabled { + @include token-utils.create-token-slot(background-color, disabled-container-color); + } + } + } + + .mdc-text-field--outlined { + height: 56px; + overflow: visible; + padding-left: 16px; + padding-right: 16px; + + @include _supports-max { + @include token-utils.use-tokens($outlined-slots...) { + $shape-var: token-utils.get-token-variable(container-shape); + padding-right: max(16px, var(#{$shape-var})); + padding-left: max(16px, calc(var(#{$shape-var}) + 4px)); + + [dir='rtl'] & { + padding-right: max(16px, calc(var(#{$shape-var}) + 4px)); + padding-left: max(16px, var(#{$shape-var})); + } + } + } + } + + .mdc-floating-label { + position: absolute; + left: 0; + transform-origin: left top; + line-height: 1.15rem; + text-align: left; + text-overflow: ellipsis; + white-space: nowrap; + cursor: text; + overflow: hidden; + will-change: transform; + + [dir='rtl'] & { + right: 0; + left: auto; + transform-origin: right top; + text-align: right; + } + + .mdc-text-field & { + top: 50%; + transform: translateY(-50%); + pointer-events: none; + } + + .mdc-notched-outline & { + display: inline-block; + position: relative; + max-width: 100%; + } + + .mdc-text-field--outlined & { + left: 4px; + right: auto; + } + + [dir='rtl'] .mdc-text-field--outlined & { + left: auto; + right: 4px; + } + + .mdc-text-field--filled & { + left: 16px; + right: auto; + } + + [dir='rtl'] .mdc-text-field--filled & { + left: auto; + right: 16px; + } + + .mdc-text-field--disabled & { + cursor: default; + + @include cdk.high-contrast(active, off) { + z-index: 1; + } + } + + .mdc-text-field--filled.mdc-text-field--no-label & { + display: none; + } + + @include token-utils.use-tokens($filled-slots...) { + @include _floating-label-tokens('.mdc-text-field--filled'); + } + + @include token-utils.use-tokens($outlined-slots...) { + @include _floating-label-tokens('.mdc-text-field--outlined'); + } + } + + .mdc-floating-label--float-above { + cursor: auto; + transform: translateY(-106%) scale(0.75); + + .mdc-text-field--filled & { + transform: translateY(-106%) scale(0.75); + } + + .mdc-text-field--outlined & { + transform: translateY(-37.25px) scale(1); + font-size: 0.75rem; + } + + .mdc-notched-outline & { + text-overflow: clip; + } + + .mdc-notched-outline--upgraded & { + max-width: 133.3333333333%; + } + + .mdc-text-field--outlined.mdc-notched-outline--upgraded &, + .mdc-text-field--outlined .mdc-notched-outline--upgraded & { + transform: translateY(-34.75px) scale(0.75); + } + + .mdc-text-field--outlined.mdc-notched-outline--upgraded &, + .mdc-text-field--outlined .mdc-notched-outline--upgraded & { + font-size: 1rem; + } + } + + .mdc-floating-label--required { + &:not(.mdc-floating-label--hide-required-marker)::after { + margin-left: 1px; + margin-right: 0; + content: '*'; + + [dir='rtl'] & { + margin-left: 0; + margin-right: 1px; + } + } + } + + .mdc-notched-outline { + display: flex; + position: absolute; + top: 0; + right: 0; + left: 0; + box-sizing: border-box; + width: 100%; + max-width: 100%; + height: 100%; + text-align: left; + pointer-events: none; + + [dir='rtl'] & { + text-align: right; + } + + .mdc-text-field--outlined & { + z-index: 1; + } + } + + .mat-mdc-notch-piece { + box-sizing: border-box; + height: 100%; + pointer-events: none; + border-top: 1px solid; + border-bottom: 1px solid; + + .mdc-text-field--focused & { + border-width: 2px; + } + + @include token-utils.use-tokens($outlined-slots...) { + // Moved out into variables because the selectors we inherited were too long. + $enabled-selector: '.mdc-text-field--outlined:not(.mdc-text-field--disabled)'; + $hover-selector: ':not(.mdc-text-field--focused):hover'; + + #{$enabled-selector} & { + @include token-utils.create-token-slot(border-color, outline-color); + @include token-utils.create-token-slot(border-width, outline-width); + } + + #{$enabled-selector}#{$hover-selector} & { + @include token-utils.create-token-slot(border-color, hover-outline-color); + } + + #{$enabled-selector}.mdc-text-field--focused & { + @include token-utils.create-token-slot(border-color, focus-outline-color); + } + + .mdc-text-field--outlined.mdc-text-field--disabled & { + @include token-utils.create-token-slot(border-color, disabled-outline-color); + } + + #{$enabled-selector}.mdc-text-field--invalid & { + @include token-utils.create-token-slot(border-color, error-outline-color); + } + + #{$enabled-selector}.mdc-text-field--invalid#{$hover-selector} .mdc-notched-outline & { + @include token-utils.create-token-slot(border-color, error-hover-outline-color); + } + + #{$enabled-selector}.mdc-text-field--invalid.mdc-text-field--focused & { + @include token-utils.create-token-slot(border-color, error-focus-outline-color); + } + + #{$enabled-selector}.mdc-text-field--focused .mdc-notched-outline & { + @include token-utils.create-token-slot(border-width, focus-outline-width); + } + } + } + + .mdc-notched-outline__leading { + border-left: 1px solid; + border-right: none; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + width: 12px; + + @include token-utils.use-tokens($outlined-slots...) { + @include token-utils.create-token-slot(border-top-left-radius, container-shape); + @include token-utils.create-token-slot(border-bottom-left-radius, container-shape); + + @include _supports-max { + .mdc-text-field--outlined .mdc-notched-outline & { + $shape-var: token-utils.get-token-variable(container-shape); + width: max(12px, var(#{$shape-var})); + } + } + } + + [dir='rtl'] & { + border-left: none; + border-right: 1px solid; + border-bottom-left-radius: 0; + border-top-left-radius: 0; + + @include token-utils.use-tokens($outlined-slots...) { + @include token-utils.create-token-slot(border-top-right-radius, container-shape); + @include token-utils.create-token-slot(border-bottom-right-radius, container-shape); + } + } + } + + .mdc-notched-outline__trailing { + flex-grow: 1; + border-left: none; + border-right: 1px solid; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + + @include token-utils.use-tokens($outlined-slots...) { + @include token-utils.create-token-slot(border-top-right-radius, container-shape); + @include token-utils.create-token-slot(border-bottom-right-radius, container-shape); + } + + [dir='rtl'] & { + border-left: 1px solid; + border-right: none; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + + @include token-utils.use-tokens($outlined-slots...) { + @include token-utils.create-token-slot(border-top-left-radius, container-shape); + @include token-utils.create-token-slot(border-bottom-left-radius, container-shape); + } + } + } + + .mdc-notched-outline__notch { + flex: 0 0 auto; + width: auto; + max-width: calc(100% - 24px); + + @include token-utils.use-tokens($outlined-slots...) { + @include _supports-max { + .mdc-text-field--outlined .mdc-notched-outline & { + $shape-var: token-utils.get-token-variable(container-shape); + max-width: calc(100% - max(12px, var(#{$shape-var})) * 2); + } + } + } + + .mdc-text-field--outlined .mdc-notched-outline--notched & { + padding-top: 1px; + } + + .mdc-text-field--focused.mdc-text-field--outlined .mdc-notched-outline--notched & { + padding-top: 2px; + } + + .mdc-notched-outline--notched & { + padding-left: 0; + padding-right: 8px; + border-top: none; + } + + [dir='rtl'] .mdc-notched-outline--notched & { + padding-left: 8px; + padding-right: 0; + } + + .mdc-notched-outline--no-label & { + display: none; + } + } + + .mdc-line-ripple { + &::before, &::after { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + border-bottom-style: solid; + content: ''; + } + + &::before { + z-index: 1; + + @include token-utils.use-tokens($filled-slots...) { + $enabled-field: '.mdc-text-field--filled:not(.mdc-text-field--disabled)'; + + @include token-utils.create-token-slot(border-bottom-width, active-indicator-height); + + #{$enabled-field} & { + @include token-utils.create-token-slot(border-bottom-color, active-indicator-color); + } + + #{$enabled-field}:not(.mdc-text-field--focused):hover & { + @include token-utils.create-token-slot(border-bottom-color, hover-active-indicator-color); + } + + .mdc-text-field--filled.mdc-text-field--disabled & { + @include token-utils.create-token-slot(border-bottom-color, + disabled-active-indicator-color); + } + + #{$enabled-field}.mdc-text-field--invalid & { + @include token-utils.create-token-slot(border-bottom-color, error-active-indicator-color); + } + + #{$enabled-field}.mdc-text-field--invalid:not(.mdc-text-field--focused):hover & { + @include token-utils.create-token-slot(border-bottom-color, + error-hover-active-indicator-color); + } + } + } + + &::after { + transform: scaleX(0); + opacity: 0; + z-index: 2; + + @include token-utils.use-tokens($filled-slots...) { + .mdc-text-field--filled & { + @include token-utils.create-token-slot(border-bottom-width, + focus-active-indicator-height); + } + + .mdc-text-field--filled:not(.mdc-text-field--disabled) & { + @include token-utils.create-token-slot(border-bottom-color, focus-active-indicator-color); + } + + .mdc-text-field--filled.mdc-text-field--invalid:not(.mdc-text-field--disabled) & { + @include token-utils.create-token-slot(border-bottom-color, + error-focus-active-indicator-color); + } + } + } + } + + .mdc-line-ripple--active::after { + transform: scaleX(1); + opacity: 1; + } + + .mdc-line-ripple--deactivating::after { + opacity: 0; + } + + .mdc-text-field--disabled { + pointer-events: none; + } +} + +// Includes the tokens for the floating label for a specific form field variant. +@mixin _floating-label-tokens($selector) { + $enabled-field: '#{$selector}:not(.mdc-text-field--disabled)'; + + #{$enabled-field} & { + @include token-utils.create-token-slot(color, label-text-color); + } + + #{$enabled-field}.mdc-text-field--focused & { + @include token-utils.create-token-slot(color, focus-label-text-color); + } + + #{$enabled-field}:not(.mdc-text-field--focused):hover & { + @include token-utils.create-token-slot(color, hover-label-text-color); + } + + #{$selector}.mdc-text-field--disabled & { + @include token-utils.create-token-slot(color, disabled-label-text-color); + } + + #{$enabled-field}.mdc-text-field--invalid & { + @include token-utils.create-token-slot(color, error-label-text-color); + } + + #{$enabled-field}.mdc-text-field--invalid.mdc-text-field--focused & { + @include token-utils.create-token-slot(color, error-focus-label-text-color); + } + + #{$enabled-field}.mdc-text-field--invalid:not(.mdc-text-field--disabled):hover & { + @include token-utils.create-token-slot(color, error-hover-label-text-color); + } + + #{$selector} & { + @include token-utils.create-token-slot(font-family, label-text-font); + @include token-utils.create-token-slot(font-size, label-text-size); + @include token-utils.create-token-slot(font-weight, label-text-weight); + @include token-utils.create-token-slot(letter-spacing, label-text-tracking); + } +} + +// Includes the tokens for the input for a specific form field variant. +@mixin _input-tokens($selector) { + #{$selector}:not(.mdc-text-field--disabled) & { + @include token-utils.create-token-slot(color, input-text-color); + @include token-utils.create-token-slot(caret-color, caret-color); + + @include vendor-prefixes.input-placeholder { + @include token-utils.create-token-slot(color, input-text-placeholder-color); + } + } + + #{$selector}.mdc-text-field--invalid:not(.mdc-text-field--disabled) & { + @include token-utils.create-token-slot(caret-color, error-caret-color); + } + + #{$selector}.mdc-text-field--disabled & { + @include token-utils.create-token-slot(color, disabled-input-text-color); + } +} + +// Wraps the content in a `@supports` query targeting the `max` CSS function. +@mixin _supports-max { + // stylelint-disable material/no-prefixes + @supports (top: max(0%)) { + @content; + } + // stylelint-enable +} + +// Includes the animation styles for the form field inherited from MDC. +@mixin private-text-field-animations { + $timing-curve: cubic-bezier(0.4, 0, 0.2, 1); + + .mdc-floating-label { + transition: transform 150ms $timing-curve, color 150ms $timing-curve; + } + + .mdc-text-field__input { + transition: opacity 150ms $timing-curve; + + @include vendor-prefixes.input-placeholder { + transition: opacity 67ms $timing-curve; + } + } + + &.mdc-text-field--no-label, + &.mdc-text-field--focused { + .mdc-text-field__input { + @include vendor-prefixes.input-placeholder { + transition-delay: 40ms; + transition-duration: 110ms; + } + } + } + + .mdc-text-field--filled:not(.mdc-ripple-upgraded):focus .mdc-text-field__ripple::before { + transition-duration: 75ms; + } + + .mdc-line-ripple::after { + transition: transform 180ms $timing-curve, opacity 180ms $timing-curve; + } +} diff --git a/src/material/form-field/directives/notched-outline.html b/src/material/form-field/directives/notched-outline.html index a7a66160b83a..d0faa8854148 100644 --- a/src/material/form-field/directives/notched-outline.html +++ b/src/material/form-field/directives/notched-outline.html @@ -1,5 +1,5 @@ -
-
+
+
-
+
diff --git a/src/material/form-field/form-field.scss b/src/material/form-field/form-field.scss index 48d5481ee235..32ae389caeb6 100644 --- a/src/material/form-field/form-field.scss +++ b/src/material/form-field/form-field.scss @@ -1,57 +1,18 @@ -@use '@material/textfield/text-field' as mdc-textfield; -@use '@material/textfield/filled-text-field-theme' as mdc-filled-text-field-theme; -@use '@material/textfield/outlined-text-field-theme' as mdc-outlined-text-field-theme; -@use '@material/floating-label/floating-label' as mdc-floating-label; -@use '@material/floating-label/floating-label-theme' as mdc-floating-label-theme; -@use '@material/notched-outline/notched-outline' as mdc-notched-outline; -@use '@material/notched-outline/notched-outline-theme' as mdc-notched-outline-theme; -@use '@material/line-ripple/line-ripple' as mdc-line-ripple; -@use '@material/line-ripple/line-ripple-theme' as mdc-line-ripple-theme; -@use '@material/theme/custom-properties' as mdc-custom-properties; @use '../core/tokens/token-utils'; -@use '../core/mdc-helpers/mdc-helpers'; @use '../core/style/vendor-prefixes'; @use '../core/tokens/m2/mat/form-field' as tokens-mat-form-field; -@use '../core/tokens/m2/mdc/filled-text-field' as tokens-mdc-filled-text-field; -@use '../core/tokens/m2/mdc/outlined-text-field' as tokens-mdc-outlined-text-field; @use './form-field-subscript'; @use './form-field-focus-overlay'; @use './form-field-high-contrast'; @use './form-field-native-select'; @use './user-agent-overrides'; +@use './mdc-text-field-structure'; @use './mdc-text-field-textarea-overrides'; @use './mdc-text-field-structure-overrides'; @use './mdc-text-field-density-overrides'; -// Includes the structural styles of the components that the form field is composed of. -@mixin _static-styles($query) { - @include mdc-textfield.static-styles($query); - @include mdc-floating-label.static-styles($query); - @include mdc-notched-outline.static-styles($query); - @include mdc-line-ripple.static-styles($query); - - // Note that while these components have token slots, they're included through the MDC text field. - @include mdc-floating-label-theme.theme-styles($query); - @include mdc-notched-outline-theme.theme-styles($query); - @include mdc-line-ripple-theme.theme-styles($query); -} - // Base styles for MDC text-field, notched-outline, floating label and line-ripple. -@include _static-styles(mdc-helpers.$mdc-base-styles-without-animation-query); - -@include mdc-custom-properties.configure( - $emit-fallback-values: false, $emit-fallback-vars: false) { - - .mdc-text-field--filled { - @include mdc-filled-text-field-theme.theme-styles( - tokens-mdc-filled-text-field.get-token-slots()); - } - - .mdc-text-field--outlined { - @include mdc-outlined-text-field-theme.theme-styles( - tokens-mdc-outlined-text-field.get-token-slots()); - } -} +@include mdc-text-field-structure.private-text-field-structure(); // MDC text-field overwrites. @include mdc-text-field-textarea-overrides.private-text-field-textarea-overrides(); @@ -246,7 +207,7 @@ $_icon-prefix-infix-padding: 4px; // In order to make it possible for developers to disable animations for form-fields, // we only activate the animation styles if animations are not explicitly disabled. .mat-mdc-form-field:not(.mat-form-field-no-animations) { - @include _static-styles(animation); + @include mdc-text-field-structure.private-text-field-animations; } // Allow the label to grow 1px bigger than the notch.