Skip to content

Commit 1a7a61a

Browse files
mmalerbatinayuangao
authored andcommitted
fix(input): Add pure-CSS floating label logic that will work on... (#8491)
* fix(input): Add pure-CSS floating label logic that will work on... ...server-side-rendered pages. * address comments
1 parent a6d0847 commit 1a7a61a

File tree

4 files changed

+45
-18
lines changed

4 files changed

+45
-18
lines changed

src/lib/form-field/_form-field-theme.scss

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -90,32 +90,27 @@
9090
}
9191
}
9292

93+
// Used to make instances of the _mat-form-field-placeholder-floating mixin negligibly different,
94+
// and prevent Google's CSS Optimizer from collapsing the declarations. This is needed because some
95+
// of the selectors contain pseudo-classes not recognized in all browsers. If a browser encounters
96+
// an unknown pseudo-class it will discard the entire rule set.
97+
$dedupe: 0;
98+
9399
// Applies a floating placeholder above the form field control itself.
94100
@mixin _mat-form-field-placeholder-floating($font-scale, $infix-padding, $infix-margin-top) {
95101
// We use perspective to fix the text blurriness as described here:
96102
// http://www.useragentman.com/blog/2014/05/04/fixing-typography-inside-of-2-d-css-transforms/
97103
// This results in a small jitter after the label floats on Firefox, which the
98104
// translateZ fixes.
99105
transform: translateY(-$infix-margin-top - $infix-padding) scale($font-scale) perspective(100px)
100-
translateZ(0.001px);
106+
translateZ(0.001px + $dedupe);
101107
// The tricks above used to smooth out the animation on chrome and firefox actually make things
102108
// worse on IE, so we don't include them in the IE version.
103-
-ms-transform: translateY(-$infix-margin-top - $infix-padding) scale($font-scale);
109+
-ms-transform: translateY(-$infix-margin-top - $infix-padding + $dedupe) scale($font-scale);
104110

105-
width: 100% / $font-scale;
106-
}
111+
width: 100% / $font-scale + $dedupe;
107112

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

121116
@mixin mat-form-field-typography($config) {
@@ -188,14 +183,23 @@
188183
}
189184

190185
.mat-form-field-can-float {
191-
&.mat-form-field-should-float .mat-form-field-placeholder {
186+
&.mat-form-field-should-float .mat-form-field-placeholder,
187+
.mat-input-server:focus + .mat-form-field-placeholder-wrapper .mat-form-field-placeholder {
192188
@include _mat-form-field-placeholder-floating(
193-
$subscript-font-scale, $infix-padding, $infix-margin-top);
189+
$subscript-font-scale, $infix-padding, $infix-margin-top);
194190
}
195191

196192
.mat-form-field-autofill-control:-webkit-autofill + .mat-form-field-placeholder-wrapper
197193
.mat-form-field-placeholder {
198-
@include _mat-form-field-placeholder-float-nodedupe(
194+
@include _mat-form-field-placeholder-floating(
195+
$subscript-font-scale, $infix-padding, $infix-margin-top);
196+
}
197+
198+
// Server-side rendered matInput with a placeholder attribute but placeholder not shown
199+
// (used as a pure CSS stand-in for mat-form-field-should-float).
200+
.mat-input-server[placeholder]:not(:placeholder-shown) + .mat-form-field-placeholder-wrapper
201+
.mat-form-field-placeholder {
202+
@include _mat-form-field-placeholder-floating(
199203
$subscript-font-scale, $infix-padding, $infix-margin-top);
200204
}
201205
}

src/lib/form-field/form-field.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,18 @@ $mat-form-field-default-infix-width: 180px !default;
138138
}
139139
}
140140

141+
// Server-side rendered matInput with focus or a placeholder attribute but placeholder not shown
142+
// (used as a pure CSS stand-in for mat-form-field-should-float).
143+
.mat-input-server:focus + .mat-form-field-placeholder-wrapper .mat-form-field-placeholder,
144+
.mat-input-server[placeholder]:not(:placeholder-shown) + .mat-form-field-placeholder-wrapper
145+
.mat-form-field-placeholder {
146+
display: none;
147+
148+
.mat-form-field-can-float & {
149+
display: block;
150+
}
151+
}
152+
141153
// Disable the placeholder animation when the control is not empty (this prevents placeholder
142154
// animating up when the value is set programmatically).
143155
.mat-form-field-placeholder:not(.mat-form-field-empty) {

src/lib/input/input.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {Subject} from 'rxjs/Subject';
2626
import {getMatInputUnsupportedTypeError} from './input-errors';
2727
import {MAT_INPUT_VALUE_ACCESSOR} from './input-value-accessor';
2828

29+
2930
// Invalid input type. Using one of these will throw an MatInputUnsupportedTypeError.
3031
const MAT_INPUT_INVALID_TYPES = [
3132
'button',
@@ -49,6 +50,7 @@ let nextUniqueId = 0;
4950
exportAs: 'matInput',
5051
host: {
5152
'class': 'mat-input-element mat-form-field-autofill-control',
53+
'[class.mat-input-server]': '_isServer',
5254
// Native input properties that are overwritten by Angular inputs need to be synced with
5355
// the native input element. Otherwise property bindings for those don't work.
5456
'[attr.id]': 'id',
@@ -85,6 +87,9 @@ export class MatInput implements MatFormFieldControl<any>, OnChanges, OnDestroy,
8587
/** The aria-describedby attribute on the input for improved a11y. */
8688
_ariaDescribedby: string;
8789

90+
/** Whether the component is being rendered on the server. */
91+
_isServer = false;
92+
8893
/**
8994
* Stream that emits whenever the state of the input changes such that the wrapping `MatFormField`
9095
* needs to run change detection.
@@ -185,6 +190,8 @@ export class MatInput implements MatFormFieldControl<any>, OnChanges, OnDestroy,
185190
}
186191
});
187192
}
193+
194+
this._isServer = !this._platform.isBrowser;
188195
}
189196

190197
ngOnChanges() {

src/universal-app/kitchen-sink/kitchen-sink.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ <h2>Icon</h2>
103103

104104
<h2>Input</h2>
105105

106+
<mat-form-field floatPlaceholder="never">
107+
<input matInput placeholder="name">
108+
</mat-form-field>
109+
106110
<mat-form-field>
107111
<input type="number" matInput placeholder="amount">
108112
<span matPrefix>$&nbsp;</span>

0 commit comments

Comments
 (0)