From a4e5ffedfa891e88539e07a852b0cf3e3f0a8683 Mon Sep 17 00:00:00 2001 From: Wagner Maciel Date: Wed, 12 Jul 2023 14:06:01 -0400 Subject: [PATCH] refactor(material/chips): switch to lazy ripple rendering --- src/material/chips/chip-option.html | 4 --- src/material/chips/chip-row.html | 5 ---- src/material/chips/chip-row.ts | 4 +++ src/material/chips/chip.html | 4 --- src/material/chips/chip.scss | 2 +- src/material/chips/chip.spec.ts | 18 +++++--------- src/material/chips/chip.ts | 29 +++++++++++++++++++--- src/material/core/private/ripple-loader.ts | 5 ++++ tools/public_api_guard/material/chips.md | 12 ++++++--- tools/public_api_guard/material/core.md | 1 + 10 files changed, 51 insertions(+), 33 deletions(-) diff --git a/src/material/chips/chip-option.html b/src/material/chips/chip-option.html index 5970e0328e51..43f81bf23db3 100644 --- a/src/material/chips/chip-option.html +++ b/src/material/chips/chip-option.html @@ -1,7 +1,3 @@ - diff --git a/src/material/chips/chip-row.html b/src/material/chips/chip-row.html index 83c8607c2450..63157ab2919d 100644 --- a/src/material/chips/chip-row.html +++ b/src/material/chips/chip-row.html @@ -1,12 +1,7 @@ - - diff --git a/src/material/chips/chip.scss b/src/material/chips/chip.scss index fa620a8c69dc..44a9d28cdfac 100644 --- a/src/material/chips/chip.scss +++ b/src/material/chips/chip.scss @@ -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 diff --git a/src/material/chips/chip.spec.ts b/src/material/chips/chip.spec.ts index 37b2127708f1..a5f828a20e61 100644 --- a/src/material/chips/chip.spec.ts +++ b/src/material/chips/chip.spec.ts @@ -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'; @@ -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'; @@ -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); - expect(chipRippleInstance.disabled) + chipInstance = chipDebugElement.injector.get(MatChip); + expect(chipInstance.ripple.disabled) .withContext('Expected basic chip ripples to be disabled.') .toBe(true); }); @@ -93,8 +89,6 @@ describe('MDC-based MatChip', () => { chipDebugElement = fixture.debugElement.query(By.directive(MatChip))!; chipNativeElement = chipDebugElement.nativeElement; chipInstance = chipDebugElement.injector.get(MatChip); - chipRippleDebugElement = chipDebugElement.query(By.directive(MatRipple))!; - chipRippleInstance = chipRippleDebugElement.injector.get(MatRipple); testComponent = fixture.debugElement.componentInstance; primaryAction = chipNativeElement.querySelector('.mdc-evolution-chip__action--primary')!; }); @@ -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); }); diff --git a/src/material/chips/chip.ts b/src/material/chips/chip.ts index 98cf5b249441..9e7038e93bf6 100644 --- a/src/material/chips/chip.ts +++ b/src/material/chips/chip.ts @@ -29,6 +29,8 @@ import { ContentChildren, QueryList, OnInit, + DoCheck, + inject, } from '@angular/core'; import {DOCUMENT} from '@angular/common'; import { @@ -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'; @@ -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(); @@ -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, @@ -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() { @@ -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(); diff --git a/src/material/core/private/ripple-loader.ts b/src/material/core/private/ripple-loader.ts index 41fca0c6b432..17f03e218662 100644 --- a/src/material/core/private/ripple-loader.ts +++ b/src/material/core/private/ripple-loader.ts @@ -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. @@ -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. */ diff --git a/tools/public_api_guard/material/chips.md b/tools/public_api_guard/material/chips.md index b1e574bd17ba..532ec1e2be14 100644 --- a/tools/public_api_guard/material/chips.md +++ b/tools/public_api_guard/material/chips.md @@ -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'; @@ -61,7 +62,7 @@ export const MAT_CHIP_TRAILING_ICON: InjectionToken; export const MAT_CHIPS_DEFAULT_OPTIONS: InjectionToken; // @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, _ngZone: NgZone, _focusMonitor: FocusMonitor, _document: any, animationMode?: string, _globalRippleOptions?: RippleGlobalOptions | undefined, tabIndex?: string); protected _allLeadingIcons: QueryList; protected _allRemoveIcons: QueryList; @@ -90,7 +91,6 @@ export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit, protected _highlighted: boolean; id: string; _isBasicChip: boolean; - readonly _isRippleCentered = false; _isRippleDisabled(): boolean; leadingIcon: MatChipAvatar; // (undocumented) @@ -98,6 +98,8 @@ export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit, // (undocumented) ngAfterViewInit(): void; // (undocumented) + ngDoCheck(): void; + // (undocumented) ngOnDestroy(): void; // (undocumented) ngOnInit(): void; @@ -114,7 +116,9 @@ export class MatChip extends _MatChipMixinBase implements OnInit, AfterViewInit, readonly removed: EventEmitter; removeIcon: MatChipRemove; // @deprecated - ripple: MatRipple; + get ripple(): MatRipple; + set ripple(v: MatRipple); + _rippleLoader: MatRippleLoader; role: string | null; trailingIcon: MatChipTrailingIcon; get value(): any; @@ -407,6 +411,8 @@ export class MatChipRow extends MatChip implements AfterViewInit { // (undocumented) _isEditing: boolean; // (undocumented) + _isRippleDisabled(): boolean; + // (undocumented) static ɵcmp: i0.ɵɵComponentDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; diff --git a/tools/public_api_guard/material/core.md b/tools/public_api_guard/material/core.md index ac9be0792f67..4844f95ca2f0 100644 --- a/tools/public_api_guard/material/core.md +++ b/tools/public_api_guard/material/core.md @@ -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;