feat(ripple): support animation duration overwrites (#9253)

* Adds a new option to the ripples that allows developers to have a better control of the animation (all ripples, or even individual ripples).
* Deprecates the `matRippleSpeedFactor` in favor of the `matRippleAnimation` binding that accepts a `RippleAnimationConfig`. The configuration is more explicit, clean and not confusing as the `speedFactor`.
* To provide a more user-friendly `launch()` method API, the passed ripple config will extend the default ripple config from the `MatRipple` instance (removes unnecessary bloat; requested in https://github.com/angular/material2/issues/4179#issuecomment-295889498)
* Disables ripples for most of the demo buttons in the ripple demo (allows better debugging; when pressing the buttons)
This commit is contained in:
Paul Gschwendtner
2018-01-24 17:00:52 +01:00
committed by Jeremy Elbourn
parent d5a7ccea25
commit 3bc4cd308d
16 changed files with 184 additions and 84 deletions
+4 -3
View File
@@ -35,9 +35,10 @@
</mat-form-field>
</section>
<section>
<button mat-raised-button (click)="launchRipple()">Launch Ripple</button>
<button mat-raised-button (click)="launchRipple(true)">Launch Ripple (Persistent)</button>
<button mat-raised-button (click)="fadeOutAll()">Fade Out All</button>
<button mat-raised-button (click)="launchRipple()" disableRipple>Launch Ripple</button>
<button mat-raised-button (click)="launchRipple(true)" disableRipple>Launch Ripple (Persistent)</button>
<button mat-raised-button (click)="launchRipple(true, true)" disableRipple>Launch Ripple (No Animation)</button>
<button mat-raised-button (click)="fadeOutAll()" disableRipple>Fade Out All</button>
</section>
<section>
<div class="demo-ripple-container"
+11 -3
View File
@@ -29,10 +29,18 @@ export class RippleDemo {
disableButtonRipples = false;
launchRipple(persistent = false) {
if (this.ripple) {
this.ripple.launch(0, 0, { centered: true, persistent });
launchRipple(persistent = false, disableAnimation = false) {
if (!this.ripple) {
return;
}
const rippleConfig = {
centered: true,
persistent: persistent,
animation: disableAnimation ? {enterDuration: 0, exitDuration: 0} : undefined
};
this.ripple.launch(0, 0, rippleConfig);
}
fadeOutAll() {
+3 -3
View File
@@ -19,9 +19,9 @@
<div matRipple class="mat-checkbox-ripple"
[matRippleTrigger]="label"
[matRippleDisabled]="_isRippleDisabled()"
[matRippleRadius]="_rippleConfig.radius"
[matRippleSpeedFactor]="_rippleConfig.speedFactor"
[matRippleCentered]="_rippleConfig.centered">
[matRippleRadius]="25"
[matRippleCentered]="true"
[matRippleAnimation]="{enterDuration: 300}">
</div>
<div class="mat-checkbox-frame"></div>
<div class="mat-checkbox-background">
+3 -3
View File
@@ -4,7 +4,7 @@ import {Component, DebugElement} from '@angular/core';
import {By} from '@angular/platform-browser';
import {dispatchFakeEvent} from '@angular/cdk/testing';
import {MatCheckbox, MatCheckboxChange, MatCheckboxModule} from './index';
import {RIPPLE_FADE_IN_DURATION, RIPPLE_FADE_OUT_DURATION} from '@angular/material/core';
import {defaultRippleAnimationConfig} from '@angular/material/core';
import {MAT_CHECKBOX_CLICK_ACTION} from './checkbox-config';
import {MutationObserverFactory} from '@angular/cdk/observers';
@@ -390,13 +390,13 @@ describe('MatCheckbox', () => {
dispatchFakeEvent(inputElement, 'keydown');
dispatchFakeEvent(inputElement, 'focus');
tick(RIPPLE_FADE_IN_DURATION);
tick(defaultRippleAnimationConfig.enterDuration);
expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length)
.toBe(1, 'Expected ripple after element is focused.');
dispatchFakeEvent(checkboxInstance._inputElement.nativeElement, 'blur');
tick(RIPPLE_FADE_OUT_DURATION);
tick(defaultRippleAnimationConfig.exitDuration);
expect(fixture.nativeElement.querySelectorAll('.mat-ripple-element').length)
.toBe(0, 'Expected no ripple after element is blurred.');
+1 -5
View File
@@ -36,7 +36,6 @@ import {
mixinDisabled,
mixinDisableRipple,
mixinTabIndex,
RippleConfig,
RippleRef,
} from '@angular/material/core';
import {MAT_CHECKBOX_CLICK_ACTION, MatCheckboxClickAction} from './checkbox-config';
@@ -180,9 +179,6 @@ export class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAc
/** Reference to the ripple instance of the checkbox. */
@ViewChild(MatRipple) ripple: MatRipple;
/** Ripple configuration for the mouse ripples and focus indicators. */
_rippleConfig: RippleConfig = {centered: true, radius: 25, speedFactor: 1.5};
/**
* Called when the checkbox is blurred. Needed to properly implement ControlValueAccessor.
* @docs-private
@@ -341,7 +337,7 @@ export class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAc
/** Function is called whenever the focus changes for the input element. */
private _onInputFocusChange(focusOrigin: FocusOrigin) {
if (!this._focusRipple && focusOrigin === 'keyboard') {
this._focusRipple = this.ripple.launch(0, 0, {persistent: true, ...this._rippleConfig});
this._focusRipple = this.ripple.launch(0, 0, {persistent: true});
} else if (!focusOrigin) {
this._removeFocusRipple();
this.onTouched();
+35 -18
View File
@@ -9,26 +9,27 @@ import {ElementRef, NgZone} from '@angular/core';
import {Platform, supportsPassiveEventListeners} from '@angular/cdk/platform';
import {RippleRef, RippleState} from './ripple-ref';
/** Fade-in duration for the ripples. Can be modified with the speedFactor option. */
export const RIPPLE_FADE_IN_DURATION = 450;
/** Fade-out duration for the ripples in milliseconds. This can't be modified by the speedFactor. */
export const RIPPLE_FADE_OUT_DURATION = 400;
/**
* Timeout for ignoring mouse events. Mouse events will be temporary ignored after touch
* events to avoid synthetic mouse events.
*/
const IGNORE_MOUSE_EVENTS_TIMEOUT = 800;
export type RippleConfig = {
color?: string;
centered?: boolean;
radius?: number;
speedFactor?: number;
persistent?: boolean;
animation?: RippleAnimationConfig;
/** @deprecated Use the animation property instead. */
speedFactor?: number;
};
/**
* Interface that describes the configuration for the animation of a ripple.
* There are two animation phases with different durations for the ripples.
*/
export interface RippleAnimationConfig {
/** Duration in milliseconds for the enter animation (expansion from point of contact). */
enterDuration?: number;
/** Duration in milliseconds for the exit animation (fade-out). */
exitDuration?: number;
}
/**
* Interface that describes the target for launching ripples.
* It defines the ripple configuration and disabled state for interaction ripples.
@@ -37,11 +38,25 @@ export type RippleConfig = {
export interface RippleTarget {
/** Configuration for ripples that are launched on pointer down. */
rippleConfig: RippleConfig;
/** Whether ripples on pointer down should be disabled. */
rippleDisabled: boolean;
}
/**
* Default ripple animation configuration for ripples without an explicit
* animation config specified.
*/
export const defaultRippleAnimationConfig = {
enterDuration: 450,
exitDuration: 400
};
/**
* Timeout for ignoring mouse events. Mouse events will be temporary ignored after touch
* events to avoid synthetic mouse events.
*/
const ignoreMouseEventsTimeout = 800;
/**
* 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
@@ -99,6 +114,7 @@ export class RippleRenderer {
*/
fadeInRipple(x: number, y: number, config: RippleConfig = {}): RippleRef {
const containerRect = this._containerElement.getBoundingClientRect();
const animationConfig = {...defaultRippleAnimationConfig, ...config.animation};
if (config.centered) {
x = containerRect.left + containerRect.width / 2;
@@ -106,9 +122,9 @@ export class RippleRenderer {
}
const radius = config.radius || distanceToFurthestCorner(x, y, containerRect);
const duration = RIPPLE_FADE_IN_DURATION / (config.speedFactor || 1);
const offsetX = x - containerRect.left;
const offsetY = y - containerRect.top;
const duration = animationConfig.enterDuration / (config.speedFactor || 1);
const ripple = document.createElement('div');
ripple.classList.add('mat-ripple-element');
@@ -159,8 +175,9 @@ export class RippleRenderer {
}
const rippleEl = rippleRef.element;
const animationConfig = {...defaultRippleAnimationConfig, ...rippleRef.config.animation};
rippleEl.style.transitionDuration = `${RIPPLE_FADE_OUT_DURATION}ms`;
rippleEl.style.transitionDuration = `${animationConfig.exitDuration}ms`;
rippleEl.style.opacity = '0';
rippleRef.state = RippleState.FADING_OUT;
@@ -169,7 +186,7 @@ export class RippleRenderer {
this.runTimeoutOutsideZone(() => {
rippleRef.state = RippleState.HIDDEN;
rippleEl.parentNode!.removeChild(rippleEl);
}, RIPPLE_FADE_OUT_DURATION);
}, animationConfig.exitDuration);
}
/** Fades out all currently active ripples. */
@@ -197,7 +214,7 @@ export class RippleRenderer {
/** Function being called whenever the trigger is being pressed using mouse. */
private onMousedown = (event: MouseEvent) => {
const isSyntheticEvent = this._lastTouchStartEvent &&
Date.now() < this._lastTouchStartEvent + IGNORE_MOUSE_EVENTS_TIMEOUT;
Date.now() < this._lastTouchStartEvent + ignoreMouseEventsTimeout;
if (!this._target.rippleDisabled && !isSyntheticEvent) {
this._isPointerDown = true;
+25 -2
View File
@@ -75,8 +75,11 @@ Global ripple options can be specified by setting the `MAT_RIPPLE_GLOBAL_OPTIONS
```ts
const globalRippleConfig: RippleGlobalOptions = {
disabled: true,
baseSpeedFactor: 1.5 // Ripples will animate 50% faster than before.
}
animation: {
enterDuration: 300,
exitDuration: 0
}
};
@NgModule({
providers: [
@@ -86,3 +89,23 @@ const globalRippleConfig: RippleGlobalOptions = {
```
All available global options can be seen in the `RippleGlobalOptions` interface.
### Disabling animation
The animation of ripples can be disabled by using the `animation` global option. If the
`enterDuration` and `exitDuration` is being set to `0`, ripples will just appear without any
animation.
This is specifically useful in combination with the `disabled` global option, because globally
disabling ripples won't affect the focus indicator ripples. If someone still wants to disable
those ripples for performance reasons, the duration can be set to `0`, to remove the ripple feel.
```ts
const globalRippleConfig: RippleGlobalOptions = {
disabled: true,
animation: {
enterDuration: 0,
exitDuration: 0
}
};
```
+63 -18
View File
@@ -2,11 +2,13 @@ import {TestBed, ComponentFixture, fakeAsync, tick, inject} from '@angular/core/
import {Component, ViewChild} from '@angular/core';
import {Platform} from '@angular/cdk/platform';
import {dispatchMouseEvent, dispatchTouchEvent} from '@angular/cdk/testing';
import {RIPPLE_FADE_OUT_DURATION, RIPPLE_FADE_IN_DURATION} from './ripple-renderer';
import {defaultRippleAnimationConfig, RippleAnimationConfig} from './ripple-renderer';
import {
MatRipple, MatRippleModule, MAT_RIPPLE_GLOBAL_OPTIONS, RippleState, RippleGlobalOptions
} from './index';
/** Shorthands for the enter and exit duration of ripples. */
const {enterDuration, exitDuration} = defaultRippleAnimationConfig;
describe('MatRipple', () => {
let fixture: ComponentFixture<any>;
@@ -108,12 +110,12 @@ describe('MatRipple', () => {
dispatchTouchEvent(rippleTarget, 'touchstart');
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
tick(RIPPLE_FADE_IN_DURATION);
tick(enterDuration);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
dispatchTouchEvent(rippleTarget, 'touchend');
tick(RIPPLE_FADE_OUT_DURATION);
tick(exitDuration);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));
@@ -122,12 +124,12 @@ describe('MatRipple', () => {
dispatchTouchEvent(rippleTarget, 'touchstart');
dispatchTouchEvent(rippleTarget, 'mousedown');
tick(RIPPLE_FADE_IN_DURATION);
tick(enterDuration);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
dispatchTouchEvent(rippleTarget, 'touchend');
tick(RIPPLE_FADE_OUT_DURATION);
tick(exitDuration);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));
@@ -139,7 +141,7 @@ describe('MatRipple', () => {
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
// Calculates the duration for fading-in and fading-out the ripple.
tick(RIPPLE_FADE_IN_DURATION + RIPPLE_FADE_OUT_DURATION);
tick(enterDuration + exitDuration);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));
@@ -151,19 +153,19 @@ describe('MatRipple', () => {
// Fakes the duration of fading-in and fading-out normal ripples.
// The fade-out duration has been added to ensure that didn't start fading out.
tick(RIPPLE_FADE_IN_DURATION + RIPPLE_FADE_OUT_DURATION);
tick(enterDuration + exitDuration);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
dispatchMouseEvent(rippleTarget, 'mouseup');
tick(RIPPLE_FADE_OUT_DURATION);
tick(exitDuration);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));
it('should not hide ripples while animating.', fakeAsync(() => {
// Calculates the duration for fading-in and fading-out the ripple.
let hideDuration = RIPPLE_FADE_IN_DURATION + RIPPLE_FADE_OUT_DURATION;
let hideDuration = enterDuration + exitDuration;
dispatchMouseEvent(rippleTarget, 'mousedown');
dispatchMouseEvent(rippleTarget, 'mouseup');
@@ -323,13 +325,13 @@ describe('MatRipple', () => {
// Calculates the duration for fading-in and fading-out the ripple. Also adds some
// extra time to demonstrate that the ripples are persistent.
tick(RIPPLE_FADE_IN_DURATION + RIPPLE_FADE_OUT_DURATION + 5000);
tick(enterDuration + exitDuration + 5000);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
rippleRef.fadeOut();
tick(RIPPLE_FADE_OUT_DURATION);
tick(exitDuration);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));
@@ -341,11 +343,11 @@ describe('MatRipple', () => {
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
tick(RIPPLE_FADE_IN_DURATION / 2);
tick(enterDuration / 2);
rippleDirective.fadeOutAll();
tick(RIPPLE_FADE_OUT_DURATION);
tick(exitDuration);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length)
.toBe(0, 'Expected no ripples to be active after calling fadeOutAll.');
@@ -359,7 +361,7 @@ describe('MatRipple', () => {
expect(rippleRef.state).toBe(RippleState.FADING_IN);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
tick(RIPPLE_FADE_IN_DURATION);
tick(enterDuration);
expect(rippleRef.state).toBe(RippleState.VISIBLE);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
@@ -369,12 +371,25 @@ describe('MatRipple', () => {
expect(rippleRef.state).toBe(RippleState.FADING_OUT);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
tick(RIPPLE_FADE_OUT_DURATION);
tick(exitDuration);
expect(rippleRef.state).toBe(RippleState.HIDDEN);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));
it('should allow setting a specific animation config for a ripple', fakeAsync(() => {
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
rippleDirective.launch(0, 0, {
animation: {enterDuration: 120, exitDuration: 0}
});
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
tick(120);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));
});
describe('global ripple options', () => {
@@ -449,7 +464,7 @@ describe('MatRipple', () => {
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);
tick(enterDuration * fadeInFactor + exitDuration);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));
@@ -466,11 +481,25 @@ describe('MatRipple', () => {
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);
tick(enterDuration * fadeInFactor + exitDuration);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));
it('should support changing the animation duration', fakeAsync(() => {
createTestComponent({
animation: {enterDuration: 100, exitDuration: 100}
});
rippleDirective.launch(0, 0);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
// Wait the 200ms of the enter duration and exit duration.
tick(100 + 100);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));
});
describe('configuring behavior', () => {
@@ -487,7 +516,7 @@ describe('MatRipple', () => {
});
it('sets ripple color', () => {
let backgroundColor = 'rgba(12, 34, 56, 0.8)';
const backgroundColor = 'rgba(12, 34, 56, 0.8)';
controller.color = backgroundColor;
fixture.detectChanges();
@@ -583,6 +612,20 @@ describe('MatRipple', () => {
expect(pxStringToFloat(ripple.style.width)).toBeCloseTo(2 * customRadius, 1);
expect(pxStringToFloat(ripple.style.height)).toBeCloseTo(2 * customRadius, 1);
});
it('should be able to specify animation config through binding', fakeAsync(() => {
controller.animationConfig = {enterDuration: 150, exitDuration: 150};
fixture.detectChanges();
dispatchMouseEvent(rippleTarget, 'mousedown');
dispatchMouseEvent(rippleTarget, 'mouseup');
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
tick(150 + 150);
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
}));
});
});
@@ -607,12 +650,14 @@ class BasicRippleContainer {
[matRippleCentered]="centered"
[matRippleRadius]="radius"
[matRippleDisabled]="disabled"
[matRippleAnimation]="animationConfig"
[matRippleColor]="color">
</div>
<div class="alternateTrigger"></div>
`,
})
class RippleContainerWithInputBindings {
animationConfig: RippleAnimationConfig;
trigger: HTMLElement;
centered = false;
disabled = false;
+21 -6
View File
@@ -19,7 +19,7 @@ import {
Optional,
} from '@angular/core';
import {RippleRef} from './ripple-ref';
import {RippleConfig, RippleRenderer, RippleTarget} from './ripple-renderer';
import {RippleAnimationConfig, RippleConfig, RippleRenderer, RippleTarget} from './ripple-renderer';
/** Configurable options for `matRipple`. */
export interface RippleGlobalOptions {
@@ -29,10 +29,17 @@ export interface RippleGlobalOptions {
*/
disabled?: boolean;
/**
* Configuration for the animation duration of the ripples.
* There are two phases with different durations for the ripples.
*/
animation?: RippleAnimationConfig;
/**
* If set, the default duration of the fade-in animation is divided by this value. For example,
* setting it to 0.5 will cause the ripple fade-in animation to take twice as long.
* A changed speedFactor will not affect the fade-out duration of the ripples.
* @deprecated Use the `animation` global option instead.
*/
baseSpeedFactor?: number;
}
@@ -74,9 +81,16 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget {
* If set, the normal duration of ripple animations is divided by this value. For example,
* setting it to 0.5 will cause the animations to take twice as long.
* A changed speedFactor will not modify the fade-out duration of the ripples.
* @deprecated Use the [matRippleAnimation] binding instead.
*/
@Input('matRippleSpeedFactor') speedFactor: number = 1;
/**
* Configuration for the ripple animation. Allows modifying the enter and exit animation
* duration of the ripples.
*/
@Input('matRippleAnimation') animation: RippleAnimationConfig;
/**
* Whether click events will not trigger the ripple. Ripples can be still launched manually
* by using the `launch()` method.
@@ -129,8 +143,8 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget {
}
/** Launches a manual ripple at the specified position. */
launch(x: number, y: number, config: RippleConfig = this): RippleRef {
return this._rippleRenderer.fadeInRipple(x, y, config);
launch(x: number, y: number, config?: RippleConfig): RippleRef {
return this._rippleRenderer.fadeInRipple(x, y, {...this.rippleConfig, ...config});
}
/** Fades out all currently showing ripple elements. */
@@ -142,13 +156,14 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget {
get rippleConfig(): RippleConfig {
return {
centered: this.centered,
speedFactor: this.speedFactor * (this._globalOptions.baseSpeedFactor || 1),
radius: this.radius,
color: this.color
color: this.color,
animation: {...this._globalOptions.animation, ...this.animation},
speedFactor: this.speedFactor * (this._globalOptions.baseSpeedFactor || 1),
};
}
/** Whether ripples on pointer-down are disabled or not. */
/** Whether ripples on pointer-down are disabled or not. */
get rippleDisabled(): boolean {
return this.disabled || !!this._globalOptions.disabled;
}
+3 -3
View File
@@ -8,9 +8,9 @@
<div mat-ripple class="mat-radio-ripple"
[matRippleTrigger]="label"
[matRippleDisabled]="_isRippleDisabled()"
[matRippleCentered]="_rippleConfig.centered"
[matRippleRadius]="_rippleConfig.radius"
[matRippleSpeedFactor]="_rippleConfig.speedFactor">
[matRippleCentered]="true"
[matRippleRadius]="23"
[matRippleAnimation]="{enterDuration: 300}">
</div>
</div>
+3 -3
View File
@@ -3,7 +3,7 @@ import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/f
import {Component, DebugElement} from '@angular/core';
import {By} from '@angular/platform-browser';
import {dispatchFakeEvent} from '@angular/cdk/testing';
import {RIPPLE_FADE_IN_DURATION, RIPPLE_FADE_OUT_DURATION} from '@angular/material/core';
import {defaultRippleAnimationConfig} from '@angular/material/core';
import {MatRadioButton, MatRadioChange, MatRadioGroup, MatRadioModule} from './index';
describe('MatRadio', () => {
@@ -205,13 +205,13 @@ describe('MatRadio', () => {
dispatchFakeEvent(radioInputElements[0], 'keydown');
dispatchFakeEvent(radioInputElements[0], 'focus');
tick(RIPPLE_FADE_IN_DURATION);
tick(defaultRippleAnimationConfig.enterDuration);
expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length)
.toBe(1, 'Expected one ripple after keyboard focus.');
dispatchFakeEvent(radioInputElements[0], 'blur');
tick(RIPPLE_FADE_OUT_DURATION);
tick(defaultRippleAnimationConfig.exitDuration);
expect(radioNativeElements[0].querySelectorAll('.mat-ripple-element').length)
.toBe(0, 'Expected no ripples on blur.');
+1 -5
View File
@@ -40,7 +40,6 @@ import {
mixinDisabled,
mixinDisableRipple,
mixinTabIndex,
RippleConfig,
RippleRef,
} from '@angular/material/core';
@@ -482,9 +481,6 @@ export class MatRadioButton extends _MatRadioButtonMixinBase
/** The child ripple instance. */
@ViewChild(MatRipple) _ripple: MatRipple;
/** Ripple configuration for the mouse ripples and focus indicators. */
_rippleConfig: RippleConfig = {centered: true, radius: 23, speedFactor: 1.5};
/** Reference to the current focus ripple. */
private _focusRipple: RippleRef | null;
@@ -598,7 +594,7 @@ export class MatRadioButton extends _MatRadioButtonMixinBase
/** Function is called whenever the focus changes for the input element. */
private _onInputFocusChange(focusOrigin: FocusOrigin) {
if (!this._focusRipple && focusOrigin === 'keyboard') {
this._focusRipple = this._ripple.launch(0, 0, {persistent: true, ...this._rippleConfig});
this._focusRipple = this._ripple.launch(0, 0, {persistent: true});
} else if (!focusOrigin) {
if (this.radioGroup) {
this.radioGroup._touch();
+3 -3
View File
@@ -25,9 +25,9 @@
<div class="mat-slide-toggle-ripple" mat-ripple
[matRippleTrigger]="label"
[matRippleDisabled]="disableRipple || disabled"
[matRippleCentered]="_rippleConfig.centered"
[matRippleRadius]="_rippleConfig.radius"
[matRippleSpeedFactor]="_rippleConfig.speedFactor">
[matRippleCentered]="true"
[matRippleRadius]="23"
[matRippleAnimation]="{enterDuration: 300}">
</div>
</div>
+3 -3
View File
@@ -8,7 +8,7 @@ import {NgModel, FormsModule, ReactiveFormsModule, FormControl} from '@angular/f
import {MatSlideToggle, MatSlideToggleChange, MatSlideToggleModule} from './index';
import {TestGestureConfig} from '../slider/test-gesture-config';
import {dispatchFakeEvent} from '@angular/cdk/testing';
import {RIPPLE_FADE_IN_DURATION, RIPPLE_FADE_OUT_DURATION} from '@angular/material/core';
import {defaultRippleAnimationConfig} from '@angular/material/core';
describe('MatSlideToggle without forms', () => {
let gestureConfig: TestGestureConfig;
@@ -263,14 +263,14 @@ describe('MatSlideToggle without forms', () => {
dispatchFakeEvent(inputElement, 'keydown');
dispatchFakeEvent(inputElement, 'focus');
tick(RIPPLE_FADE_IN_DURATION);
tick(defaultRippleAnimationConfig.enterDuration);
expect(slideToggleElement.querySelectorAll('.mat-ripple-element').length)
.toBe(1, 'Expected the focus ripple to be showing up.');
dispatchFakeEvent(inputElement, 'blur');
tick(RIPPLE_FADE_OUT_DURATION);
tick(defaultRippleAnimationConfig.exitDuration);
expect(slideToggleElement.querySelectorAll('.mat-ripple-element').length)
.toBe(0, 'Expected focus ripple to be removed.');
+1 -5
View File
@@ -37,7 +37,6 @@ import {
mixinDisabled,
mixinDisableRipple,
mixinTabIndex,
RippleConfig,
RippleRef,
} from '@angular/material/core';
@@ -142,9 +141,6 @@ export class MatSlideToggle extends _MatSlideToggleMixinBase implements OnDestro
/** Reference to the ripple directive on the thumb container. */
@ViewChild(MatRipple) _ripple: MatRipple;
/** Ripple configuration for the mouse ripples and focus indicators. */
_rippleConfig: RippleConfig = {centered: true, radius: 23, speedFactor: 1.5};
constructor(elementRef: ElementRef,
private _platform: Platform,
private _focusMonitor: FocusMonitor,
@@ -237,7 +233,7 @@ export class MatSlideToggle extends _MatSlideToggleMixinBase implements OnDestro
private _onInputFocusChange(focusOrigin: FocusOrigin) {
if (!this._focusRipple && focusOrigin === 'keyboard') {
// For keyboard focus show a persistent ripple as focus indicator.
this._focusRipple = this._ripple.launch(0, 0, {persistent: true, ...this._rippleConfig});
this._focusRipple = this._ripple.launch(0, 0, {persistent: true});
} else if (!focusOrigin) {
this.onTouched();
+4 -1
View File
@@ -239,7 +239,10 @@ export class MatTabLink extends _MatTabLinkMixinBase
this.tabIndex = parseInt(tabIndex) || 0;
if (globalOptions) {
this.rippleConfig = {speedFactor: globalOptions.baseSpeedFactor};
this.rippleConfig = {
speedFactor: globalOptions.baseSpeedFactor,
animation: globalOptions.animation,
};
}
}