Skip to content

fix(input): Add pure-CSS floating label logic that will work on... #8491

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

Merged
merged 2 commits into from
Nov 28, 2017
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
40 changes: 22 additions & 18 deletions src/lib/form-field/_form-field-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -90,32 +90,27 @@
}
}

// Used to make instances of the _mat-form-field-placeholder-floating mixin negligibly different,
// and prevent Google's CSS Optimizer from collapsing the declarations. This is needed because some
// of the selectors contain pseudo-classes not recognized in all browsers. If a browser encounters
// an unknown pseudo-class it will discard the entire rule set.
$dedupe: 0;

// Applies a floating placeholder above the form field control itself.
@mixin _mat-form-field-placeholder-floating($font-scale, $infix-padding, $infix-margin-top) {
// We use perspective to fix the text blurriness as described here:
// http://www.useragentman.com/blog/2014/05/04/fixing-typography-inside-of-2-d-css-transforms/
// This results in a small jitter after the label floats on Firefox, which the
// translateZ fixes.
transform: translateY(-$infix-margin-top - $infix-padding) scale($font-scale) perspective(100px)
translateZ(0.001px);
translateZ(0.001px + $dedupe);
// The tricks above used to smooth out the animation on chrome and firefox actually make things
// worse on IE, so we don't include them in the IE version.
-ms-transform: translateY(-$infix-margin-top - $infix-padding) scale($font-scale);
-ms-transform: translateY(-$infix-margin-top - $infix-padding + $dedupe) scale($font-scale);

width: 100% / $font-scale;
}
width: 100% / $font-scale + $dedupe;

// This is a total duplicate of the mixin above with insignificant values added to the rules.
// This exists because the mixin is used in two places. When Google's CSS Optimizer runs over this
// css (after compiling from sass), it combines those two declarations into one. However, one of
// those places uses `:-webkit-autofill`. When Firefox encounters this unknown pseuedo-class,
// it ignores the entire rule. To work around this, we force one of the delcarations to be
// technically different but still render the same by adding a tiny value to the transform / width.
@mixin _mat-form-field-placeholder-float-nodedupe($font-scale, $infix-padding, $infix-margin-top) {
transform: translateY(-$infix-margin-top - $infix-padding) scale($font-scale) perspective(100px)
translateZ(0.002px);
-ms-transform: translateY(-$infix-margin-top - $infix-padding) scale($font-scale);
width: (100% / $font-scale) + 0.0001;
$dedupe: $dedupe + 0.00001 !global;
}

@mixin mat-form-field-typography($config) {
Expand Down Expand Up @@ -188,14 +183,23 @@
}

.mat-form-field-can-float {
&.mat-form-field-should-float .mat-form-field-placeholder {
&.mat-form-field-should-float .mat-form-field-placeholder,
.mat-input-server:focus + .mat-form-field-placeholder-wrapper .mat-form-field-placeholder {
@include _mat-form-field-placeholder-floating(
$subscript-font-scale, $infix-padding, $infix-margin-top);
$subscript-font-scale, $infix-padding, $infix-margin-top);
}

.mat-form-field-autofill-control:-webkit-autofill + .mat-form-field-placeholder-wrapper
.mat-form-field-placeholder {
@include _mat-form-field-placeholder-float-nodedupe(
@include _mat-form-field-placeholder-floating(
$subscript-font-scale, $infix-padding, $infix-margin-top);
}

// Server-side rendered matInput with a placeholder attribute but placeholder not shown
// (used as a pure CSS stand-in for mat-form-field-should-float).
.mat-input-server[placeholder]:not(:placeholder-shown) + .mat-form-field-placeholder-wrapper
.mat-form-field-placeholder {
@include _mat-form-field-placeholder-floating(
$subscript-font-scale, $infix-padding, $infix-margin-top);
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/lib/form-field/form-field.scss
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ $mat-form-field-default-infix-width: 180px !default;
}
}

// Server-side rendered matInput with focus or a placeholder attribute but placeholder not shown
// (used as a pure CSS stand-in for mat-form-field-should-float).
.mat-input-server:focus + .mat-form-field-placeholder-wrapper .mat-form-field-placeholder,
.mat-input-server[placeholder]:not(:placeholder-shown) + .mat-form-field-placeholder-wrapper
.mat-form-field-placeholder {
display: none;

.mat-form-field-can-float & {
display: block;
}
}

// Disable the placeholder animation when the control is not empty (this prevents placeholder
// animating up when the value is set programmatically).
.mat-form-field-placeholder:not(.mat-form-field-empty) {
Expand Down
7 changes: 7 additions & 0 deletions src/lib/input/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {Subject} from 'rxjs/Subject';
import {getMatInputUnsupportedTypeError} from './input-errors';
import {MAT_INPUT_VALUE_ACCESSOR} from './input-value-accessor';


// Invalid input type. Using one of these will throw an MatInputUnsupportedTypeError.
const MAT_INPUT_INVALID_TYPES = [
'button',
Expand All @@ -50,6 +51,7 @@ let nextUniqueId = 0;
exportAs: 'matInput',
host: {
'class': 'mat-input-element mat-form-field-autofill-control',
'[class.mat-input-server]': '_isServer',
// 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.
'[attr.id]': 'id',
Expand Down Expand Up @@ -86,6 +88,9 @@ export class MatInput implements MatFormFieldControl<any>, OnChanges, OnDestroy,
/** The aria-describedby attribute on the input for improved a11y. */
_ariaDescribedby: string;

/** Whether the component is being rendered on the server. */
_isServer = false;

/**
* Stream that emits whenever the state of the input changes such that the wrapping `MatFormField`
* needs to run change detection.
Expand Down Expand Up @@ -187,6 +192,8 @@ export class MatInput implements MatFormFieldControl<any>, OnChanges, OnDestroy,
}
});
}

this._isServer = !this._platform.isBrowser;
}

ngOnChanges() {
Expand Down
4 changes: 4 additions & 0 deletions src/universal-app/kitchen-sink/kitchen-sink.html
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ <h2>Icon</h2>

<h2>Input</h2>

<mat-form-field floatPlaceholder="never">
<input matInput placeholder="name">
</mat-form-field>

<mat-form-field>
<input type="number" matInput placeholder="amount">
<span matPrefix>$&nbsp;</span>
Expand Down