Skip to content

refactor(material/chips): switch to lazy ripple rendering #27440

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/material/chips/chip-option.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
<span matRipple class="mat-mdc-chip-ripple"
[matRippleDisabled]="_isRippleDisabled()"
[matRippleCentered]="_isRippleCentered"
[matRippleTrigger]="_elementRef.nativeElement"></span>
<span class="mat-mdc-chip-focus-overlay"></span>

<span class="mdc-evolution-chip__cell mdc-evolution-chip__cell--primary">
Expand Down
5 changes: 0 additions & 5 deletions src/material/chips/chip-row.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
<ng-container *ngIf="!_isEditing">
<span matRipple class="mat-mdc-chip-ripple"
[matRippleDisabled]="_isRippleDisabled()"
[matRippleCentered]="_isRippleCentered"
[matRippleTrigger]="_elementRef.nativeElement"></span>
<span class="mat-mdc-chip-focus-overlay"></span>
</ng-container>


<span class="mdc-evolution-chip__cell mdc-evolution-chip__cell--primary" role="gridcell"
matChipAction
[tabIndex]="tabIndex"
Expand Down
4 changes: 4 additions & 0 deletions src/material/chips/chip-row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ export class MatChipRow extends MatChip implements AfterViewInit {
}
}

override _isRippleDisabled(): boolean {
return super._isRippleDisabled() || this._isEditing;
}

/**
* Gets the projected chip edit input, or the default input if none is projected in. One of these
* two values is guaranteed to be defined.
Expand Down
4 changes: 0 additions & 4 deletions src/material/chips/chip.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
<span matRipple class="mat-mdc-chip-ripple"
[matRippleDisabled]="_isRippleDisabled()"
[matRippleCentered]="_isRippleCentered"
[matRippleTrigger]="_elementRef.nativeElement"></span>
<span class="mat-mdc-chip-focus-overlay"></span>

<span class="mdc-evolution-chip__cell mdc-evolution-chip__cell--primary">
Expand Down
2 changes: 1 addition & 1 deletion src/material/chips/chip.scss
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@
}

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

// Disable pointer events for the ripple container and state overlay because the container
Expand Down
18 changes: 6 additions & 12 deletions src/material/chips/chip.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {Directionality} from '@angular/cdk/bidi';
import {Component, DebugElement, ViewChild} from '@angular/core';
import {waitForAsync, ComponentFixture, TestBed} from '@angular/core/testing';
import {MatRipple} from '@angular/material/core';
import {By} from '@angular/platform-browser';
import {Subject} from 'rxjs';
import {MatChip, MatChipEvent, MatChipSet, MatChipsModule} from './index';
Expand All @@ -11,8 +10,6 @@ describe('MDC-based MatChip', () => {
let chipDebugElement: DebugElement;
let chipNativeElement: HTMLElement;
let chipInstance: MatChip;
let chipRippleDebugElement: DebugElement;
let chipRippleInstance: MatRipple;

let dir = 'ltr';

Expand Down Expand Up @@ -74,9 +71,8 @@ describe('MDC-based MatChip', () => {
fixture = TestBed.createComponent(BasicChip);
fixture.detectChanges();
chipDebugElement = fixture.debugElement.query(By.directive(MatChip))!;
chipRippleDebugElement = chipDebugElement.query(By.directive(MatRipple))!;
chipRippleInstance = chipRippleDebugElement.injector.get<MatRipple>(MatRipple);
expect(chipRippleInstance.disabled)
chipInstance = chipDebugElement.injector.get<MatChip>(MatChip);
expect(chipInstance.ripple.disabled)
.withContext('Expected basic chip ripples to be disabled.')
.toBe(true);
});
Expand All @@ -93,8 +89,6 @@ describe('MDC-based MatChip', () => {
chipDebugElement = fixture.debugElement.query(By.directive(MatChip))!;
chipNativeElement = chipDebugElement.nativeElement;
chipInstance = chipDebugElement.injector.get<MatChip>(MatChip);
chipRippleDebugElement = chipDebugElement.query(By.directive(MatRipple))!;
chipRippleInstance = chipRippleDebugElement.injector.get<MatRipple>(MatRipple);
testComponent = fixture.debugElement.componentInstance;
primaryAction = chipNativeElement.querySelector('.mdc-evolution-chip__action--primary')!;
});
Expand Down Expand Up @@ -137,27 +131,27 @@ describe('MDC-based MatChip', () => {
});

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

testComponent.rippleDisabled = true;
fixture.detectChanges();

expect(chipRippleInstance.disabled)
expect(chipInstance.ripple.disabled)
.withContext('Expected chip ripples to be disabled.')
.toBe(true);
});

it('should disable ripples when the chip is disabled', () => {
expect(chipRippleInstance.disabled)
expect(chipInstance.ripple.disabled)
.withContext('Expected chip ripples to be enabled.')
.toBe(false);

testComponent.disabled = true;
fixture.detectChanges();

expect(chipRippleInstance.disabled)
expect(chipInstance.ripple.disabled)
.withContext('Expected chip ripples to be disabled.')
.toBe(true);
});
Expand Down
29 changes: 25 additions & 4 deletions src/material/chips/chip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
ContentChildren,
QueryList,
OnInit,
DoCheck,
inject,
} from '@angular/core';
import {DOCUMENT} from '@angular/common';
import {
Expand All @@ -43,6 +45,7 @@ import {
mixinTabIndex,
mixinDisabled,
RippleGlobalOptions,
MatRippleLoader,
} from '@angular/material/core';
import {FocusMonitor} from '@angular/cdk/a11y';
import {merge, Subject, Subscription} from 'rxjs';
Expand Down Expand Up @@ -123,14 +126,12 @@ export class MatChip
CanColor,
CanDisableRipple,
CanDisable,
DoCheck,
HasTabIndex,
OnDestroy
{
protected _document: Document;

/** Whether the ripple is centered on the chip. */
readonly _isRippleCentered = false;

/** Emits when the chip is focused. */
readonly _onFocus = new Subject<MatChipEvent>();

Expand Down Expand Up @@ -251,11 +252,22 @@ export class MatChip
* @deprecated Considered an implementation detail. To be removed.
* @breaking-change 17.0.0
*/
@ViewChild(MatRipple) ripple: MatRipple;
get ripple(): MatRipple {
return this._rippleLoader?.getRipple(this._elementRef.nativeElement)!;
}
set ripple(v: MatRipple) {
this._rippleLoader?.attachRipple(this._elementRef.nativeElement, v);
}

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

/**
* Handles the lazy creation of the MatChip ripple.
* Used to improve initial load time of large applications.
*/
_rippleLoader: MatRippleLoader = inject(MatRippleLoader);

constructor(
public _changeDetectorRef: ChangeDetectorRef,
elementRef: ElementRef<HTMLElement>,
Expand All @@ -275,6 +287,11 @@ export class MatChip
this.tabIndex = parseInt(tabIndex) ?? this.defaultTabIndex;
}
this._monitorFocus();

this._rippleLoader?.configureRipple(this._elementRef.nativeElement, {
className: 'mat-mdc-chip-ripple',
disabled: this._isRippleDisabled(),
});
}

ngOnInit() {
Expand Down Expand Up @@ -305,6 +322,10 @@ export class MatChip
).subscribe(() => this._changeDetectorRef.markForCheck());
}

ngDoCheck(): void {
this._rippleLoader.setDisabled(this._elementRef.nativeElement, this._isRippleDisabled());
}

ngOnDestroy() {
this._focusMonitor.stopMonitoring(this._elementRef);
this._actionChanges?.unsubscribe();
Expand Down
5 changes: 5 additions & 0 deletions src/material/core/private/ripple-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class MatRippleLoader implements OnDestroy {
config: {
className?: string;
centered?: boolean;
disabled?: boolean;
},
): void {
// Indicates that the ripple has not yet been rendered for this component.
Expand All @@ -89,6 +90,10 @@ export class MatRippleLoader implements OnDestroy {
if (config.centered) {
host.setAttribute(matRippleCentered, '');
}

if (config.disabled) {
host.setAttribute(matRippleDisabled, '');
}
}

/** Returns the ripple instance for the given host element. */
Expand Down
12 changes: 9 additions & 3 deletions tools/public_api_guard/material/chips.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { InjectionToken } from '@angular/core';
import { MatFormField } from '@angular/material/form-field';
import { MatFormFieldControl } from '@angular/material/form-field';
import { MatRipple } from '@angular/material/core';
import { MatRippleLoader } from '@angular/material/core';
import { NgControl } from '@angular/forms';
import { NgForm } from '@angular/forms';
import { NgZone } from '@angular/core';
Expand Down Expand Up @@ -61,7 +62,7 @@ export const MAT_CHIP_TRAILING_ICON: InjectionToken<unknown>;
export const MAT_CHIPS_DEFAULT_OPTIONS: InjectionToken<MatChipsDefaultOptions>;

// @public
export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit, AfterContentInit, CanColor, CanDisableRipple, CanDisable, HasTabIndex, OnDestroy {
export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit, AfterContentInit, CanColor, CanDisableRipple, CanDisable, DoCheck, HasTabIndex, OnDestroy {
constructor(_changeDetectorRef: ChangeDetectorRef, elementRef: ElementRef<HTMLElement>, _ngZone: NgZone, _focusMonitor: FocusMonitor, _document: any, animationMode?: string, _globalRippleOptions?: RippleGlobalOptions | undefined, tabIndex?: string);
protected _allLeadingIcons: QueryList<MatChipAvatar>;
protected _allRemoveIcons: QueryList<MatChipRemove>;
Expand Down Expand Up @@ -90,14 +91,15 @@ export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit,
protected _highlighted: boolean;
id: string;
_isBasicChip: boolean;
readonly _isRippleCentered = false;
_isRippleDisabled(): boolean;
leadingIcon: MatChipAvatar;
// (undocumented)
ngAfterContentInit(): void;
// (undocumented)
ngAfterViewInit(): void;
// (undocumented)
ngDoCheck(): void;
// (undocumented)
ngOnDestroy(): void;
// (undocumented)
ngOnInit(): void;
Expand All @@ -114,7 +116,9 @@ export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit,
readonly removed: EventEmitter<MatChipEvent>;
removeIcon: MatChipRemove;
// @deprecated
ripple: MatRipple;
get ripple(): MatRipple;
set ripple(v: MatRipple);
_rippleLoader: MatRippleLoader;
role: string | null;
trailingIcon: MatChipTrailingIcon;
get value(): any;
Expand Down Expand Up @@ -407,6 +411,8 @@ export class MatChipRow extends MatChip implements AfterViewInit {
// (undocumented)
_isEditing: boolean;
// (undocumented)
_isRippleDisabled(): boolean;
// (undocumented)
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>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<MatChipRow, [null, null, null, null, null, { optional: true; }, { optional: true; }, { attribute: "tabindex"; }]>;
Expand Down
1 change: 1 addition & 0 deletions tools/public_api_guard/material/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ export class MatRippleLoader implements OnDestroy {
configureRipple(host: HTMLElement, config: {
className?: string;
centered?: boolean;
disabled?: boolean;
}): void;
createRipple(host: HTMLElement): MatRipple | undefined;
getRipple(host: HTMLElement): MatRipple | undefined;
Expand Down