Skip to content

Commit 73ba2c0

Browse files
authored
refactor(material/chips): switch to lazy ripple rendering (#27440)
1 parent 8250a0d commit 73ba2c0

File tree

10 files changed

+51
-33
lines changed

10 files changed

+51
-33
lines changed

src/material/chips/chip-option.html

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
<span matRipple class="mat-mdc-chip-ripple"
2-
[matRippleDisabled]="_isRippleDisabled()"
3-
[matRippleCentered]="_isRippleCentered"
4-
[matRippleTrigger]="_elementRef.nativeElement"></span>
51
<span class="mat-mdc-chip-focus-overlay"></span>
62

73
<span class="mdc-evolution-chip__cell mdc-evolution-chip__cell--primary">

src/material/chips/chip-row.html

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
<ng-container *ngIf="!_isEditing">
2-
<span matRipple class="mat-mdc-chip-ripple"
3-
[matRippleDisabled]="_isRippleDisabled()"
4-
[matRippleCentered]="_isRippleCentered"
5-
[matRippleTrigger]="_elementRef.nativeElement"></span>
62
<span class="mat-mdc-chip-focus-overlay"></span>
73
</ng-container>
84

9-
105
<span class="mdc-evolution-chip__cell mdc-evolution-chip__cell--primary" role="gridcell"
116
matChipAction
127
[tabIndex]="tabIndex"

src/material/chips/chip-row.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ export class MatChipRow extends MatChip implements AfterViewInit {
207207
}
208208
}
209209

210+
override _isRippleDisabled(): boolean {
211+
return super._isRippleDisabled() || this._isEditing;
212+
}
213+
210214
/**
211215
* Gets the projected chip edit input, or the default input if none is projected in. One of these
212216
* two values is guaranteed to be defined.

src/material/chips/chip.html

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
<span matRipple class="mat-mdc-chip-ripple"
2-
[matRippleDisabled]="_isRippleDisabled()"
3-
[matRippleCentered]="_isRippleCentered"
4-
[matRippleTrigger]="_elementRef.nativeElement"></span>
51
<span class="mat-mdc-chip-focus-overlay"></span>
62

73
<span class="mdc-evolution-chip__cell mdc-evolution-chip__cell--primary">

src/material/chips/chip.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@
184184
}
185185

186186
// The ripple container should match the bounds of the entire chip.
187-
.mat-ripple.mat-mdc-chip-ripple {
187+
.mat-mdc-chip .mat-ripple.mat-mdc-chip-ripple {
188188
@include layout-common.fill;
189189

190190
// Disable pointer events for the ripple container and state overlay because the container

src/material/chips/chip.spec.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {Directionality} from '@angular/cdk/bidi';
22
import {Component, DebugElement, ViewChild} from '@angular/core';
33
import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing';
4-
import {MatRipple} from '@angular/material/core';
54
import {By} from '@angular/platform-browser';
65
import {Subject} from 'rxjs';
76
import {MatChip, MatChipEvent, MatChipSet, MatChipsModule} from './index';
@@ -11,8 +10,6 @@ describe('MDC-based MatChip', () => {
1110
let chipDebugElement: DebugElement;
1211
let chipNativeElement: HTMLElement;
1312
let chipInstance: MatChip;
14-
let chipRippleDebugElement: DebugElement;
15-
let chipRippleInstance: MatRipple;
1613

1714
let dir = 'ltr';
1815

@@ -74,9 +71,8 @@ describe('MDC-based MatChip', () => {
7471
fixture = TestBed.createComponent(BasicChip);
7572
fixture.detectChanges();
7673
chipDebugElement = fixture.debugElement.query(By.directive(MatChip))!;
77-
chipRippleDebugElement = chipDebugElement.query(By.directive(MatRipple))!;
78-
chipRippleInstance = chipRippleDebugElement.injector.get<MatRipple>(MatRipple);
79-
expect(chipRippleInstance.disabled)
74+
chipInstance = chipDebugElement.injector.get<MatChip>(MatChip);
75+
expect(chipInstance.ripple.disabled)
8076
.withContext('Expected basic chip ripples to be disabled.')
8177
.toBe(true);
8278
});
@@ -93,8 +89,6 @@ describe('MDC-based MatChip', () => {
9389
chipDebugElement = fixture.debugElement.query(By.directive(MatChip))!;
9490
chipNativeElement = chipDebugElement.nativeElement;
9591
chipInstance = chipDebugElement.injector.get<MatChip>(MatChip);
96-
chipRippleDebugElement = chipDebugElement.query(By.directive(MatRipple))!;
97-
chipRippleInstance = chipRippleDebugElement.injector.get<MatRipple>(MatRipple);
9892
testComponent = fixture.debugElement.componentInstance;
9993
primaryAction = chipNativeElement.querySelector('.mdc-evolution-chip__action--primary')!;
10094
});
@@ -137,27 +131,27 @@ describe('MDC-based MatChip', () => {
137131
});
138132

139133
it('should be able to disable ripples with the `[rippleDisabled]` input', () => {
140-
expect(chipRippleInstance.disabled)
134+
expect(chipInstance.ripple.disabled)
141135
.withContext('Expected chip ripples to be enabled.')
142136
.toBe(false);
143137

144138
testComponent.rippleDisabled = true;
145139
fixture.detectChanges();
146140

147-
expect(chipRippleInstance.disabled)
141+
expect(chipInstance.ripple.disabled)
148142
.withContext('Expected chip ripples to be disabled.')
149143
.toBe(true);
150144
});
151145

152146
it('should disable ripples when the chip is disabled', () => {
153-
expect(chipRippleInstance.disabled)
147+
expect(chipInstance.ripple.disabled)
154148
.withContext('Expected chip ripples to be enabled.')
155149
.toBe(false);
156150

157151
testComponent.disabled = true;
158152
fixture.detectChanges();
159153

160-
expect(chipRippleInstance.disabled)
154+
expect(chipInstance.ripple.disabled)
161155
.withContext('Expected chip ripples to be disabled.')
162156
.toBe(true);
163157
});

src/material/chips/chip.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import {
2929
ContentChildren,
3030
QueryList,
3131
OnInit,
32+
DoCheck,
33+
inject,
3234
} from '@angular/core';
3335
import {DOCUMENT} from '@angular/common';
3436
import {
@@ -43,6 +45,7 @@ import {
4345
mixinTabIndex,
4446
mixinDisabled,
4547
RippleGlobalOptions,
48+
MatRippleLoader,
4649
} from '@angular/material/core';
4750
import {FocusMonitor} from '@angular/cdk/a11y';
4851
import {merge, Subject, Subscription} from 'rxjs';
@@ -123,14 +126,12 @@ export class MatChip
123126
CanColor,
124127
CanDisableRipple,
125128
CanDisable,
129+
DoCheck,
126130
HasTabIndex,
127131
OnDestroy
128132
{
129133
protected _document: Document;
130134

131-
/** Whether the ripple is centered on the chip. */
132-
readonly _isRippleCentered = false;
133-
134135
/** Emits when the chip is focused. */
135136
readonly _onFocus = new Subject<MatChipEvent>();
136137

@@ -251,11 +252,22 @@ export class MatChip
251252
* @deprecated Considered an implementation detail. To be removed.
252253
* @breaking-change 17.0.0
253254
*/
254-
@ViewChild(MatRipple) ripple: MatRipple;
255+
get ripple(): MatRipple {
256+
return this._rippleLoader?.getRipple(this._elementRef.nativeElement)!;
257+
}
258+
set ripple(v: MatRipple) {
259+
this._rippleLoader?.attachRipple(this._elementRef.nativeElement, v);
260+
}
255261

256262
/** Action receiving the primary set of user interactions. */
257263
@ViewChild(MatChipAction) primaryAction: MatChipAction;
258264

265+
/**
266+
* Handles the lazy creation of the MatChip ripple.
267+
* Used to improve initial load time of large applications.
268+
*/
269+
_rippleLoader: MatRippleLoader = inject(MatRippleLoader);
270+
259271
constructor(
260272
public _changeDetectorRef: ChangeDetectorRef,
261273
elementRef: ElementRef<HTMLElement>,
@@ -275,6 +287,11 @@ export class MatChip
275287
this.tabIndex = parseInt(tabIndex) ?? this.defaultTabIndex;
276288
}
277289
this._monitorFocus();
290+
291+
this._rippleLoader?.configureRipple(this._elementRef.nativeElement, {
292+
className: 'mat-mdc-chip-ripple',
293+
disabled: this._isRippleDisabled(),
294+
});
278295
}
279296

280297
ngOnInit() {
@@ -305,6 +322,10 @@ export class MatChip
305322
).subscribe(() => this._changeDetectorRef.markForCheck());
306323
}
307324

325+
ngDoCheck(): void {
326+
this._rippleLoader.setDisabled(this._elementRef.nativeElement, this._isRippleDisabled());
327+
}
328+
308329
ngOnDestroy() {
309330
this._focusMonitor.stopMonitoring(this._elementRef);
310331
this._actionChanges?.unsubscribe();

src/material/core/private/ripple-loader.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export class MatRippleLoader implements OnDestroy {
7575
config: {
7676
className?: string;
7777
centered?: boolean;
78+
disabled?: boolean;
7879
},
7980
): void {
8081
// Indicates that the ripple has not yet been rendered for this component.
@@ -89,6 +90,10 @@ export class MatRippleLoader implements OnDestroy {
8990
if (config.centered) {
9091
host.setAttribute(matRippleCentered, '');
9192
}
93+
94+
if (config.disabled) {
95+
host.setAttribute(matRippleDisabled, '');
96+
}
9297
}
9398

9499
/** Returns the ripple instance for the given host element. */

tools/public_api_guard/material/chips.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { InjectionToken } from '@angular/core';
3131
import { MatFormField } from '@angular/material/form-field';
3232
import { MatFormFieldControl } from '@angular/material/form-field';
3333
import { MatRipple } from '@angular/material/core';
34+
import { MatRippleLoader } from '@angular/material/core';
3435
import { NgControl } from '@angular/forms';
3536
import { NgForm } from '@angular/forms';
3637
import { NgZone } from '@angular/core';
@@ -61,7 +62,7 @@ export const MAT_CHIP_TRAILING_ICON: InjectionToken<unknown>;
6162
export const MAT_CHIPS_DEFAULT_OPTIONS: InjectionToken<MatChipsDefaultOptions>;
6263

6364
// @public
64-
export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit, AfterContentInit, CanColor, CanDisableRipple, CanDisable, HasTabIndex, OnDestroy {
65+
export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit, AfterContentInit, CanColor, CanDisableRipple, CanDisable, DoCheck, HasTabIndex, OnDestroy {
6566
constructor(_changeDetectorRef: ChangeDetectorRef, elementRef: ElementRef<HTMLElement>, _ngZone: NgZone, _focusMonitor: FocusMonitor, _document: any, animationMode?: string, _globalRippleOptions?: RippleGlobalOptions | undefined, tabIndex?: string);
6667
protected _allLeadingIcons: QueryList<MatChipAvatar>;
6768
protected _allRemoveIcons: QueryList<MatChipRemove>;
@@ -90,14 +91,15 @@ export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit,
9091
protected _highlighted: boolean;
9192
id: string;
9293
_isBasicChip: boolean;
93-
readonly _isRippleCentered = false;
9494
_isRippleDisabled(): boolean;
9595
leadingIcon: MatChipAvatar;
9696
// (undocumented)
9797
ngAfterContentInit(): void;
9898
// (undocumented)
9999
ngAfterViewInit(): void;
100100
// (undocumented)
101+
ngDoCheck(): void;
102+
// (undocumented)
101103
ngOnDestroy(): void;
102104
// (undocumented)
103105
ngOnInit(): void;
@@ -114,7 +116,9 @@ export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit,
114116
readonly removed: EventEmitter<MatChipEvent>;
115117
removeIcon: MatChipRemove;
116118
// @deprecated
117-
ripple: MatRipple;
119+
get ripple(): MatRipple;
120+
set ripple(v: MatRipple);
121+
_rippleLoader: MatRippleLoader;
118122
role: string | null;
119123
trailingIcon: MatChipTrailingIcon;
120124
get value(): any;
@@ -407,6 +411,8 @@ export class MatChipRow extends MatChip implements AfterViewInit {
407411
// (undocumented)
408412
_isEditing: boolean;
409413
// (undocumented)
414+
_isRippleDisabled(): boolean;
415+
// (undocumented)
410416
static ɵcmp: i0.ɵɵComponentDeclaration<MatChipRow, "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", never, { "color": { "alias": "color"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "disableRipple": { "alias": "disableRipple"; "required": false; }; "tabIndex": { "alias": "tabIndex"; "required": false; }; "editable": { "alias": "editable"; "required": false; }; }, { "edited": "edited"; }, ["contentEditInput"], ["mat-chip-avatar, [matChipAvatar]", "*", "[matChipEditInput]", "mat-chip-trailing-icon,[matChipRemove],[matChipTrailingIcon]"], false, never>;
411417
// (undocumented)
412418
static ɵfac: i0.ɵɵFactoryDeclaration<MatChipRow, [null, null, null, null, null, { optional: true; }, { optional: true; }, { attribute: "tabindex"; }]>;

tools/public_api_guard/material/core.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ export class MatRippleLoader implements OnDestroy {
403403
configureRipple(host: HTMLElement, config: {
404404
className?: string;
405405
centered?: boolean;
406+
disabled?: boolean;
406407
}): void;
407408
createRipple(host: HTMLElement): MatRipple | undefined;
408409
getRipple(host: HTMLElement): MatRipple | undefined;

0 commit comments

Comments
 (0)