diff --git a/src/dev-app/mdc-input/mdc-input-demo.html b/src/dev-app/mdc-input/mdc-input-demo.html index a091105202f6..2e6a578f0119 100644 --- a/src/dev-app/mdc-input/mdc-input-demo.html +++ b/src/dev-app/mdc-input/mdc-input-demo.html @@ -731,6 +731,25 @@

<textarea> with bindable autosize

Label + +

+ Textarea alignment +

+ + + Textarea + + + + + Input + + + + + Input + + diff --git a/src/material-experimental/mdc-chips/chip-input.spec.ts b/src/material-experimental/mdc-chips/chip-input.spec.ts index 8f38955ba7d9..88ae71264b2d 100644 --- a/src/material-experimental/mdc-chips/chip-input.spec.ts +++ b/src/material-experimental/mdc-chips/chip-input.spec.ts @@ -141,7 +141,7 @@ describe('MDC-based MatChipInput', () => { it('should set input styling classes', () => { expect(inputNativeElement.classList).toContain('mat-mdc-input-element'); - expect(inputNativeElement.classList).toContain('mat-mdc-form-field-control'); + expect(inputNativeElement.classList).toContain('mat-mdc-form-field-input-control'); expect(inputNativeElement.classList).toContain('mat-mdc-chip-input'); expect(inputNativeElement.classList).toContain('mdc-text-field__input'); }); diff --git a/src/material-experimental/mdc-chips/chip-input.ts b/src/material-experimental/mdc-chips/chip-input.ts index e77a2aa98d63..0e6b52c98a8c 100644 --- a/src/material-experimental/mdc-chips/chip-input.ts +++ b/src/material-experimental/mdc-chips/chip-input.ts @@ -135,7 +135,7 @@ export class MatChipInput implements MatChipTextControl, AfterContentInit, OnCha this.inputElement = this._elementRef.nativeElement as HTMLInputElement; if (formField) { - this.inputElement.classList.add('mat-mdc-form-field-control'); + this.inputElement.classList.add('mat-mdc-form-field-input-control'); } } diff --git a/src/material-experimental/mdc-form-field/_form-field-native-select.scss b/src/material-experimental/mdc-form-field/_form-field-native-select.scss index 0cda173843c2..5978cb082398 100644 --- a/src/material-experimental/mdc-form-field/_form-field-native-select.scss +++ b/src/material-experimental/mdc-form-field/_form-field-native-select.scss @@ -18,7 +18,7 @@ $mat-form-field-select-horizontal-end-padding: $mat-form-field-select-arrow-widt // Remove the native select down arrow and ensure that the native appearance // does not conflict with the form-field. e.g. Focus indication of the native // select is undesired since we handle focus as part of the form-field. - select.mat-mdc-form-field-control { + select.mat-mdc-form-field-input-control { -moz-appearance: none; -webkit-appearance: none; background-color: transparent; @@ -83,7 +83,7 @@ $mat-form-field-select-horizontal-end-padding: $mat-form-field-select-arrow-widt // Add padding on the end of the native select so that the content does not // overlap with the Material Design arrow. - .mat-mdc-form-field-control { + .mat-mdc-form-field-input-control { padding-right: $mat-form-field-select-horizontal-end-padding; [dir='rtl'] & { padding-right: 0; @@ -105,7 +105,7 @@ $mat-form-field-select-horizontal-end-padding: $mat-form-field-select-arrow-widt $dropdown-icon-color: rgba(mdc-theme-color.prop-value(on-surface), 0.54); $disabled-dropdown-icon-color: rgba(mdc-theme-color.prop-value(on-surface), 0.38); - select.mat-mdc-form-field-control { + select.mat-mdc-form-field-input-control { // On dark themes we set the native `select` color to some shade of white, // however the color propagates to all of the `option` elements, which are // always on a white background inside the dropdown, causing them to blend in. diff --git a/src/material-experimental/mdc-form-field/_form-field-sizing.scss b/src/material-experimental/mdc-form-field/_form-field-sizing.scss index 532fd60171cd..3b3fbcd1174b 100644 --- a/src/material-experimental/mdc-form-field/_form-field-sizing.scss +++ b/src/material-experimental/mdc-form-field/_form-field-sizing.scss @@ -38,7 +38,3 @@ $mat-form-field-no-label-padding-top: 16px; // The amount of padding between the icon prefix/suffix and the infix. // This assumes that the icon will be a 24px square with 12px padding. $mat-form-field-icon-prefix-infix-padding: 4px; - -// The amount of padding between the end of the form-field and the infix for a form-field with no -// icons. -$mat-form-field-end-padding: 16px; diff --git a/src/material-experimental/mdc-form-field/_form-field-subscript.scss b/src/material-experimental/mdc-form-field/_form-field-subscript.scss index 3a84607b8dff..4eaa74f2cb2d 100644 --- a/src/material-experimental/mdc-form-field/_form-field-subscript.scss +++ b/src/material-experimental/mdc-form-field/_form-field-subscript.scss @@ -1,6 +1,7 @@ @use '@material/textfield' as mdc-textfield; @use '@material/theme/theme' as mdc-theme; @use '@material/typography' as mdc-typography; +@use '@material/textfield/variables' as mdc-textfield-variables; @use 'form-field-sizing'; @use '../mdc-helpers/mdc-helpers'; @use '../../material/core/theming/theming'; @@ -13,16 +14,13 @@ position: relative; } - // The horizontal padding between the edge of the text box and the start of the subscript text. - $subscript-horizontal-padding: 16px; - .mat-mdc-form-field-hint-wrapper, .mat-mdc-form-field-error-wrapper { position: absolute; top: 0; left: 0; right: 0; - padding: 0 $subscript-horizontal-padding; + padding: 0 mdc-textfield-variables.$padding-horizontal; } .mat-mdc-form-field-bottom-align::before { @@ -31,14 +29,8 @@ height: 16px; } - // Scale down icons in the subscript to be the same size as the text. - .mat-mdc-form-field-subscript-wrapper, - .mat-mdc-form-field label { - .mat-icon { - width: 1em; - height: 1em; - font-size: inherit; - } + .mat-mdc-form-field-hint-end { + order: 1; } // Clears the floats on the hints. This is necessary for the hint animation to work. diff --git a/src/material-experimental/mdc-form-field/_form-field-theme.scss b/src/material-experimental/mdc-form-field/_form-field-theme.scss index 882b95bdf843..3cd6d01776a5 100644 --- a/src/material-experimental/mdc-form-field/_form-field-theme.scss +++ b/src/material-experimental/mdc-form-field/_form-field-theme.scss @@ -3,7 +3,7 @@ @use '@material/notched-outline' as mdc-notched-outline; @use '@material/line-ripple' as mdc-line-ripple; @use '@material/theme/theme-color' as mdc-theme-color; -@use '@material/typography' as mdc-typography; +@use '@material/typography/typography' as mdc-typography; @use '../mdc-helpers/mdc-helpers'; @use '../../material/core/typography/typography'; @use 'form-field-density'; @@ -72,14 +72,30 @@ @include mdc-line-ripple.core-styles($query: mdc-helpers.$mat-typography-styles-query); @include form-field-subscript.private-form-field-subscript-typography($config); - // MDC uses the `subtitle1` level for the input label and value, but the spec shows `body1` as - // the correct level. - .mat-mdc-form-field-control, - .mat-mdc-form-field label, - .mat-mdc-form-field-text-prefix, - .mat-mdc-form-field-text-suffix { + // MDC uses `subtitle1` for the input value, placeholder and floating label. The spec + // shows `body1` for text fields though, so we manually override the typography. + // Note: Form controls inherit the typography from the parent form field. + .mat-mdc-form-field, + .mat-mdc-form-field .mdc-floating-label { @include mdc-typography.typography(body1, $query: mdc-helpers.$mat-typography-styles-query); } + + // Above, we updated the floating label to use the `body1` typography level. The MDC notched + // outline overrides this accidentally (only when the label floats) to a `rem`-based value. + // This results in different label widths when floated/docked and ultimately breaks the notch + // width as it has been measured in the docked state (where `body1` is applied). We try to + // unset these styles set by the `mdc-notched-outline`: + // https://github.com/material-components/material-components-web/blob/master/packages/mdc-notched-outline/_mixins.scss#L272-L292. + .mat-mdc-form-field .mdc-text-field--outlined { + // For the non-upgraded notch label (i.e. when rendered on the server), also + // use the correct `body1` typography level. + .mdc-floating-label--float-above { + font-size: mdc-typography.get-size(body1) * 0.75; + } + .mdc-notched-outline--upgraded .mdc-floating-label--float-above { + font-size: mdc-typography.get-size(body1); + } + } } } diff --git a/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss b/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss index 226509f38fe0..873ff08b4a0d 100644 --- a/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss +++ b/src/material-experimental/mdc-form-field/_mdc-text-field-structure-overrides.scss @@ -1,4 +1,4 @@ -@use 'form-field-sizing'; +@use '@material/textfield/variables' as mdc-textfield-variables; // Mixin that can be included to override the default MDC text-field // styles to fit our needs. See individual comments for context on why @@ -6,15 +6,21 @@ @mixin private-text-field-structure-overrides() { // Unset the border set by MDC. We move the border (which serves as the Material Design // text-field bottom line) into its own element. This is necessary because we want the - // bottom-line to span across the whole form-field (including prefixes and suffixes). - .mat-mdc-form-field-control { + // bottom-line to span across the whole form-field (including prefixes and suffixes). Also + // we ensure that font styles are inherited for input elements. We do this because inputs by + // default have explicit font styles from the user agent, and we set the desired font styles + // in the parent `mat-form-field` element (for better custom form-field control support). + // Note: We increase specificity here because the MDC textfield seems to override this, + // depending on the CSS order, with an affix selector joint with the input. + .mat-mdc-form-field-input-control.mat-mdc-form-field-input-control { + font: inherit; border: none; } // In order to ensure proper alignment of the floating label, we reset its line-height. // The line-height is not important as the element is absolutely positioned and only has one line // of text. - .mat-mdc-form-field .mdc-floating-label { + .mat-mdc-form-field .mdc-floating-label.mdc-floating-label { line-height: normal; } @@ -24,15 +30,16 @@ // not work for us since we support arbitrary form field controls which don't necessarily // use an `input` element. We organize the vertical spacing on the infix container. .mdc-text-field--no-label:not(.mdc-text-field--textarea) - .mat-mdc-form-field-control.mdc-text-field__input, - .mat-mdc-text-field-wrapper .mat-mdc-form-field-control { + .mat-mdc-form-field-input-control.mdc-text-field__input, + .mat-mdc-text-field-wrapper .mat-mdc-form-field-input-control { height: auto; } // Color inputs are a special case, because setting their height to // `auto` will collapse them. The height value is an arbitrary number // which was extracted from the user agent styles of Chrome and Firefox. - .mat-mdc-text-field-wrapper .mat-mdc-form-field-control.mdc-text-field__input[type='color'] { + .mat-mdc-text-field-wrapper + .mat-mdc-form-field-input-control.mdc-text-field__input[type='color'] { height: 23px; } @@ -57,8 +64,8 @@ [dir='rtl'] { // Undo the above padding removals which only apply in LTR languages. .mat-mdc-text-field-wrapper { - padding-left: form-field-sizing.$mat-form-field-end-padding; - padding-right: form-field-sizing.$mat-form-field-end-padding; + padding-left: mdc-textfield-variables.$padding-horizontal; + padding-right: mdc-textfield-variables.$padding-horizontal; } // ...and apply the correct padding resets for RTL languages. .mat-mdc-form-field-has-icon-suffix .mat-mdc-text-field-wrapper { diff --git a/src/material-experimental/mdc-form-field/_mdc-text-field-textarea-overrides.scss b/src/material-experimental/mdc-form-field/_mdc-text-field-textarea-overrides.scss index 008321138c11..cee19bbf8536 100644 --- a/src/material-experimental/mdc-form-field/_mdc-text-field-textarea-overrides.scss +++ b/src/material-experimental/mdc-form-field/_mdc-text-field-textarea-overrides.scss @@ -10,7 +10,15 @@ @mixin private-text-field-textarea-overrides() { // Ensures that textarea elements inside of the form-field have proper vertical spacing // to account for the floating label. Also ensures that there is no vertical text overflow. - .mat-mdc-textarea-input { + // **Note**: Before changing this selector, make sure the `cdk-textarea-autosize` class is + // still able to override the `resize` property to `none`. + .mat-mdc-form-field-textarea-control { + // Set the vertical alignment for textareas inside form fields to be the middle. This + // ensures that textareas do not stretch the infix container vertically without having + // multiple rows of text. See: https://github.com/angular/components/pull/22089. + vertical-align: middle; + // Textareas by default also allow users to resize the textarea horizontally. This + // causes the textarea to overflow the form-field. We only allow vertical resizing. resize: vertical; box-sizing: border-box; height: auto; diff --git a/src/material-experimental/mdc-form-field/form-field.scss b/src/material-experimental/mdc-form-field/form-field.scss index 7b365b37e900..e1b573d8aea3 100644 --- a/src/material-experimental/mdc-form-field/form-field.scss +++ b/src/material-experimental/mdc-form-field/form-field.scss @@ -90,13 +90,21 @@ } } +// Scale down icons in the subscript and floating label to be the same size +// as the text. +.mat-mdc-form-field-subscript-wrapper, +.mat-mdc-form-field label { + .mat-icon { + width: 1em; + height: 1em; + font-size: inherit; + } +} + // Infix that contains the projected content (usually an input or a textarea). We ensure // that the projected form-field control and content can stretch as needed, but we also // apply a default infix width to make the form-field's look natural. .mat-mdc-form-field-infix { - // Prevent extra height from being added around the textarea, which throws off the overall - // height of the form-field - line-height: 0; flex: auto; min-width: 0; width: form-field-sizing.$mat-form-field-default-infix-width; @@ -105,10 +113,6 @@ box-sizing: border-box; } -.mat-mdc-form-field-hint-end { - order: 1; -} - // 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) { diff --git a/src/material-experimental/mdc-form-field/form-field.ts b/src/material-experimental/mdc-form-field/form-field.ts index 571152ccc965..a73f11e0d837 100644 --- a/src/material-experimental/mdc-form-field/form-field.ts +++ b/src/material-experimental/mdc-form-field/form-field.ts @@ -93,6 +93,12 @@ const DEFAULT_FLOAT_LABEL: FloatLabelType = 'auto'; */ const FLOATING_LABEL_DEFAULT_DOCKED_TRANSFORM = `translateY(-50%)`; +/** + * Horizontal padding in pixels used by the MDC for the wrapper containing infix. + * This value is extracted from MDC's Sass variables. See `$padding-horizontal`. + */ +const WRAPPER_HORIZONTAL_PADDING = 16; + /** Container for form controls that applies Material Design styling and behavior. */ @Component({ selector: 'mat-form-field', @@ -691,16 +697,16 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck } const iconPrefixContainer = this._iconPrefixContainer?.nativeElement; const textPrefixContainer = this._textPrefixContainer?.nativeElement; + const iconPrefixContainerWidth = iconPrefixContainer?.getBoundingClientRect().width ?? 0; + const textPrefixContainerWidth = textPrefixContainer?.getBoundingClientRect().width ?? 0; // If the directionality is RTL, the x-axis transform needs to be inverted. This // is because `transformX` does not change based on the page directionality. const labelHorizontalOffset = (this._dir.value === 'rtl' ? -1 : 1) * ( - (iconPrefixContainer ? - // If there's an icon prefix, we disable the default 16px padding, - // so make sure to account for that. - (iconPrefixContainer?.getBoundingClientRect().width ?? 0) - 16 : 0 - ) + - (textPrefixContainer?.getBoundingClientRect().width ?? 0) + // If there's an icon prefix, we subtract the default horizontal padding as we + // reset the horizontal padding in CSS too. + (iconPrefixContainer ? iconPrefixContainerWidth - WRAPPER_HORIZONTAL_PADDING : 0) + + textPrefixContainerWidth ); // Update the transform the floating label to account for the prefix container. Note diff --git a/src/material-experimental/mdc-input/input.spec.ts b/src/material-experimental/mdc-input/input.spec.ts index b971b87f59cd..3570665cceb9 100644 --- a/src/material-experimental/mdc-input/input.spec.ts +++ b/src/material-experimental/mdc-input/input.spec.ts @@ -858,8 +858,8 @@ describe('MatMdcInput without forms', () => { const inFormField = fixture.nativeElement.querySelector('.inside'); const outsideFormField = fixture.nativeElement.querySelector('.outside'); - expect(inFormField.classList).toContain('mat-mdc-form-field-control'); - expect(outsideFormField.classList).not.toContain('mat-mdc-form-field-control'); + expect(inFormField.classList).toContain('mat-mdc-form-field-input-control'); + expect(outsideFormField.classList).not.toContain('mat-mdc-form-field-input-control'); })); }); diff --git a/src/material-experimental/mdc-input/input.ts b/src/material-experimental/mdc-input/input.ts index 329d386fc21a..06c0942316d0 100644 --- a/src/material-experimental/mdc-input/input.ts +++ b/src/material-experimental/mdc-input/input.ts @@ -27,8 +27,8 @@ import {MatInput as BaseMatInput} from '@angular/material/input'; '[class.mat-input-element]': 'false', '[class.mat-form-field-control]': 'false', '[class.mat-input-server]': '_isServer', - '[class.mat-mdc-textarea-input]': '_isTextarea', - '[class.mat-mdc-form-field-control]': '_isInFormField', + '[class.mat-mdc-form-field-textarea-control]': '_isInFormField && _isTextarea', + '[class.mat-mdc-form-field-input-control]': '_isInFormField', '[class.mdc-text-field__input]': '_isInFormField', // Native input properties that are overwritten by Angular inputs need to be synced with // the native input element. Otherwise property bindings for those don't work. diff --git a/src/material/datepicker/date-range-input.ts b/src/material/datepicker/date-range-input.ts index 1a8411e5c626..47c498394884 100644 --- a/src/material/datepicker/date-range-input.ts +++ b/src/material/datepicker/date-range-input.ts @@ -249,7 +249,7 @@ export class MatDateRangeInput implements MatFormFieldControl>, if (_formField?._elementRef.nativeElement.classList.contains('mat-mdc-form-field')) { const classList = _elementRef.nativeElement.classList; classList.add('mat-mdc-input-element'); - classList.add('mat-mdc-form-field-control'); + classList.add('mat-mdc-form-field-input-control'); } // TODO(crisbeto): remove `as any` after #18206 lands. diff --git a/src/material/form-field/.form-field.md.swp b/src/material/form-field/.form-field.md.swp deleted file mode 100644 index 9039143c4e59..000000000000 Binary files a/src/material/form-field/.form-field.md.swp and /dev/null differ