@@ -15,30 +15,32 @@ import {
1515 OnDestroy ,
1616 inject ,
1717} from '@angular/core' ;
18- import {
19- MAT_RIPPLE_GLOBAL_OPTIONS ,
20- MatRipple ,
21- RippleConfig ,
22- RippleGlobalOptions ,
23- RippleRenderer ,
24- RippleTarget ,
25- } from '@angular/material/core' ;
2618import { Platform } from '@angular/cdk/platform' ;
19+ import { MAT_RIPPLE_GLOBAL_OPTIONS , MatRipple } from '@angular/material/core' ;
2720
2821/** The options for the MatButtonRippleLoader's event listeners. */
2922const eventListenerOptions = { capture : true } ;
3023
3124/** The events that should trigger the initialization of the ripple. */
3225const rippleInteractionEvents = [ 'focus' , 'click' , 'mouseenter' , 'touchstart' ] ;
3326
34- /** The attribute attached to a mat-button whose ripple has not yet been initialized. */
35- export const MAT_BUTTON_RIPPLE_UNINITIALIZED = 'mat-button-ripple-uninitialized' ;
27+ /** The attribute attached to a component whose ripple has not yet been initialized. */
28+ const matRippleUninitialized = 'mat-ripple-loader-uninitialized' ;
29+
30+ /** Additional classes that should be added to the ripple when it is rendered. */
31+ const matRippleClassName = 'mat-ripple-loader-class-name' ;
32+
33+ /** Whether the ripple should be centered. */
34+ const matRippleCentered = 'mat-ripple-loader-centered' ;
35+
36+ /** Whether the ripple should be disabled. */
37+ const matRippleDisabled = 'mat-ripple-loader-disabled' ;
3638
3739/**
38- * Handles attaching the MatButton's ripple on demand.
40+ * Handles attaching ripples on demand.
3941 *
40- * This service allows us to avoid eagerly creating & attaching the MatButton's ripple .
41- * It works by creating & attaching the ripple only when a MatButton is first interacted with.
42+ * This service allows us to avoid eagerly creating & attaching MatRipples .
43+ * It works by creating & attaching a ripple only when a component is first interacted with.
4244 */
4345@Injectable ( { providedIn : 'root' } )
4446export class MatButtonLazyLoader implements OnDestroy {
@@ -62,50 +64,93 @@ export class MatButtonLazyLoader implements OnDestroy {
6264 }
6365 }
6466
65- /** Handles creating and attaching button internals when a button is initially interacted with. */
66- private _onInteraction = ( event : Event ) => {
67- if ( event . target === this . _document ) {
68- return ;
67+ /**
68+ * Configures the ripple that will be rendered by the ripple loader.
69+ *
70+ * Stores the given information about how the ripple should be configured on the host
71+ * element so that it can later be retrived & used when the ripple is actually created.
72+ */
73+ configureRipple (
74+ host : HTMLElement ,
75+ config : {
76+ className ?: string ;
77+ centered ?: boolean ;
78+ disabled ?: boolean ;
79+ } ,
80+ ) : void {
81+ // Indicates that the ripple has not yet been rendered for this component.
82+ host . setAttribute ( matRippleUninitialized , '' ) ;
83+
84+ // Store the additional class name(s) that should be added to the ripple element.
85+ if ( config . className || ! host . hasAttribute ( matRippleClassName ) ) {
86+ host . setAttribute ( matRippleClassName , config . className || '' ) ;
6987 }
70- const eventTarget = event . target as Element ;
7188
72- // TODO(wagnermaciel): Consider batching these events to improve runtime performance.
89+ // Store whether the ripple should be centered.
90+ if ( config . centered ) {
91+ host . setAttribute ( matRippleCentered , '' ) ;
92+ }
7393
74- const button = eventTarget . closest ( `[${ MAT_BUTTON_RIPPLE_UNINITIALIZED } ]` ) ;
75- if ( button ) {
76- button . removeAttribute ( MAT_BUTTON_RIPPLE_UNINITIALIZED ) ;
77- this . _appendRipple ( button as HTMLElement ) ;
94+ if ( config . disabled ) {
95+ host . setAttribute ( matRippleDisabled , '' ) ;
7896 }
79- } ;
97+ }
8098
81- /** Creates a MatButtonRipple and appends it to the given button element. */
82- private _appendRipple ( button : HTMLElement ) : void {
83- if ( ! this . _document ) {
84- return ;
99+ /** Returns the ripple instance for the given host element. */
100+ getRipple ( host : HTMLElement ) : MatRipple | undefined {
101+ if ( ( host as any ) . matRipple ) {
102+ return ( host as any ) . matRipple ;
85103 }
86- const ripple = this . _document . createElement ( 'span' ) ;
87- ripple . classList . add ( 'mat-mdc-button-ripple' ) ;
104+ return this . createRipple ( host ) ;
105+ }
88106
89- const target = new MatButtonRippleTarget (
90- button ,
91- this . _globalRippleOptions ? this . _globalRippleOptions : undefined ,
92- this . _animationMode ? this . _animationMode : undefined ,
93- ) ;
94- target . rippleConfig . centered = button . hasAttribute ( 'mat-icon-button' ) ;
107+ /** Sets the disabled state on the ripple instance corresponding to the given host element. */
108+ setDisabled ( host : HTMLElement , disabled : boolean ) : void {
109+ const ripple = ( host as any ) . matRipple as MatRipple | undefined ;
95110
96- const rippleRenderer = new RippleRenderer ( target , this . _ngZone , ripple , this . _platform ) ;
97- rippleRenderer . setupTriggerEvents ( button ) ;
98- button . append ( ripple ) ;
111+ // If the ripple has already been instantiated, just disable it.
112+ if ( ripple ) {
113+ ripple . disabled = disabled ;
114+ return ;
115+ }
116+
117+ // Otherwise, set an attribute so we know what the
118+ // disabled state should be when the ripple is initialized.
119+ if ( disabled ) {
120+ host . setAttribute ( matRippleDisabled , '' ) ;
121+ } else {
122+ host . removeAttribute ( matRippleDisabled ) ;
123+ }
99124 }
100125
101- _createMatRipple ( button : HTMLElement ) : MatRipple | undefined {
126+ /** Handles creating and attaching component internals when a component it is initially interacted with. */
127+ private _onInteraction = ( event : Event ) => {
128+ if ( ! ( event . target instanceof HTMLElement ) ) {
129+ return ;
130+ }
131+ const eventTarget = event . target as HTMLElement ;
132+
133+ // TODO(wagnermaciel): Consider batching these events to improve runtime performance.
134+
135+ const element = eventTarget . closest ( `[${ matRippleUninitialized } ]` ) ;
136+ if ( element ) {
137+ this . createRipple ( element as HTMLElement ) ;
138+ }
139+ } ;
140+
141+ /** Creates a MatRipple and appends it to the given element. */
142+ createRipple ( host : HTMLElement ) : MatRipple | undefined {
102143 if ( ! this . _document ) {
103144 return ;
104145 }
105- button . querySelector ( '.mat-mdc-button-ripple' ) ?. remove ( ) ;
106- button . removeAttribute ( MAT_BUTTON_RIPPLE_UNINITIALIZED ) ;
146+
147+ // Create the ripple element.
148+ host . querySelector ( '.mat-ripple' ) ?. remove ( ) ;
107149 const rippleEl = this . _document ! . createElement ( 'span' ) ;
108- rippleEl . classList . add ( 'mat-mdc-button-ripple' ) ;
150+ rippleEl . classList . add ( 'mat-ripple' , host . getAttribute ( matRippleClassName ) ! ) ;
151+ host . append ( rippleEl ) ;
152+
153+ // Create the MatRipple.
109154 const ripple = new MatRipple (
110155 new ElementRef ( rippleEl ) ,
111156 this . _ngZone ,
@@ -114,38 +159,15 @@ export class MatButtonLazyLoader implements OnDestroy {
114159 this . _animationMode ? this . _animationMode : undefined ,
115160 ) ;
116161 ripple . _isInitialized = true ;
117- ripple . trigger = button ;
118- button . append ( rippleEl ) ;
162+ ripple . trigger = host ;
163+ ripple . centered = host . hasAttribute ( matRippleCentered ) ;
164+ ripple . disabled = host . hasAttribute ( matRippleDisabled ) ;
165+ this . attachRipple ( host , ripple ) ;
119166 return ripple ;
120167 }
121- }
122-
123- /**
124- * The RippleTarget for the lazily rendered MatButton ripple.
125- * It handles ripple configuration and disabled state for ripples interactions.
126- *
127- * Note that this configuration is usually handled by the MatRipple, but the MatButtonLazyLoader does not use the
128- * MatRipple Directive. In order to create & attach a ripple on demand, it uses the "lower level" RippleRenderer.
129- */
130- class MatButtonRippleTarget implements RippleTarget {
131- rippleConfig : RippleConfig & RippleGlobalOptions ;
132-
133- constructor (
134- private _button : HTMLElement ,
135- private _globalRippleOptions ?: RippleGlobalOptions ,
136- animationMode ?: string ,
137- ) {
138- this . _setRippleConfig ( _globalRippleOptions , animationMode ) ;
139- }
140-
141- private _setRippleConfig ( globalRippleOptions ?: RippleGlobalOptions , animationMode ?: string ) {
142- this . rippleConfig = globalRippleOptions || { } ;
143- if ( animationMode === 'NoopAnimations' ) {
144- this . rippleConfig . animation = { enterDuration : 0 , exitDuration : 0 } ;
145- }
146- }
147168
148- get rippleDisabled ( ) : boolean {
149- return this . _button . hasAttribute ( 'disabled' ) || ! ! this . _globalRippleOptions ?. disabled ;
169+ attachRipple ( host : Element , ripple : MatRipple ) : void {
170+ host . removeAttribute ( matRippleUninitialized ) ;
171+ ( host as any ) . matRipple = ripple ;
150172 }
151173}
0 commit comments