From fb75a136e72489d75c44ca8e68d99352dc71afef Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Fri, 10 Mar 2017 19:37:17 +0100 Subject: [PATCH] feat(ripple): support for global ripple options (#3463) * feat(ripple): add option to change global speedFactor * Refactors the `MD_DISABLE_RIPPLES` token to a configuration object, that can also include options like a global `baseSpeedFactor`. * Fix Angular issue --- src/lib/core/ripple/index.ts | 2 +- src/lib/core/ripple/ripple.spec.ts | 59 +++++++++++++++++++++++++----- src/lib/core/ripple/ripple.ts | 25 ++++++++++--- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/src/lib/core/ripple/index.ts b/src/lib/core/ripple/index.ts index aab611db0302..7058ac5784e8 100644 --- a/src/lib/core/ripple/index.ts +++ b/src/lib/core/ripple/index.ts @@ -4,7 +4,7 @@ import {CompatibilityModule} from '../compatibility/compatibility'; import {VIEWPORT_RULER_PROVIDER} from '../overlay/position/viewport-ruler'; import {SCROLL_DISPATCHER_PROVIDER} from '../overlay/scroll/scroll-dispatcher'; -export {MdRipple, MD_DISABLE_RIPPLES} from './ripple'; +export {MdRipple, RippleGlobalOptions, MD_RIPPLE_GLOBAL_OPTIONS} from './ripple'; export {RippleRef, RippleState} from './ripple-ref'; export {RippleConfig} from './ripple-renderer'; diff --git a/src/lib/core/ripple/ripple.spec.ts b/src/lib/core/ripple/ripple.spec.ts index 330da08fea53..e42dc8bd416b 100644 --- a/src/lib/core/ripple/ripple.spec.ts +++ b/src/lib/core/ripple/ripple.spec.ts @@ -1,9 +1,11 @@ import {TestBed, ComponentFixture, fakeAsync, tick, inject} from '@angular/core/testing'; import {Component, ViewChild} from '@angular/core'; -import {MdRipple, MdRippleModule, MD_DISABLE_RIPPLES, RippleState} from './index'; import {ViewportRuler} from '../overlay/position/viewport-ruler'; import {RIPPLE_FADE_OUT_DURATION, RIPPLE_FADE_IN_DURATION} from './ripple-renderer'; import {dispatchMouseEvent} from '../testing/dispatch-events'; +import { + MdRipple, MdRippleModule, MD_RIPPLE_GLOBAL_OPTIONS, RippleState, RippleGlobalOptions +} from './index'; /** Extracts the numeric value of a pixel size string like '123px'. */ const pxStringToFloat = (s: string) => { @@ -346,29 +348,29 @@ describe('MdRipple', () => { }); - describe('with ripples disabled', () => { + describe('global ripple options', () => { let rippleDirective: MdRipple; - beforeEach(() => { - // Reset the previously configured testing module to be able to disable ripples globally. + function createTestComponent(rippleConfig: RippleGlobalOptions) { + // Reset the previously configured testing module to be able set new providers. // The testing module has been initialized in the root describe group for the ripples. TestBed.resetTestingModule(); TestBed.configureTestingModule({ imports: [MdRippleModule], declarations: [BasicRippleContainer], - providers: [{ provide: MD_DISABLE_RIPPLES, useValue: true }] + providers: [{ provide: MD_RIPPLE_GLOBAL_OPTIONS, useValue: rippleConfig }] }); - }); - beforeEach(() => { fixture = TestBed.createComponent(BasicRippleContainer); fixture.detectChanges(); rippleTarget = fixture.nativeElement.querySelector('[mat-ripple]'); rippleDirective = fixture.componentInstance.ripple; - }); + } + + it('when disabled should not show any ripples on mousedown', () => { + createTestComponent({ disabled: true }); - it('should not show any ripples on mousedown', () => { dispatchMouseEvent(rippleTarget, 'mousedown'); dispatchMouseEvent(rippleTarget, 'mouseup'); @@ -380,7 +382,9 @@ describe('MdRipple', () => { expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); }); - it('should still allow manual ripples', () => { + it('when disabled should still allow manual ripples', () => { + createTestComponent({ disabled: true }); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); rippleDirective.launch(0, 0); @@ -388,6 +392,41 @@ describe('MdRipple', () => { expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); }); + it('should support changing the baseSpeedFactor', fakeAsync(() => { + createTestComponent({ baseSpeedFactor: 0.5 }); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + // Calculates the speedFactor for the duration. Those factors needs to be inverted, because + // a lower speed factor, will make the duration longer. For example: 0.5 => 2x duration. + let fadeInFactor = 1 / 0.5; + + // Calculates the duration for fading-in and fading-out the ripple. + tick(RIPPLE_FADE_IN_DURATION * fadeInFactor + RIPPLE_FADE_OUT_DURATION); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + })); + + it('should combine individual speed factor with baseSpeedFactor', fakeAsync(() => { + createTestComponent({ baseSpeedFactor: 0.5 }); + + rippleDirective.launch(0, 0, { speedFactor: 1.5 }); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + // Calculates the speedFactor for the duration. Those factors needs to be inverted, because + // a lower speed factor, will make the duration longer. For example: 0.5 => 2x duration. + let fadeInFactor = 1 / (0.5 * 1.5); + + // Calculates the duration for fading-in and fading-out the ripple. + tick(RIPPLE_FADE_IN_DURATION * fadeInFactor + RIPPLE_FADE_OUT_DURATION); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + })); + }); describe('configuring behavior', () => { diff --git a/src/lib/core/ripple/ripple.ts b/src/lib/core/ripple/ripple.ts index 96e62d4803f4..8e631006f72a 100644 --- a/src/lib/core/ripple/ripple.ts +++ b/src/lib/core/ripple/ripple.ts @@ -14,8 +14,13 @@ import {RippleConfig, RippleRenderer} from './ripple-renderer'; import {ViewportRuler} from '../overlay/position/viewport-ruler'; import {RippleRef} from './ripple-ref'; -/** OpaqueToken that can be used to globally disable all ripples. Except programmatic ones. */ -export const MD_DISABLE_RIPPLES = new OpaqueToken('md-disable-ripples'); +/** OpaqueToken that can be used to specify the global ripple options. */ +export const MD_RIPPLE_GLOBAL_OPTIONS = new OpaqueToken('md-ripple-global-options'); + +export type RippleGlobalOptions = { + disabled?: boolean; + baseSpeedFactor?: number; +}; @Directive({ selector: '[md-ripple], [mat-ripple], [mdRipple], [matRipple]', @@ -70,10 +75,18 @@ export class MdRipple implements OnChanges, OnDestroy { /** Renderer for the ripple DOM manipulations. */ private _rippleRenderer: RippleRenderer; - constructor(elementRef: ElementRef, ngZone: NgZone, ruler: ViewportRuler, - @Optional() @Inject(MD_DISABLE_RIPPLES) private _forceDisableRipples: boolean) { + /** Options that are set globally for all ripples. */ + private _globalOptions: RippleGlobalOptions; + constructor( + elementRef: ElementRef, + ngZone: NgZone, + ruler: ViewportRuler, + // Type needs to be `any` because of https://github.com/angular/angular/issues/12631 + @Optional() @Inject(MD_RIPPLE_GLOBAL_OPTIONS) globalOptions: any + ) { this._rippleRenderer = new RippleRenderer(elementRef, ngZone, ruler); + this._globalOptions = globalOptions ? globalOptions : {}; } ngOnChanges(changes: SimpleChanges) { @@ -81,7 +94,7 @@ export class MdRipple implements OnChanges, OnDestroy { this._rippleRenderer.setTriggerElement(this.trigger); } - this._rippleRenderer.rippleDisabled = this._forceDisableRipples || this.disabled; + this._rippleRenderer.rippleDisabled = this._globalOptions.disabled || this.disabled; this._rippleRenderer.rippleConfig = this.rippleConfig; } @@ -104,7 +117,7 @@ export class MdRipple implements OnChanges, OnDestroy { get rippleConfig(): RippleConfig { return { centered: this.centered, - speedFactor: this.speedFactor, + speedFactor: this.speedFactor * (this._globalOptions.baseSpeedFactor || 1), radius: this.radius, color: this.color };