From c95f3aa78df9fc279061710b7893c6c9ae8dc5c8 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 22 Aug 2024 08:29:50 +0200 Subject: [PATCH] fix(material/core): avoid having to manually load ripple styles Makes it so the ripple loads the necessary styles itself, instead of requiring the user to do it. --- src/material/core/BUILD.bazel | 8 +++++++ src/material/core/_core.scss | 2 -- src/material/core/private/ripple-loader.ts | 3 +++ src/material/core/ripple/index.ts | 2 +- src/material/core/ripple/ripple-renderer.ts | 22 ++++++++++++++++++- .../core/ripple/ripple-structure.scss | 3 +++ src/material/core/ripple/ripple.ts | 16 +++++++++++++- src/material/list/BUILD.bazel | 1 + src/material/list/list-base.ts | 2 ++ tools/public_api_guard/material/core.md | 7 +++--- 10 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 src/material/core/ripple/ripple-structure.scss diff --git a/src/material/core/BUILD.bazel b/src/material/core/BUILD.bazel index a5b126bb209e..322a2ec9fe24 100644 --- a/src/material/core/BUILD.bazel +++ b/src/material/core/BUILD.bazel @@ -24,6 +24,7 @@ ng_module( ":option/option.css", ":option/optgroup.css", ":internal-form-field/internal-form-field.css", + ":ripple/ripple-structure.css", ] + glob(["**/*.html"]), deps = [ "//src:dev_mode_types", @@ -33,6 +34,7 @@ ng_module( "//src/cdk/coercion", "//src/cdk/keycodes", "//src/cdk/platform", + "//src/cdk/private", "@npm//@angular/animations", "@npm//@angular/core", "@npm//@angular/forms", @@ -94,6 +96,12 @@ sass_binary( deps = [":core_scss_lib"], ) +sass_binary( + name = "ripple_structure_scss", + src = "ripple/ripple-structure.scss", + deps = [":core_scss_lib"], +) + # M3 themes sass_binary( name = "azure_blue_prebuilt", diff --git a/src/material/core/_core.scss b/src/material/core/_core.scss index acd07c39755d..77f732b3bebd 100644 --- a/src/material/core/_core.scss +++ b/src/material/core/_core.scss @@ -1,7 +1,6 @@ @use '@angular/cdk'; @use './tokens/m2/mat/app' as tokens-mat-app; @use './tokens/token-utils'; -@use './ripple/ripple'; @use './style/elevation'; @use './focus-indicators/private'; @@ -15,7 +14,6 @@ --mat-app-on-surface: initial; } - @include ripple.ripple(); @include cdk.a11y-visually-hidden(); @include cdk.overlay(); @include cdk.text-field-autosize(); diff --git a/src/material/core/private/ripple-loader.ts b/src/material/core/private/ripple-loader.ts index 6c2501a27620..ddd05dc7e0aa 100644 --- a/src/material/core/private/ripple-loader.ts +++ b/src/material/core/private/ripple-loader.ts @@ -17,6 +17,7 @@ import { } from '@angular/core'; import {MAT_RIPPLE_GLOBAL_OPTIONS, MatRipple} from '../ripple'; import {Platform, _getEventTarget} from '@angular/cdk/platform'; +import {_CdkPrivateStyleLoader} from '@angular/cdk/private'; /** The options for the MatRippleLoader's event listeners. */ const eventListenerOptions = {capture: true}; @@ -56,6 +57,7 @@ export class MatRippleLoader implements OnDestroy { private _platform = inject(Platform); private _ngZone = inject(NgZone); private _hosts = new Map(); + private _styleLoader = inject(_CdkPrivateStyleLoader); constructor() { this._ngZone.runOutsideAngular(() => { @@ -177,6 +179,7 @@ export class MatRippleLoader implements OnDestroy { this._platform, this._globalRippleOptions ? this._globalRippleOptions : undefined, this._animationMode ? this._animationMode : undefined, + this._styleLoader, ); ripple._isInitialized = true; ripple.trigger = host; diff --git a/src/material/core/ripple/index.ts b/src/material/core/ripple/index.ts index c116f5251acc..704621a4832f 100644 --- a/src/material/core/ripple/index.ts +++ b/src/material/core/ripple/index.ts @@ -12,7 +12,7 @@ import {MatRipple} from './ripple'; export * from './ripple'; export * from './ripple-ref'; -export * from './ripple-renderer'; +export {RippleRenderer, RippleTarget, defaultRippleAnimationConfig} from './ripple-renderer'; @NgModule({ imports: [MatCommonModule, MatRipple], diff --git a/src/material/core/ripple/ripple-renderer.ts b/src/material/core/ripple/ripple-renderer.ts index 89309484a8a7..d7499f1774fe 100644 --- a/src/material/core/ripple/ripple-renderer.ts +++ b/src/material/core/ripple/ripple-renderer.ts @@ -5,10 +5,17 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {ElementRef, NgZone} from '@angular/core'; +import { + ElementRef, + NgZone, + Component, + ChangeDetectionStrategy, + ViewEncapsulation, +} from '@angular/core'; import {Platform, normalizePassiveListenerOptions, _getEventTarget} from '@angular/cdk/platform'; import {isFakeMousedownFromScreenReader, isFakeTouchstartFromScreenReader} from '@angular/cdk/a11y'; import {coerceElement} from '@angular/cdk/coercion'; +import {_CdkPrivateStyleLoader} from '@angular/cdk/private'; import {RippleRef, RippleState, RippleConfig} from './ripple-ref'; import {RippleEventManager} from './ripple-event-manager'; @@ -58,6 +65,16 @@ const pointerDownEvents = ['mousedown', 'touchstart']; /** Events that signal that the pointer is up. */ const pointerUpEvents = ['mouseup', 'mouseleave', 'touchend', 'touchcancel']; +@Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + standalone: true, + styleUrl: 'ripple-structure.css', + host: {'mat-ripple-style-loader': ''}, +}) +export class _MatRippleStylesLoader {} + /** * Helper service that performs DOM manipulations. Not intended to be used outside this module. * The constructor takes a reference to the ripple directive's host element and a map of DOM @@ -105,11 +122,14 @@ export class RippleRenderer implements EventListenerObject { private _ngZone: NgZone, elementOrElementRef: HTMLElement | ElementRef, private _platform: Platform, + styleLoader: _CdkPrivateStyleLoader, ) { // Only do anything if we're on the browser. if (_platform.isBrowser) { this._containerElement = coerceElement(elementOrElementRef); } + + styleLoader.load(_MatRippleStylesLoader); } /** diff --git a/src/material/core/ripple/ripple-structure.scss b/src/material/core/ripple/ripple-structure.scss new file mode 100644 index 000000000000..1ef209c2644c --- /dev/null +++ b/src/material/core/ripple/ripple-structure.scss @@ -0,0 +1,3 @@ +@use './ripple'; + +@include ripple.ripple; diff --git a/src/material/core/ripple/ripple.ts b/src/material/core/ripple/ripple.ts index 72ca76189368..c284f079d80b 100644 --- a/src/material/core/ripple/ripple.ts +++ b/src/material/core/ripple/ripple.ts @@ -18,7 +18,9 @@ import { OnInit, Optional, ANIMATION_MODULE_TYPE, + inject, } from '@angular/core'; +import {_CdkPrivateStyleLoader} from '@angular/cdk/private'; import {RippleAnimationConfig, RippleConfig, RippleRef} from './ripple-ref'; import {RippleRenderer, RippleTarget} from './ripple-renderer'; @@ -136,9 +138,21 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget { platform: Platform, @Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalOptions?: RippleGlobalOptions, @Optional() @Inject(ANIMATION_MODULE_TYPE) private _animationMode?: string, + private _styleLoader?: _CdkPrivateStyleLoader, ) { this._globalOptions = globalOptions || {}; - this._rippleRenderer = new RippleRenderer(this, ngZone, _elementRef, platform); + + if (!this._styleLoader) { + this._styleLoader = inject(_CdkPrivateStyleLoader); + } + + this._rippleRenderer = new RippleRenderer( + this, + ngZone, + _elementRef, + platform, + this._styleLoader, + ); } ngOnInit() { diff --git a/src/material/list/BUILD.bazel b/src/material/list/BUILD.bazel index 854552d0f760..834bcde15678 100644 --- a/src/material/list/BUILD.bazel +++ b/src/material/list/BUILD.bazel @@ -27,6 +27,7 @@ ng_module( "//src/cdk/coercion", "//src/cdk/collections", "//src/cdk/observers", + "//src/cdk/private", "//src/material/core", "//src/material/divider", "@npm//@angular/core", diff --git a/src/material/list/list-base.ts b/src/material/list/list-base.ts index 85d316290464..fb8ee54dbb11 100644 --- a/src/material/list/list-base.ts +++ b/src/material/list/list-base.ts @@ -29,6 +29,7 @@ import { RippleRenderer, RippleTarget, } from '@angular/material/core'; +import {_CdkPrivateStyleLoader} from '@angular/cdk/private'; import {Subscription, merge} from 'rxjs'; import { MatListItemLine, @@ -223,6 +224,7 @@ export abstract class MatListItemBase implements AfterViewInit, OnDestroy, Rippl this._ngZone, this._hostElement, this._platform, + inject(_CdkPrivateStyleLoader), ); this._rippleRenderer.setupTriggerEvents(this._hostElement); } diff --git a/tools/public_api_guard/material/core.md b/tools/public_api_guard/material/core.md index 21b46b72c8ed..753ee6142af1 100644 --- a/tools/public_api_guard/material/core.md +++ b/tools/public_api_guard/material/core.md @@ -6,6 +6,7 @@ import { AbstractControl } from '@angular/forms'; import { AfterViewChecked } from '@angular/core'; +import { _CdkPrivateStyleLoader } from '@angular/cdk/private'; import { ChangeDetectorRef } from '@angular/core'; import { ElementRef } from '@angular/core'; import { EventEmitter } from '@angular/core'; @@ -372,7 +373,7 @@ export type MatPseudoCheckboxState = 'unchecked' | 'checked' | 'indeterminate'; // @public (undocumented) export class MatRipple implements OnInit, OnDestroy, RippleTarget { - constructor(_elementRef: ElementRef, ngZone: NgZone, platform: Platform, globalOptions?: RippleGlobalOptions, _animationMode?: string | undefined); + constructor(_elementRef: ElementRef, ngZone: NgZone, platform: Platform, globalOptions?: RippleGlobalOptions, _animationMode?: string | undefined, _styleLoader?: _CdkPrivateStyleLoader | undefined); animation: RippleAnimationConfig; centered: boolean; color: string; @@ -396,7 +397,7 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget { // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) - static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵfac: i0.ɵɵFactoryDeclaration; } // @public @@ -560,7 +561,7 @@ export class RippleRef { // @public export class RippleRenderer implements EventListenerObject { - constructor(_target: RippleTarget, _ngZone: NgZone, elementOrElementRef: HTMLElement | ElementRef, _platform: Platform); + constructor(_target: RippleTarget, _ngZone: NgZone, elementOrElementRef: HTMLElement | ElementRef, _platform: Platform, styleLoader: _CdkPrivateStyleLoader); fadeInRipple(x: number, y: number, config?: RippleConfig): RippleRef; fadeOutAll(): void; fadeOutAllNonPersistent(): void;