Skip to content

Commit

Permalink
feat(ripples): support updating global ripple options at runtime
Browse files Browse the repository at this point in the history
* Allows updating any global ripple option at runtime. This makes it possible for developers to disable ripples at runtime.

Closes angular#9729
  • Loading branch information
devversion committed Jan 2, 2019
1 parent d22f48c commit e32f201
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 96 deletions.
3 changes: 3 additions & 0 deletions src/dev-app/dev-app-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {CommonModule} from '@angular/common';
import {HttpClientModule} from '@angular/common/http';
import {NgModule} from '@angular/core';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {MAT_RIPPLE_GLOBAL_OPTIONS} from '@angular/material';
import {ExampleModule} from '@angular/material-examples';
import {BrowserModule} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
Expand Down Expand Up @@ -50,6 +51,7 @@ import {ProgressBarDemo} from './progress-bar/progress-bar-demo';
import {ProgressSpinnerDemo} from './progress-spinner/progress-spinner-demo';
import {RadioDemo} from './radio/radio-demo';
import {RippleDemo} from './ripple/ripple-demo';
import {DevAppRippleOptions} from './ripple/ripple-options';
import {DEV_APP_ROUTES} from './routes';
import {ScreenTypeDemo} from './screen-type/screen-type-demo';
import {SelectDemo} from './select/select-demo';
Expand Down Expand Up @@ -140,6 +142,7 @@ import {VirtualScrollDemo} from './virtual-scroll/virtual-scroll-demo';
],
providers: [
{provide: OverlayContainer, useClass: FullscreenOverlayContainer},
{provide: MAT_RIPPLE_GLOBAL_OPTIONS, useExisting: DevAppRippleOptions},
],
entryComponents: [
ContentElementDialog,
Expand Down
3 changes: 3 additions & 0 deletions src/dev-app/dev-app.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ <h1>Angular Material Demos</h1>
<mat-icon>fullscreen</mat-icon>
</button>
<button mat-button (click)="toggleTheme()">{{dark ? 'Light' : 'Dark'}} theme</button>
<button mat-button (click)="rippleOptions.disabled = !rippleOptions.disabled">
{{rippleOptions.disabled ? 'Enable' : 'Disable'}} ripples
</button>
<button mat-button (click)="root.dir = (root.dir === 'rtl' ? 'ltr' : 'rtl')"
title="Toggle between RTL and LTR">
{{root.dir.toUpperCase()}}
Expand Down
4 changes: 3 additions & 1 deletion src/dev-app/dev-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import {OverlayContainer} from '@angular/cdk/overlay';
import {Component, ElementRef, ViewEncapsulation} from '@angular/core';
import {DevAppRippleOptions} from './ripple/ripple-options';

/** Root component for the dev-app demos. */
@Component({
Expand Down Expand Up @@ -69,7 +70,8 @@ export class DevAppComponent {

constructor(
private _element: ElementRef<HTMLElement>,
private _overlayContainer: OverlayContainer) {}
private _overlayContainer: OverlayContainer,
public rippleOptions: DevAppRippleOptions) {}

toggleFullscreen() {
// Cast to `any`, because the typings don't include the browser-prefixed methods.
Expand Down
21 changes: 21 additions & 0 deletions src/dev-app/ripple/ripple-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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 {Injectable} from '@angular/core';
import {RippleGlobalOptions} from '@angular/material';

/**
* Global ripple options for the dev-app. The ripple options are used as a class
* so that the global options can be changed at runtime.
*/
@Injectable({providedIn: 'root'})
export class DevAppRippleOptions implements RippleGlobalOptions {

/** Whether ripples should be disabled */
disabled: boolean = false;
}
1 change: 1 addition & 0 deletions src/lib/chips/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ ng_test_library(
"@angular//packages/platform-browser/animations",
"//src/cdk/a11y",
"//src/cdk/bidi",
"//src/lib/core",
"//src/cdk/keycodes",
"//src/cdk/platform",
"//src/cdk/testing",
Expand Down
19 changes: 15 additions & 4 deletions src/lib/chips/chip.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {BACKSPACE, DELETE, SPACE} from '@angular/cdk/keycodes';
import {createKeyboardEvent, dispatchFakeEvent} from '@angular/cdk/testing';
import {Component, DebugElement} from '@angular/core';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {MAT_RIPPLE_GLOBAL_OPTIONS, RippleGlobalOptions} from '@angular/material/core';
import {By} from '@angular/platform-browser';
import {Subject} from 'rxjs';
import {MatChip, MatChipEvent, MatChipSelectionChange, MatChipsModule} from './index';
Expand All @@ -13,19 +14,22 @@ describe('Chips', () => {
let chipDebugElement: DebugElement;
let chipNativeElement: HTMLElement;
let chipInstance: MatChip;
let globalRippleOptions: RippleGlobalOptions;

let dir = 'ltr';

beforeEach(async(() => {
globalRippleOptions = {};
TestBed.configureTestingModule({
imports: [MatChipsModule],
declarations: [BasicChip, SingleChip],
providers: [{
provide: Directionality, useFactory: () => ({
providers: [
{provide: MAT_RIPPLE_GLOBAL_OPTIONS, useFactory: () => globalRippleOptions},
{provide: Directionality, useFactory: () => ({
value: dir,
change: new Subject()
})
}]
})},
]
});

TestBed.compileComponents();
Expand Down Expand Up @@ -203,6 +207,13 @@ describe('Chips', () => {
subscription.unsubscribe();
});

it('should be able to disable ripples through ripple global options at runtime', () => {
expect(chipInstance.rippleDisabled).toBe(false, 'Expected chip ripples to be enabled.');

globalRippleOptions.disabled = true;

expect(chipInstance.rippleDisabled).toBe(true, 'Expected chip ripples to be disabled.');
});
});

describe('keyboard behavior', () => {
Expand Down
24 changes: 8 additions & 16 deletions src/lib/chips/chip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,21 +123,20 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes
/** Reference to the RippleRenderer for the chip. */
private _chipRipple: RippleRenderer;

/** Whether the ripples are globally disabled through the RippleGlobalOptions */
private _ripplesGloballyDisabled = false;

/**
* Ripple configuration for ripples that are launched on pointer down.
* Ripple configuration for ripples that are launched on pointer down. The ripple config
* is set to the global ripple options since we don't have any configurable options for
* the chip ripples.
* @docs-private
*/
rippleConfig: RippleConfig = {};
rippleConfig: RippleConfig & RippleGlobalOptions;

/**
* Whether ripples are disabled on interaction
* @docs-private
*/
get rippleDisabled(): boolean {
return this.disabled || this.disableRipple || this._ripplesGloballyDisabled;
return this.disabled || this.disableRipple || !!this.rippleConfig.disabled;
}

/** Whether the chip has focus. */
Expand Down Expand Up @@ -225,22 +224,15 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes
constructor(public _elementRef: ElementRef,
private _ngZone: NgZone,
platform: Platform,
@Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalOptions: RippleGlobalOptions) {
@Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS)
globalRippleOptions: RippleGlobalOptions | null) {
super(_elementRef);

this._addHostClassName();

this._chipRipple = new RippleRenderer(this, _ngZone, _elementRef, platform);
this._chipRipple.setupTriggerEvents(_elementRef.nativeElement);

if (globalOptions) {
// TODO(paul): Do not copy each option manually. Allow dynamic global option changes: #9729
this._ripplesGloballyDisabled = !!globalOptions.disabled;
this.rippleConfig = {
animation: globalOptions.animation,
terminateOnPointerUp: globalOptions.terminateOnPointerUp,
};
}
this.rippleConfig = globalRippleOptions || {};
}

_addHostClassName() {
Expand Down
52 changes: 46 additions & 6 deletions src/lib/core/ripple/ripple.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ and calling its `launch` method.
### Ripple trigger

By default ripples will fade in on interaction with the directive's host element.
In some situations, developers may want to show ripples on interaction with *some other* element,
In some situations, developers may want to show ripples on interaction with *some other* element,
but still want to have the ripples placed in another location. This can be done by specifying
the `matRippleTrigger` option that expects a reference to an `HTMLElement`.

Expand All @@ -29,7 +29,7 @@ the `matRippleTrigger` option that expects a reference to an `HTMLElement`.
<div matRipple [matRippleTrigger]="trigger" class="my-ripple-container">
<!-- This is the ripple container, but not the trigger element for ripples. -->
</div>

<div #trigger></div>
</div>
```
Expand All @@ -43,14 +43,14 @@ class MyComponent {

/** Reference to the directive instance of the ripple. */
@ViewChild(MatRipple) ripple: MatRipple;

/** Shows a centered and persistent ripple. */
launchRipple() {
const rippleRef = this.ripple.launch({
persistent: true,
centered: true
});

// Fade out the ripple later.
rippleRef.fadeOut();
}
Expand Down Expand Up @@ -91,7 +91,7 @@ const globalRippleConfig: RippleGlobalOptions = {

@NgModule({
providers: [
{provide: MAT_RIPPLE_GLOBAL_OPTIONS, useValue: globalRippleConfig}
{provide: MAT_RIPPLE_GLOBAL_OPTIONS, useValue: globalRippleConfig}
]
})
```
Expand All @@ -100,7 +100,7 @@ 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
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.

Expand Down Expand Up @@ -139,3 +139,43 @@ const globalRippleConfig: RippleGlobalOptions = {
terminateOnPointerUp: true
};
```

### Updating global options at runtime

Developers are able to update the global `ripple` options at runtime by just injecting the
the `MAT_RIPPLE_GLOBAL_OPTIONS` provider and updating its options. Note that there are
multiple ways to inject the global options.

For example, developers could create a class for the options and use it for the global
options. This makes it more simple and clean to update options at runtime.

```ts
@Injectable({providedIn: 'root'})
export class AppGlobalRippleOptions implements RippleGlobalOptions {
/** Whether ripples should be disabled globally. */
disabled: boolean = false;
}
```

```ts
@NgModule({
providers: [
{provide: MAT_RIPPLE_GLOBAL_OPTIONS, useExisting: AppGlobalRippleOptions},
]
})
export class MyModule {...}
```

Now that the global ripple options are set to an existing provider, we can inject the
service in our component and update the desired ripple options at runtime.

```ts
@Component(...)
export class MyComponent {
constructor(private _appRippleOptions: AppGlobalRippleOptions) {}

disableRipples() {
this._appRippleOptions.disabled = true;
}
}
```
2 changes: 1 addition & 1 deletion src/lib/core/ripple/ripple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget {
constructor(private _elementRef: ElementRef<HTMLElement>,
ngZone: NgZone,
platform: Platform,
@Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalOptions: RippleGlobalOptions,
@Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalOptions?: RippleGlobalOptions,
@Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string) {

this._globalOptions = globalOptions || {};
Expand Down
Loading

0 comments on commit e32f201

Please sign in to comment.