Skip to content

Commit c270625

Browse files
committed
refactor(material/core): generalize lazy ripple logic
1 parent c85995c commit c270625

File tree

10 files changed

+238
-196
lines changed

10 files changed

+238
-196
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: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
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,
@@ -25,8 +26,8 @@ import {
2526
mixinColor,
2627
mixinDisabled,
2728
mixinDisableRipple,
29+
MatRippleLoader,
2830
} from '@angular/material/core';
29-
import {MAT_BUTTON_RIPPLE_UNINITIALIZED, MatButtonLazyLoader} from './button-lazy-loader';
3031

3132
/** Inputs common to all buttons. */
3233
export const MAT_BUTTON_INPUTS = ['disabled', 'disableRipple', 'color'];
@@ -42,8 +43,6 @@ export const MAT_BUTTON_HOST = {
4243
// Add a class that applies to all buttons. This makes it easier to target if somebody
4344
// wants to target all Material buttons.
4445
'[class.mat-mdc-button-base]': 'true',
45-
[MAT_BUTTON_RIPPLE_UNINITIALIZED]: '',
46-
'[attr.mat-button-disabled]': '_isRippleDisabled()',
4746
'[attr.mat-button-is-fab]': '_isFab',
4847
};
4948

@@ -103,29 +102,45 @@ export class MatButtonBase
103102
* Handles the lazy creation of the MatButton ripple.
104103
* Used to improve initial load time of large applications.
105104
*/
106-
_rippleLoader: MatButtonLazyLoader = inject(MatButtonLazyLoader);
105+
_rippleLoader: MatRippleLoader = inject(MatRippleLoader);
107106

108107
/** Whether this button is a FAB. Used to apply the correct class on the ripple. */
109108
_isFab = false;
110109

110+
// We override `disableRipple` and `disabled` so we can hook into
111+
// their setters and update the ripple disabled state accordingly.
112+
113+
/** Whether the ripple effect is disabled or not. */
114+
override get disableRipple(): boolean {
115+
return this._disableRipple;
116+
}
117+
override set disableRipple(value: any) {
118+
this._disableRipple = coerceBooleanProperty(value);
119+
this._updateRippleDisabled();
120+
}
121+
private _disableRipple: boolean = false;
122+
123+
override get disabled(): boolean {
124+
return this._disabled;
125+
}
126+
override set disabled(value: any) {
127+
this._disabled = coerceBooleanProperty(value);
128+
this._updateRippleDisabled();
129+
}
130+
private _disabled: boolean = false;
131+
111132
/**
112133
* Reference to the MatRipple instance of the button.
113134
* @deprecated Considered an implementation detail. To be removed.
114135
* @breaking-change 17.0.0
115136
*/
116137
get ripple(): MatRipple {
117-
if (!this._ripple && this._rippleLoader) {
118-
this._ripple = this._rippleLoader._createMatRipple(this._elementRef.nativeElement);
119-
}
120-
return this._ripple!;
138+
return this._rippleLoader?.getRipple(this._elementRef.nativeElement)!;
121139
}
122140
set ripple(v: MatRipple) {
123-
this._ripple = v;
141+
this._rippleLoader?.attachRipple(this._elementRef.nativeElement, v);
124142
}
125143

126-
/** @docs-private Reference to the MatRipple instance of the button. */
127-
protected _ripple?: MatRipple;
128-
129144
constructor(
130145
elementRef: ElementRef,
131146
public _platform: Platform,
@@ -134,6 +149,10 @@ export class MatButtonBase
134149
) {
135150
super(elementRef);
136151

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

139158
// For each of the variant selectors that is present in the button's host
@@ -169,10 +188,11 @@ export class MatButtonBase
169188
return attributes.some(attribute => this._elementRef.nativeElement.hasAttribute(attribute));
170189
}
171190

172-
_isRippleDisabled() {
173-
if (this._ripple) {
174-
this._ripple.disabled = this.disableRipple || this.disabled;
175-
}
191+
private _updateRippleDisabled(): void {
192+
this._rippleLoader?.setDisabled(
193+
this._elementRef.nativeElement,
194+
this.disableRipple || this.disabled,
195+
);
176196
}
177197
}
178198

@@ -196,8 +216,6 @@ export const MAT_ANCHOR_HOST = {
196216
// Add a class that applies to all buttons. This makes it easier to target if somebody
197217
// wants to target all Material buttons.
198218
'[class.mat-mdc-button-base]': 'true',
199-
[MAT_BUTTON_RIPPLE_UNINITIALIZED]: '',
200-
'[attr.mat-button-disabled]': '_isRippleDisabled()',
201219
'[attr.mat-button-is-fab]': '_isFab',
202220
};
203221

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)