Skip to content

Commit 18400e7

Browse files
authored
refactor(material/core): generalize lazy ripple logic (#26897)
* refactor(material/core): generalize lazy ripple logic * fixup! refactor(material/core): generalize lazy ripple logic * fixup! refactor(material/core): generalize lazy ripple logic * fixup! refactor(material/core): generalize lazy ripple logic * fixup! refactor(material/core): generalize lazy ripple logic
1 parent f74ade4 commit 18400e7

File tree

10 files changed

+240
-198
lines changed

10 files changed

+240
-198
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
/src/material/core/mdc-helpers/** @mmalerba
7777
/src/material/core/option/** @crisbeto
7878
/src/material/core/placeholder/** @mmalerba
79+
/src/material/core/private/** @wagnermaciel
7980
/src/material/core/ripple/** @devversion
8081
/src/material/core/selection/** @andrewseguin
8182
/src/material/core/selection/pseudo*/** @crisbeto @andrewseguin

src/material/button/button-base.ts

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
*/
88

99
import {FocusMonitor, FocusOrigin} from '@angular/cdk/a11y';
10+
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1011
import {Platform} from '@angular/cdk/platform';
1112
import {
1213
AfterViewInit,
1314
Directive,
1415
ElementRef,
1516
inject,
1617
NgZone,
17-
OnChanges,
1818
OnDestroy,
1919
OnInit,
2020
} from '@angular/core';
@@ -26,8 +26,8 @@ import {
2626
mixinColor,
2727
mixinDisabled,
2828
mixinDisableRipple,
29+
MatRippleLoader,
2930
} from '@angular/material/core';
30-
import {MAT_BUTTON_RIPPLE_UNINITIALIZED, MatButtonLazyLoader} from './button-lazy-loader';
3131

3232
/** Inputs common to all buttons. */
3333
export const MAT_BUTTON_INPUTS = ['disabled', 'disableRipple', 'color'];
@@ -43,7 +43,6 @@ export const MAT_BUTTON_HOST = {
4343
// Add a class that applies to all buttons. This makes it easier to target if somebody
4444
// wants to target all Material buttons.
4545
'[class.mat-mdc-button-base]': 'true',
46-
[MAT_BUTTON_RIPPLE_UNINITIALIZED]: '',
4746
};
4847

4948
/** List of classes to add to buttons instances based on host attribute selector. */
@@ -94,15 +93,15 @@ export const _MatButtonMixin = mixinColor(
9493
@Directive()
9594
export class MatButtonBase
9695
extends _MatButtonMixin
97-
implements CanDisable, CanColor, CanDisableRipple, AfterViewInit, OnChanges, OnDestroy
96+
implements CanDisable, CanColor, CanDisableRipple, AfterViewInit, OnDestroy
9897
{
9998
private readonly _focusMonitor = inject(FocusMonitor);
10099

101100
/**
102101
* Handles the lazy creation of the MatButton ripple.
103102
* Used to improve initial load time of large applications.
104103
*/
105-
_rippleLoader: MatButtonLazyLoader = inject(MatButtonLazyLoader);
104+
_rippleLoader: MatRippleLoader = inject(MatRippleLoader);
106105

107106
/** Whether this button is a FAB. Used to apply the correct class on the ripple. */
108107
_isFab = false;
@@ -113,17 +112,33 @@ export class MatButtonBase
113112
* @breaking-change 17.0.0
114113
*/
115114
get ripple(): MatRipple {
116-
if (!this._ripple && this._rippleLoader) {
117-
this._ripple = this._rippleLoader._createMatRipple(this._elementRef.nativeElement);
118-
}
119-
return this._ripple!;
115+
return this._rippleLoader?.getRipple(this._elementRef.nativeElement)!;
120116
}
121117
set ripple(v: MatRipple) {
122-
this._ripple = v;
118+
this._rippleLoader?.attachRipple(this._elementRef.nativeElement, v);
123119
}
124120

125-
/** @docs-private Reference to the MatRipple instance of the button. */
126-
protected _ripple?: MatRipple;
121+
// We override `disableRipple` and `disabled` so we can hook into
122+
// their setters and update the ripple disabled state accordingly.
123+
124+
/** Whether the ripple effect is disabled or not. */
125+
override get disableRipple(): boolean {
126+
return this._disableRipple;
127+
}
128+
override set disableRipple(value: any) {
129+
this._disableRipple = coerceBooleanProperty(value);
130+
this._updateRippleDisabled();
131+
}
132+
private _disableRipple: boolean = false;
133+
134+
override get disabled(): boolean {
135+
return this._disabled;
136+
}
137+
override set disabled(value: any) {
138+
this._disabled = coerceBooleanProperty(value);
139+
this._updateRippleDisabled();
140+
}
141+
private _disabled: boolean = false;
127142

128143
constructor(
129144
elementRef: ElementRef,
@@ -133,6 +148,10 @@ export class MatButtonBase
133148
) {
134149
super(elementRef);
135150

151+
this._rippleLoader?.configureRipple(this._elementRef.nativeElement, {
152+
className: 'mat-mdc-button-ripple',
153+
});
154+
136155
const classList = (elementRef.nativeElement as HTMLElement).classList;
137156

138157
// For each of the variant selectors that is present in the button's host
@@ -150,12 +169,6 @@ export class MatButtonBase
150169
this._focusMonitor.monitor(this._elementRef, true);
151170
}
152171

153-
ngOnChanges() {
154-
if (this._ripple) {
155-
this._ripple.disabled = this.disableRipple || this.disabled;
156-
}
157-
}
158-
159172
ngOnDestroy() {
160173
this._focusMonitor.stopMonitoring(this._elementRef);
161174
}
@@ -173,6 +186,13 @@ export class MatButtonBase
173186
private _hasHostAttributes(...attributes: string[]) {
174187
return attributes.some(attribute => this._elementRef.nativeElement.hasAttribute(attribute));
175188
}
189+
190+
private _updateRippleDisabled(): void {
191+
this._rippleLoader?.setDisabled(
192+
this._elementRef.nativeElement,
193+
this.disableRipple || this.disabled,
194+
);
195+
}
176196
}
177197

178198
/** Shared inputs by buttons using the `<a>` tag */
@@ -195,7 +215,6 @@ export const MAT_ANCHOR_HOST = {
195215
// Add a class that applies to all buttons. This makes it easier to target if somebody
196216
// wants to target all Material buttons.
197217
'[class.mat-mdc-button-base]': 'true',
198-
[MAT_BUTTON_RIPPLE_UNINITIALIZED]: '',
199218
};
200219

201220
/**

src/material/button/button-lazy-loader.ts

Lines changed: 0 additions & 151 deletions
This file was deleted.

src/material/button/button.spec.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {By} from '@angular/platform-browser';
44
import {MatButtonModule, MatButton, MatFabDefaultOptions, MAT_FAB_DEFAULT_OPTIONS} from './index';
55
import {MatRipple, ThemePalette} from '@angular/material/core';
66
import {createMouseEvent, dispatchEvent} from '@angular/cdk/testing/private';
7-
import {MAT_BUTTON_RIPPLE_UNINITIALIZED} from './button-lazy-loader';
87

98
describe('MDC-based MatButton', () => {
109
beforeEach(waitForAsync(() => {
@@ -317,9 +316,6 @@ describe('MDC-based MatButton', () => {
317316
const fab = fixture.debugElement.query(By.css('button[mat-fab]'))!;
318317
let ripple = fab.nativeElement.querySelector('.mat-mdc-button-ripple');
319318
expect(ripple).withContext('Expect ripple to be absent before user interaction').toBeNull();
320-
expect(fab.nativeElement.hasAttribute(MAT_BUTTON_RIPPLE_UNINITIALIZED))
321-
.withContext('Expect mat-button to have the "uninitialized" attr before user interaction')
322-
.toBeTrue();
323319

324320
// Referencing the ripple should instantiate the ripple.
325321
expect(fab.componentInstance.ripple).toBeDefined();
@@ -328,11 +324,6 @@ describe('MDC-based MatButton', () => {
328324
expect(ripple)
329325
.withContext('Expect ripple to be present after user interaction')
330326
.not.toBeNull();
331-
expect(fab.nativeElement.hasAttribute(MAT_BUTTON_RIPPLE_UNINITIALIZED))
332-
.withContext(
333-
'Expect mat-button NOT to have the "uninitialized" attr after user interaction',
334-
)
335-
.toBeFalse();
336327
});
337328

338329
// Ensure each of these events triggers the initialization of the button ripple.
@@ -341,12 +332,10 @@ describe('MDC-based MatButton', () => {
341332
const fab = fixture.debugElement.query(By.css('button[mat-fab]'))!;
342333
let ripple = fab.nativeElement.querySelector('.mat-mdc-button-ripple');
343334
expect(ripple).toBeNull();
344-
expect(fab.nativeElement.hasAttribute(MAT_BUTTON_RIPPLE_UNINITIALIZED)).toBeTrue();
345335

346336
dispatchEvent(fab.nativeElement, createMouseEvent(event));
347337
ripple = fab.nativeElement.querySelector('.mat-mdc-button-ripple');
348338
expect(ripple).not.toBeNull();
349-
expect(fab.nativeElement.hasAttribute(MAT_BUTTON_RIPPLE_UNINITIALIZED)).toBeFalse();
350339
});
351340
}
352341
});

src/material/button/icon-button.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import {
2626
MatAnchorBase,
2727
MatButtonBase,
2828
} from './button-base';
29-
import {MatRipple} from '@angular/material/core';
3029

3130
/**
3231
* Material Design icon button component. This type of button displays a single interactive icon for
@@ -44,26 +43,15 @@ import {MatRipple} from '@angular/material/core';
4443
changeDetection: ChangeDetectionStrategy.OnPush,
4544
})
4645
export class MatIconButton extends MatButtonBase {
47-
/**
48-
* Reference to the MatRipple instance of the button.
49-
* @deprecated Considered an implementation detail. To be removed.
50-
* @breaking-change 17.0.0
51-
*/
52-
override get ripple(): MatRipple {
53-
if (!this._ripple && this._rippleLoader) {
54-
this._ripple = this._rippleLoader._createMatRipple(this._elementRef.nativeElement);
55-
this._ripple!.centered = true;
56-
}
57-
return this._ripple!;
58-
}
59-
6046
constructor(
6147
elementRef: ElementRef,
6248
platform: Platform,
6349
ngZone: NgZone,
6450
@Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,
6551
) {
6652
super(elementRef, platform, ngZone, animationMode);
53+
54+
this._rippleLoader.configureRipple(this._elementRef.nativeElement, {centered: true});
6755
}
6856
}
6957

src/material/core/private/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
export {MatRippleLoader} from './ripple-loader';

0 commit comments

Comments
 (0)