Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(material-experimental/mdc-form-field): fix prefix/suffix padding #22090

Merged
merged 1 commit into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,11 @@ $mat-form-field-with-label-input-padding-bottom: 8px;
// same reasoning applies to the padding for text fields without label.
$mat-form-field-no-label-padding-bottom: 16px;
$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;
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,31 @@
flex: auto;
}

// The icon prefix/suffix is closer to the edge of the form-field than the infix is in a
// form-field with no prefix/suffix. Therefore the standard padding has to be removed when showing
// an icon prefix or suffix. We can't rely on MDC's styles for this because we use a different
// structure for our form-field in order to support arbitrary height input elements.
.mat-mdc-form-field-has-icon-prefix .mat-mdc-text-field-wrapper {
mmalerba marked this conversation as resolved.
Show resolved Hide resolved
padding-left: 0;
}
.mat-mdc-form-field-has-icon-suffix .mat-mdc-text-field-wrapper {
padding-right: 0;
}
[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;
}
// ...and apply the correct padding resets for RTL languages.
.mat-mdc-form-field-has-icon-suffix .mat-mdc-text-field-wrapper {
padding-left: 0;
}
.mat-mdc-form-field-has-icon-prefix .mat-mdc-text-field-wrapper {
padding-right: 0;
}
}

// The default MDC text-field implementation does not support labels which always float.
// MDC only renders the placeholder if the input is focused. We extend this to show the
// placeholder if the form-field label is set to always float.
Expand Down
10 changes: 8 additions & 2 deletions src/material-experimental/mdc-form-field/directives/prefix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Directive, InjectionToken} from '@angular/core';
import {Directive, ElementRef, InjectionToken} from '@angular/core';

/**
* Injection token that can be used to reference instances of `MatPrefix`. It serves as
Expand All @@ -20,4 +20,10 @@ export const MAT_PREFIX = new InjectionToken<MatPrefix>('MatPrefix');
selector: '[matPrefix], [matIconPrefix], [matTextPrefix]',
providers: [{provide: MAT_PREFIX, useExisting: MatPrefix}],
})
export class MatPrefix {}
export class MatPrefix {
_isText = false;

constructor(elementRef: ElementRef) {
this._isText = elementRef.nativeElement.hasAttribute('matTextPrefix');
}
}
10 changes: 8 additions & 2 deletions src/material-experimental/mdc-form-field/directives/suffix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Directive, InjectionToken} from '@angular/core';
import {Directive, ElementRef, InjectionToken} from '@angular/core';

/**
* Injection token that can be used to reference instances of `MatSuffix`. It serves as
Expand All @@ -20,4 +20,10 @@ export const MAT_SUFFIX = new InjectionToken<MatSuffix>('MatSuffix');
selector: '[matSuffix], [matIconSuffix], [matTextSuffix]',
providers: [{provide: MAT_SUFFIX, useExisting: MatSuffix}],
})
export class MatSuffix {}
export class MatSuffix {
_isText = false;

constructor(elementRef: ElementRef) {
this._isText = elementRef.nativeElement.hasAttribute('matTextSuffix');
}
}
8 changes: 4 additions & 4 deletions src/material-experimental/mdc-form-field/form-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@
</ng-template>
</div>

<div class="mat-mdc-form-field-icon-prefix" *ngIf="_prefixChildren.length" #iconPrefixContainer>
<div class="mat-mdc-form-field-icon-prefix" *ngIf="_hasIconPrefix" #iconPrefixContainer>
<ng-content select="[matPrefix], [matIconPrefix]"></ng-content>
</div>
<div class="mat-mdc-form-field-text-prefix" *ngIf="_prefixChildren.length" #textPrefixContainer>
<div class="mat-mdc-form-field-text-prefix" *ngIf="_hasTextPrefix" #textPrefixContainer>
<ng-content select="[matTextPrefix]"></ng-content>
</div>

Expand All @@ -59,10 +59,10 @@
<ng-content></ng-content>
</div>

<div class="mat-mdc-form-field-text-suffix" *ngIf="_suffixChildren.length">
<div class="mat-mdc-form-field-text-suffix" *ngIf="_hasTextSuffix">
<ng-content select="[matTextSuffix]"></ng-content>
</div>
<div class="mat-mdc-form-field-icon-suffix" *ngIf="_suffixChildren.length">
<div class="mat-mdc-form-field-icon-suffix" *ngIf="_hasIconSuffix">
<ng-content select="[matSuffix], [matIconSuffix]"></ng-content>
</div>
</div>
Expand Down
24 changes: 23 additions & 1 deletion src/material-experimental/mdc-form-field/form-field.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
@use '../mdc-helpers/mdc-helpers';
@import '@material/textfield/mixins.import';


// Base styles for MDC text-field, notched-outline, floating label and line-ripple.
@include mdc-text-field-without-ripple(
$query: mdc-helpers.$mat-base-styles-without-animation-query);
Expand Down Expand Up @@ -52,9 +51,32 @@
width: 100%;
}

// Vertically center icons.
.mat-mdc-form-field-icon-prefix,
.mat-mdc-form-field-icon-suffix {
align-self: center;
// The line-height can cause the prefix/suffix container to be taller than the actual icons,
// breaking the vertical centering. To prevent this we set the line-height to 0.
line-height: 0;
}

// The prefix/suffix needs a little extra padding between the icon and the infix. Because we need to
// support arbitrary height input elements, we use a different DOM structure for prefix and suffix
// icons, and therefore can't rely on MDC for these styles.
.mat-mdc-form-field-icon-prefix,
[dir='rtl'] .mat-mdc-form-field-icon-suffix {
padding: 0 form-field-sizing.$mat-form-field-icon-prefix-infix-padding 0 0;
}
.mat-mdc-form-field-icon-suffix,
[dir='rtl'] .mat-mdc-form-field-icon-prefix {
padding: 0 0 0 form-field-sizing.$mat-form-field-icon-prefix-infix-padding;
}

.mat-mdc-form-field-icon-prefix,
.mat-mdc-form-field-icon-suffix {
& > .mat-icon {
padding: 12px;
}
}

// Infix that contains the projected content (usually an input or a textarea). We ensure
Expand Down
34 changes: 28 additions & 6 deletions src/material-experimental/mdc-form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ const FLOATING_LABEL_DEFAULT_DOCKED_TRANSFORM = `translateY(-50%)`;
host: {
'class': 'mat-mdc-form-field',
'[class.mat-mdc-form-field-label-always-float]': '_shouldAlwaysFloat()',
'[class.mat-mdc-form-field-has-icon-prefix]': '_hasIconPrefix',
'[class.mat-mdc-form-field-has-icon-suffix]': '_hasIconSuffix',

// Note that these classes reuse the same names as the non-MDC version, because they can be
// considered a public API since custom form controls may use them to style themselves.
Expand Down Expand Up @@ -197,6 +199,11 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
}
private _hintLabel = '';

_hasIconPrefix = false;
_hasTextPrefix = false;
_hasIconSuffix = false;
_hasTextSuffix = false;

// Unique id for the internal form field label.
readonly _labelId = `mat-mdc-form-field-label-${nextUniqueId++}`;

Expand Down Expand Up @@ -438,13 +445,24 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
}
}

private _checkPrefixAndSuffixTypes() {
this._hasIconPrefix = !!this._prefixChildren.find(p => !p._isText);
this._hasTextPrefix = !!this._prefixChildren.find(p => p._isText);
this._hasIconSuffix = !!this._suffixChildren.find(s => !s._isText);
this._hasTextSuffix = !!this._suffixChildren.find(s => s._isText);
}

/** Initializes the prefix and suffix containers. */
private _initializePrefixAndSuffix() {
this._checkPrefixAndSuffixTypes();
// Mark the form-field as dirty whenever the prefix or suffix children change. This
// is necessary because we conditionally display the prefix/suffix containers based
// on whether there is projected content.
merge(this._prefixChildren.changes, this._suffixChildren.changes)
.subscribe(() => this._changeDetectorRef.markForCheck());
.subscribe(() => {
this._checkPrefixAndSuffixTypes();
this._changeDetectorRef.markForCheck();
});
}

/**
Expand Down Expand Up @@ -667,15 +685,19 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck
this._needsOutlineLabelOffsetUpdateOnStable = true;
return;
}
const iconPrefixContainer = this._iconPrefixContainer.nativeElement as HTMLElement;
const textPrefixContainer = this._textPrefixContainer.nativeElement as HTMLElement;
const iconPrefixContainer = this._iconPrefixContainer?.nativeElement;
const textPrefixContainer = this._textPrefixContainer?.nativeElement;
// 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.getBoundingClientRect().width +
textPrefixContainer.getBoundingClientRect().width
);
(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)
);

// Update the transform the floating label to account for the prefix container. Note
// that we do not want to overwrite the default transform for docked floating labels.
Expand Down