From 855ed49482b1e215f43e1e9b96f1b28eded94640 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. BREAKING CHANGE: * The ripples styles are now loaded slightly later than before which can change their specificity. You may have to update any ripple style overrides. --- src/material/core/BUILD.bazel | 8 ++++++ src/material/core/_core.scss | 2 -- src/material/core/private/ripple-loader.ts | 1 + src/material/core/ripple/index.ts | 2 +- src/material/core/ripple/ripple-renderer.ts | 25 ++++++++++++++++++- .../core/ripple/ripple-structure.scss | 3 +++ src/material/core/ripple/ripple.ts | 7 +++++- src/material/list/BUILD.bazel | 1 + src/material/list/list-base.ts | 3 +++ tools/public_api_guard/material/core.md | 7 +++--- 10 files changed, 51 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 63aec3a26897..7cf0003cd7a4 100644 --- a/src/material/core/private/ripple-loader.ts +++ b/src/material/core/private/ripple-loader.ts @@ -15,6 +15,7 @@ import { defaultRippleAnimationConfig, } 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}; 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..0b13d4bca3e3 100644 --- a/src/material/core/ripple/ripple-renderer.ts +++ b/src/material/core/ripple/ripple-renderer.ts @@ -5,10 +5,18 @@ * 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, + Injector, +} 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 +66,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 +123,16 @@ export class RippleRenderer implements EventListenerObject { private _ngZone: NgZone, elementOrElementRef: HTMLElement | ElementRef, private _platform: Platform, + injector?: Injector, ) { // Only do anything if we're on the browser. if (_platform.isBrowser) { this._containerElement = coerceElement(elementOrElementRef); } + + if (injector) { + injector.get(_CdkPrivateStyleLoader).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..dce9aa124a86 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, + Injector, } 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,12 @@ 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, + injector?: Injector, ) { + // Note: cannot use `inject()` here, because this class + // gets instantiated manually in the ripple loader. this._globalOptions = globalOptions || {}; - this._rippleRenderer = new RippleRenderer(this, ngZone, _elementRef, platform); + this._rippleRenderer = new RippleRenderer(this, ngZone, _elementRef, platform, injector); } 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..2f42bbfb05e9 100644 --- a/src/material/list/list-base.ts +++ b/src/material/list/list-base.ts @@ -21,6 +21,7 @@ import { Optional, QueryList, ANIMATION_MODULE_TYPE, + Injector, } from '@angular/core'; import { MAT_RIPPLE_GLOBAL_OPTIONS, @@ -29,6 +30,7 @@ import { RippleRenderer, RippleTarget, } from '@angular/material/core'; +import {_CdkPrivateStyleLoader} from '@angular/cdk/private'; import {Subscription, merge} from 'rxjs'; import { MatListItemLine, @@ -223,6 +225,7 @@ export abstract class MatListItemBase implements AfterViewInit, OnDestroy, Rippl this._ngZone, this._hostElement, this._platform, + inject(Injector), ); this._rippleRenderer.setupTriggerEvents(this._hostElement); } diff --git a/tools/public_api_guard/material/core.md b/tools/public_api_guard/material/core.md index aa5296e9fd1b..b3e4be1c0c30 100644 --- a/tools/public_api_guard/material/core.md +++ b/tools/public_api_guard/material/core.md @@ -16,6 +16,7 @@ import { HighContrastModeDetector } from '@angular/cdk/a11y'; import * as i0 from '@angular/core'; import * as i1 from '@angular/cdk/bidi'; import { InjectionToken } from '@angular/core'; +import { Injector } from '@angular/core'; import { NgControl } from '@angular/forms'; import { NgForm } from '@angular/forms'; import { NgZone } 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, injector?: Injector); 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 @@ -557,7 +558,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, injector?: Injector); fadeInRipple(x: number, y: number, config?: RippleConfig): RippleRef; fadeOutAll(): void; fadeOutAllNonPersistent(): void;