Skip to content

Commit 4fa46bc

Browse files
authoredJan 21, 2025··
fix(material/form-field): remove dependency on animations module (#30354)
Switches the form field to use CSS for the subscript animations, instead of going through the animations module.
1 parent 48117e7 commit 4fa46bc

File tree

9 files changed

+48
-33
lines changed

9 files changed

+48
-33
lines changed
 

‎src/material/form-field/_form-field-subscript.scss

+18
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33
@use '../core/tokens/token-utils';
44

55
@mixin private-form-field-subscript() {
6+
@keyframes _mat-form-field-subscript-animation {
7+
from {
8+
opacity: 0;
9+
transform: translateY(-5px);
10+
}
11+
12+
to {
13+
opacity: 1;
14+
transform: translateY(0);
15+
}
16+
}
17+
618
// Wrapper for the hints and error messages.
719
.mat-mdc-form-field-subscript-wrapper {
820
box-sizing: border-box;
@@ -17,6 +29,12 @@
1729
left: 0;
1830
right: 0;
1931
padding: 0 16px;
32+
opacity: 1;
33+
transform: translateY(0);
34+
35+
// Note: animation-duration gets set when animations are enabled.
36+
// This allows us to skip the animation on init.
37+
animation: _mat-form-field-subscript-animation 0ms cubic-bezier(0.55, 0, 0.55, 0.2);
2038
}
2139

2240
.mat-mdc-form-field-subscript-dynamic-size {

‎src/material/form-field/_mdc-text-field-structure.scss

+6-3
Original file line numberDiff line numberDiff line change
@@ -611,8 +611,11 @@
611611
}
612612

613613
.mdc-line-ripple::after {
614-
transition:
615-
transform 180ms $timing-curve,
616-
opacity 180ms $timing-curve;
614+
transition: transform 180ms $timing-curve, opacity 180ms $timing-curve;
615+
}
616+
617+
.mat-mdc-form-field-hint-wrapper,
618+
.mat-mdc-form-field-error-wrapper {
619+
animation-duration: 300ms;
617620
}
618621
}

‎src/material/form-field/form-field-animations.ts

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
/**
1818
* Animations used by the MatFormField.
1919
* @docs-private
20+
* @deprecated No longer used, will be removed.
21+
* @breaking-change 21.0.0
2022
*/
2123
export const matFormFieldAnimations: {
2224
readonly transitionMessages: AnimationTriggerMetadata;

‎src/material/form-field/form-field.html

+2-5
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,13 @@
101101
>
102102
@switch (_getDisplayedMessages()) {
103103
@case ('error') {
104-
<div
105-
class="mat-mdc-form-field-error-wrapper"
106-
[@transitionMessages]="_subscriptAnimationState"
107-
>
104+
<div class="mat-mdc-form-field-error-wrapper">
108105
<ng-content select="mat-error, [matError]"></ng-content>
109106
</div>
110107
}
111108

112109
@case ('hint') {
113-
<div class="mat-mdc-form-field-hint-wrapper" [@transitionMessages]="_subscriptAnimationState">
110+
<div class="mat-mdc-form-field-hint-wrapper">
114111
@if (hintLabel) {
115112
<mat-hint [id]="_hintLabelId">{{hintLabel}}</mat-hint>
116113
}

‎src/material/form-field/form-field.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ $_icon-prefix-infix-padding: 4px;
212212

213213
// In order to make it possible for developers to disable animations for form-fields,
214214
// we only activate the animation styles if animations are not explicitly disabled.
215-
.mat-mdc-form-field:not(.mat-form-field-no-animations) {
215+
.mat-mdc-form-field.mat-form-field-animations-enabled {
216216
@include mdc-text-field-structure.private-text-field-animations;
217217
}
218218

‎src/material/form-field/form-field.ts

+16-11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
InjectionToken,
2424
Injector,
2525
Input,
26+
NgZone,
2627
OnDestroy,
2728
QueryList,
2829
ViewChild,
@@ -49,7 +50,6 @@ import {MatFormFieldLineRipple} from './directives/line-ripple';
4950
import {MatFormFieldNotchedOutline} from './directives/notched-outline';
5051
import {MAT_PREFIX, MatPrefix} from './directives/prefix';
5152
import {MAT_SUFFIX, MatSuffix} from './directives/suffix';
52-
import {matFormFieldAnimations} from './form-field-animations';
5353
import {MatFormFieldControl as _MatFormFieldControl} from './form-field-control';
5454
import {
5555
getMatFormFieldDuplicatedHintError,
@@ -141,7 +141,6 @@ interface MatFormFieldControl<T> extends _MatFormFieldControl<T> {}
141141
exportAs: 'matFormField',
142142
templateUrl: './form-field.html',
143143
styleUrl: './form-field.css',
144-
animations: [matFormFieldAnimations.transitionMessages],
145144
host: {
146145
'class': 'mat-mdc-form-field',
147146
'[class.mat-mdc-form-field-label-always-float]': '_shouldAlwaysFloat()',
@@ -153,7 +152,6 @@ interface MatFormFieldControl<T> extends _MatFormFieldControl<T> {}
153152
'[class.mat-form-field-invalid]': '_control.errorState',
154153
'[class.mat-form-field-disabled]': '_control.disabled',
155154
'[class.mat-form-field-autofilled]': '_control.autofilled',
156-
'[class.mat-form-field-no-animations]': '_animationMode === "NoopAnimations"',
157155
'[class.mat-form-field-appearance-fill]': 'appearance == "fill"',
158156
'[class.mat-form-field-appearance-outline]': 'appearance == "outline"',
159157
'[class.mat-form-field-hide-placeholder]': '_hasFloatingLabel() && !_shouldLabelFloat()',
@@ -191,10 +189,11 @@ export class MatFormField
191189
private _dir = inject(Directionality);
192190
private _platform = inject(Platform);
193191
private _idGenerator = inject(_IdGenerator);
192+
private _ngZone = inject(NgZone);
193+
private _injector = inject(Injector);
194194
private _defaults = inject<MatFormFieldDefaultOptions>(MAT_FORM_FIELD_DEFAULT_OPTIONS, {
195195
optional: true,
196196
});
197-
_animationMode = inject(ANIMATION_MODULE_TYPE, {optional: true});
198197

199198
@ViewChild('textField') _textField: ElementRef<HTMLElement>;
200199
@ViewChild('iconPrefixContainer') _iconPrefixContainer: ElementRef<HTMLElement>;
@@ -310,9 +309,6 @@ export class MatFormField
310309
// Unique id for the hint label.
311310
readonly _hintLabelId = this._idGenerator.getId('mat-mdc-hint-');
312311

313-
/** State of the mat-hint and mat-error animations. */
314-
_subscriptAnimationState = '';
315-
316312
/** Gets the current form field control */
317313
get _control(): MatFormFieldControl<any> {
318314
return this._explicitFormFieldControl || this._formFieldControl;
@@ -329,8 +325,7 @@ export class MatFormField
329325
private _stateChanges: Subscription | undefined;
330326
private _valueChanges: Subscription | undefined;
331327
private _describedByChanges: Subscription | undefined;
332-
333-
private _injector = inject(Injector);
328+
protected readonly _animationsDisabled: boolean;
334329

335330
constructor(...args: unknown[]);
336331

@@ -346,14 +341,24 @@ export class MatFormField
346341
this.color = defaults.color;
347342
}
348343
}
344+
345+
this._animationsDisabled = inject(ANIMATION_MODULE_TYPE, {optional: true}) === 'NoopAnimations';
349346
}
350347

351348
ngAfterViewInit() {
352349
// Initial focus state sync. This happens rarely, but we want to account for
353350
// it in case the form field control has "focused" set to true on init.
354351
this._updateFocusState();
355-
// Enable animations now. This ensures we don't animate on initial render.
356-
this._subscriptAnimationState = 'enter';
352+
353+
if (!this._animationsDisabled) {
354+
this._ngZone.runOutsideAngular(() => {
355+
// Enable animations after a certain amount of time so that they don't run on init.
356+
setTimeout(() => {
357+
this._elementRef.nativeElement.classList.add('mat-form-field-animations-enabled');
358+
}, 300);
359+
});
360+
}
361+
357362
// Because the above changes a value used in the template after it was checked, we need
358363
// to trigger CD or the change might not be reflected if there is no other CD scheduled.
359364
this._changeDetectorRef.detectChanges();

‎src/material/input/input.spec.ts

-9
Original file line numberDiff line numberDiff line change
@@ -486,15 +486,6 @@ describe('MatMdcInput without forms', () => {
486486
expect(selectEl.disabled).toBe(true);
487487
}));
488488

489-
it('should add a class to the form-field if animations are disabled', () => {
490-
configureTestingModule(MatInputWithId, {animations: false});
491-
const fixture = TestBed.createComponent(MatInputWithId);
492-
fixture.detectChanges();
493-
494-
const formFieldEl = fixture.nativeElement.querySelector('.mat-mdc-form-field');
495-
expect(formFieldEl.classList).toContain('mat-form-field-no-animations');
496-
});
497-
498489
it('should add a class to the form field if it has a native select', fakeAsync(() => {
499490
const fixture = createComponent(MatInputSelect);
500491
fixture.detectChanges();

‎src/material/select/select.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ div.mat-mdc-select-panel {
185185
@include token-utils.create-token-slot(color, placeholder-text-color);
186186
}
187187

188-
.mat-form-field-no-animations &,
188+
.mat-mdc-form-field:not(.mat-form-field-animations-enabled) &,
189189
._mat-animation-noopable & {
190190
transition: none;
191191
}

‎tools/public_api_guard/material/form-field.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export class MatFormField implements FloatingLabelParent, AfterContentInit, Afte
6565
constructor(...args: unknown[]);
6666
_animateAndLockLabel(): void;
6767
// (undocumented)
68-
_animationMode: "NoopAnimations" | "BrowserAnimations" | null;
68+
protected readonly _animationsDisabled: boolean;
6969
get appearance(): MatFormFieldAppearance;
7070
set appearance(value: MatFormFieldAppearance);
7171
color: ThemePalette;
@@ -131,7 +131,6 @@ export class MatFormField implements FloatingLabelParent, AfterContentInit, Afte
131131
_shouldForward(prop: keyof AbstractControlDirective): boolean;
132132
// (undocumented)
133133
_shouldLabelFloat(): boolean;
134-
_subscriptAnimationState: string;
135134
get subscriptSizing(): SubscriptSizing;
136135
set subscriptSizing(value: SubscriptSizing);
137136
// (undocumented)
@@ -148,7 +147,7 @@ export class MatFormField implements FloatingLabelParent, AfterContentInit, Afte
148147
static ɵfac: i0.ɵɵFactoryDeclaration<MatFormField, never>;
149148
}
150149

151-
// @public
150+
// @public @deprecated
152151
export const matFormFieldAnimations: {
153152
readonly transitionMessages: AnimationTriggerMetadata;
154153
};

0 commit comments

Comments
 (0)
Please sign in to comment.