Skip to content

Commit

Permalink
fix(material/form-field): Don't allow label to grow larger than input (
Browse files Browse the repository at this point in the history
…#29673)

Fixes #26558

(cherry picked from commit 77051f8)
  • Loading branch information
mmalerba committed Sep 9, 2024
1 parent 103ec13 commit 0e6dee3
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 71 deletions.
96 changes: 49 additions & 47 deletions src/material/form-field/_mdc-text-field-structure.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@

// 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());
$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;
Expand Down Expand Up @@ -124,19 +128,15 @@
.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, #{$shape-var});
padding-left: max(16px, calc(#{$shape-var} + 4px));
@include token-utils.use-tokens($outlined-slots...) {
$shape-var: token-utils.get-token-variable(container-shape);
padding-right: max(16px, #{$shape-var});
padding-left: max(16px, calc(#{$shape-var} + 4px));

[dir='rtl'] & {
padding-right: max(16px, calc(#{$shape-var} + 4px));
padding-left: max(16px, #{$shape-var});
}
[dir='rtl'] & {
padding-right: max(16px, calc(#{$shape-var} + 4px));
padding-left: max(16px, #{$shape-var});
}
}
}
Expand Down Expand Up @@ -336,17 +336,14 @@
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, #{$shape-var});
}
.mdc-text-field--outlined .mdc-notched-outline & {
$shape-var: token-utils.get-token-variable(container-shape);
width: max(12px, #{$shape-var});
}
}

Expand Down Expand Up @@ -391,14 +388,14 @@
.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, #{$shape-var}) * 2);
}
.mdc-text-field--outlined .mdc-notched-outline & {
$shape-var: token-utils.get-token-variable(container-shape);
max-width: min(
var(--mat-form-field-notch-max-width, 100%),
calc(100% - max(12px, #{$shape-var}) * 2)
);
}
}

Expand All @@ -414,6 +411,7 @@
padding-left: 0;
padding-right: 8px;
border-top: none;
--mat-form-field-notch-max-width: 100%;
}

[dir='rtl'] .mdc-notched-outline--notched & {
Expand All @@ -427,7 +425,8 @@
}

.mdc-line-ripple {
&::before, &::after {
&::before,
&::after {
position: absolute;
bottom: 0;
left: 0;
Expand All @@ -453,17 +452,21 @@
}

.mdc-text-field--filled.mdc-text-field--disabled & {
@include token-utils.create-token-slot(border-bottom-color,
disabled-active-indicator-color);
@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);
@include token-utils.create-token-slot(
border-bottom-color,
error-hover-active-indicator-color
);
}
}
}
Expand All @@ -475,17 +478,21 @@

@include token-utils.use-tokens($filled-slots...) {
.mdc-text-field--filled & {
@include token-utils.create-token-slot(border-bottom-width,
focus-active-indicator-height);
@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);
@include token-utils.create-token-slot(
border-bottom-color,
error-focus-active-indicator-color
);
}
}
}
Expand Down Expand Up @@ -565,21 +572,14 @@
}
}

// 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;
transition:
transform 150ms $timing-curve,
color 150ms $timing-curve;
}

.mdc-text-field__input {
Expand All @@ -605,6 +605,8 @@
}

.mdc-line-ripple::after {
transition: transform 180ms $timing-curve, opacity 180ms $timing-curve;
transition:
transform 180ms $timing-curve,
opacity 180ms $timing-curve;
}
}
57 changes: 33 additions & 24 deletions src/material/form-field/form-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,39 @@
simply link the label to the control using the label `for` attribute.
-->
@if (_hasFloatingLabel()) {
<label matFormFieldFloatingLabel
[floating]="_shouldLabelFloat()"
[monitorResize]="_hasOutline()"
[id]="_labelId"
[attr.for]="_control.disableAutomaticLabeling ? null : _control.id">
<label
matFormFieldFloatingLabel
[floating]="_shouldLabelFloat()"
[monitorResize]="_hasOutline()"
[id]="_labelId"
[attr.for]="_control.disableAutomaticLabeling ? null : _control.id"
>
<ng-content select="mat-label"></ng-content>
<!--
We set the required marker as a separate element, in order to make it easier to target if
apps want to override it and to be able to set `aria-hidden` so that screen readers don't
pick it up.
-->
@if (!hideRequiredMarker && _control.required) {
<span
aria-hidden="true"
class="mat-mdc-form-field-required-marker mdc-floating-label--required"></span>
}
@if (!hideRequiredMarker && _control.required) {
<span
aria-hidden="true"
class="mat-mdc-form-field-required-marker mdc-floating-label--required"
></span>
}
</label>
}
</ng-template>

<div class="mat-mdc-text-field-wrapper mdc-text-field" #textField
[class.mdc-text-field--filled]="!_hasOutline()"
[class.mdc-text-field--outlined]="_hasOutline()"
[class.mdc-text-field--no-label]="!_hasFloatingLabel()"
[class.mdc-text-field--disabled]="_control.disabled"
[class.mdc-text-field--invalid]="_control.errorState"
(click)="_control.onContainerClick($event)">
<div
class="mat-mdc-text-field-wrapper mdc-text-field"
#textField
[class.mdc-text-field--filled]="!_hasOutline()"
[class.mdc-text-field--outlined]="_hasOutline()"
[class.mdc-text-field--no-label]="!_hasFloatingLabel()"
[class.mdc-text-field--disabled]="_control.disabled"
[class.mdc-text-field--invalid]="_control.errorState"
(click)="_control.onContainerClick($event)"
>
@if (!_hasOutline() && !_control.disabled) {
<div class="mat-mdc-form-field-focus-overlay"></div>
}
Expand Down Expand Up @@ -72,13 +78,13 @@
</div>

@if (_hasTextSuffix) {
<div class="mat-mdc-form-field-text-suffix">
<div class="mat-mdc-form-field-text-suffix" #textSuffixContainer>
<ng-content select="[matTextSuffix]"></ng-content>
</div>
}

@if (_hasIconSuffix) {
<div class="mat-mdc-form-field-icon-suffix">
<div class="mat-mdc-form-field-icon-suffix" #iconSuffixContainer>
<ng-content select="[matSuffix], [matIconSuffix]"></ng-content>
</div>
}
Expand All @@ -89,13 +95,16 @@
}
</div>

<div class="mat-mdc-form-field-subscript-wrapper mat-mdc-form-field-bottom-align"
[class.mat-mdc-form-field-subscript-dynamic-size]="subscriptSizing === 'dynamic'">

<div
class="mat-mdc-form-field-subscript-wrapper mat-mdc-form-field-bottom-align"
[class.mat-mdc-form-field-subscript-dynamic-size]="subscriptSizing === 'dynamic'"
>
@switch (_getDisplayedMessages()) {
@case ('error') {
<div class="mat-mdc-form-field-error-wrapper"
[@transitionMessages]="_subscriptAnimationState">
<div
class="mat-mdc-form-field-error-wrapper"
[@transitionMessages]="_subscriptAnimationState"
>
<ng-content select="mat-error, [matError]"></ng-content>
</div>
}
Expand Down
17 changes: 17 additions & 0 deletions src/material/form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ export class MatFormField
@ViewChild('textField') _textField: ElementRef<HTMLElement>;
@ViewChild('iconPrefixContainer') _iconPrefixContainer: ElementRef<HTMLElement>;
@ViewChild('textPrefixContainer') _textPrefixContainer: ElementRef<HTMLElement>;
@ViewChild('iconSuffixContainer') _iconSuffixContainer: ElementRef<HTMLElement>;
@ViewChild('textSuffixContainer') _textSuffixContainer: ElementRef<HTMLElement>;
@ViewChild(MatFormFieldFloatingLabel) _floatingLabel: MatFormFieldFloatingLabel | undefined;
@ViewChild(MatFormFieldNotchedOutline) _notchedOutline: MatFormFieldNotchedOutline | undefined;
@ViewChild(MatFormFieldLineRipple) _lineRipple: MatFormFieldLineRipple | undefined;
Expand Down Expand Up @@ -708,8 +710,12 @@ export class MatFormField
}
const iconPrefixContainer = this._iconPrefixContainer?.nativeElement;
const textPrefixContainer = this._textPrefixContainer?.nativeElement;
const iconSuffixContainer = this._iconSuffixContainer?.nativeElement;
const textSuffixContainer = this._textSuffixContainer?.nativeElement;
const iconPrefixContainerWidth = iconPrefixContainer?.getBoundingClientRect().width ?? 0;
const textPrefixContainerWidth = textPrefixContainer?.getBoundingClientRect().width ?? 0;
const iconSuffixContainerWidth = iconSuffixContainer?.getBoundingClientRect().width ?? 0;
const textSuffixContainerWidth = textSuffixContainer?.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 negate = this._dir.value === 'rtl' ? '-1' : '1';
Expand All @@ -724,6 +730,17 @@ export class MatFormField
--mat-mdc-form-field-label-transform,
${FLOATING_LABEL_DEFAULT_DOCKED_TRANSFORM} translateX(${labelHorizontalOffset})
)`;

// Prevent the label from overlapping the suffix when in resting position.
const prefixAndSuffixWidth =
iconPrefixContainerWidth +
textPrefixContainerWidth +
iconSuffixContainerWidth +
textSuffixContainerWidth;
this._elementRef.nativeElement.style.setProperty(
'--mat-form-field-notch-max-width',
`calc(100% - ${prefixAndSuffixWidth}px)`,
);
}

/** Checks whether the form field is attached to the DOM. */
Expand Down
4 changes: 4 additions & 0 deletions tools/public_api_guard/material/form-field.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export class MatFormField implements FloatingLabelParent, AfterContentInit, Afte
// (undocumented)
_iconPrefixContainer: ElementRef<HTMLElement>;
// (undocumented)
_iconSuffixContainer: ElementRef<HTMLElement>;
// (undocumented)
readonly _labelId: string;
// (undocumented)
_lineRipple: MatFormFieldLineRipple | undefined;
Expand Down Expand Up @@ -146,6 +148,8 @@ export class MatFormField implements FloatingLabelParent, AfterContentInit, Afte
// (undocumented)
_textPrefixContainer: ElementRef<HTMLElement>;
// (undocumented)
_textSuffixContainer: ElementRef<HTMLElement>;
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration<MatFormField, "mat-form-field", ["matFormField"], { "hideRequiredMarker": { "alias": "hideRequiredMarker"; "required": false; }; "color": { "alias": "color"; "required": false; }; "floatLabel": { "alias": "floatLabel"; "required": false; }; "appearance": { "alias": "appearance"; "required": false; }; "subscriptSizing": { "alias": "subscriptSizing"; "required": false; }; "hintLabel": { "alias": "hintLabel"; "required": false; }; }, {}, ["_labelChild", "_formFieldControl", "_prefixChildren", "_suffixChildren", "_errorChildren", "_hintChildren"], ["mat-label", "[matPrefix], [matIconPrefix]", "[matTextPrefix]", "*", "[matTextSuffix]", "[matSuffix], [matIconSuffix]", "mat-error, [matError]", "mat-hint:not([align='end'])", "mat-hint[align='end']"], true, never>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<MatFormField, [null, null, null, null, null, { optional: true; }, { optional: true; }, null]>;
Expand Down

0 comments on commit 0e6dee3

Please sign in to comment.