Skip to content

Commit b3e8a96

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

File tree

10 files changed

+240
-196
lines changed

10 files changed

+240
-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: 38 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,
@@ -26,8 +27,8 @@ import {
2627
mixinColor,
2728
mixinDisabled,
2829
mixinDisableRipple,
30+
MatRippleLoader,
2931
} from '@angular/material/core';
30-
import {MAT_BUTTON_RIPPLE_UNINITIALIZED, MatButtonLazyLoader} from './button-lazy-loader';
3132

3233
/** Inputs common to all buttons. */
3334
export const MAT_BUTTON_INPUTS = ['disabled', 'disableRipple', 'color'];
@@ -43,7 +44,6 @@ export const MAT_BUTTON_HOST = {
4344
// Add a class that applies to all buttons. This makes it easier to target if somebody
4445
// wants to target all Material buttons.
4546
'[class.mat-mdc-button-base]': 'true',
46-
[MAT_BUTTON_RIPPLE_UNINITIALIZED]: '',
4747
};
4848

4949
/** List of classes to add to buttons instances based on host attribute selector. */
@@ -94,15 +94,15 @@ export const _MatButtonMixin = mixinColor(
9494
@Directive()
9595
export class MatButtonBase
9696
extends _MatButtonMixin
97-
implements CanDisable, CanColor, CanDisableRipple, AfterViewInit, OnChanges, OnDestroy
97+
implements CanDisable, CanColor, CanDisableRipple, AfterViewInit, OnDestroy
9898
{
9999
private readonly _focusMonitor = inject(FocusMonitor);
100100

101101
/**
102102
* Handles the lazy creation of the MatButton ripple.
103103
* Used to improve initial load time of large applications.
104104
*/
105-
_rippleLoader: MatButtonLazyLoader = inject(MatButtonLazyLoader);
105+
_rippleLoader: MatRippleLoader = inject(MatRippleLoader);
106106

107107
/** Whether this button is a FAB. Used to apply the correct class on the ripple. */
108108
_isFab = false;
@@ -113,17 +113,33 @@ export class MatButtonBase
113113
* @breaking-change 17.0.0
114114
*/
115115
get ripple(): MatRipple {
116-
if (!this._ripple && this._rippleLoader) {
117-
this._ripple = this._rippleLoader._createMatRipple(this._elementRef.nativeElement);
118-
}
119-
return this._ripple!;
116+
return this._rippleLoader?.getRipple(this._elementRef.nativeElement)!;
120117
}
121118
set ripple(v: MatRipple) {
122-
this._ripple = v;
119+
this._rippleLoader?.attachRipple(this._elementRef.nativeElement, v);
123120
}
124121

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

128144
constructor(
129145
elementRef: ElementRef,
@@ -133,6 +149,10 @@ export class MatButtonBase
133149
) {
134150
super(elementRef);
135151

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

138158
// For each of the variant selectors that is present in the button's host
@@ -150,12 +170,6 @@ export class MatButtonBase
150170
this._focusMonitor.monitor(this._elementRef, true);
151171
}
152172

153-
ngOnChanges() {
154-
if (this._ripple) {
155-
this._ripple.disabled = this.disableRipple || this.disabled;
156-
}
157-
}
158-
159173
ngOnDestroy() {
160174
this._focusMonitor.stopMonitoring(this._elementRef);
161175
}
@@ -173,6 +187,13 @@ export class MatButtonBase
173187
private _hasHostAttributes(...attributes: string[]) {
174188
return attributes.some(attribute => this._elementRef.nativeElement.hasAttribute(attribute));
175189
}
190+
191+
private _updateRippleDisabled(): void {
192+
this._rippleLoader?.setDisabled(
193+
this._elementRef.nativeElement,
194+
this.disableRipple || this.disabled,
195+
);
196+
}
176197
}
177198

178199
/** Shared inputs by buttons using the `<a>` tag */
@@ -195,7 +216,6 @@ export const MAT_ANCHOR_HOST = {
195216
// Add a class that applies to all buttons. This makes it easier to target if somebody
196217
// wants to target all Material buttons.
197218
'[class.mat-mdc-button-base]': 'true',
198-
[MAT_BUTTON_RIPPLE_UNINITIALIZED]: '',
199219
};
200220

201221
/**

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)