Skip to content

Commit

Permalink
refactor(module:core): add wave directive (#2075)
Browse files Browse the repository at this point in the history
  • Loading branch information
hsuanxyz authored and wen committed Sep 2, 2018
1 parent ed4120f commit c3d16fb
Show file tree
Hide file tree
Showing 11 changed files with 421 additions and 22 deletions.
30 changes: 14 additions & 16 deletions components/button/nz-button.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import {
ChangeDetectorRef,
Component,
ElementRef,
HostListener,
Input,
HostBinding,
Input, NgZone, OnDestroy, OnInit,
Renderer2,
ViewChild
} from '@angular/core';

import { NzUpdateHostClassService } from '../core/services/update-host-class.service';
import { isEmpty } from '../core/util/check';
import { toBoolean } from '../core/util/convert';
import { NzWaveDirective } from '../core/wave/nz-wave.directive';

export type NzButtonType = 'primary' | 'dashed' | 'danger';
export type NzButtonShape = 'circle' | null ;
Expand All @@ -23,7 +24,7 @@ export type NzButtonSize = 'small' | 'large' | 'default' ;
preserveWhitespaces: false,
templateUrl : './nz-button.component.html'
})
export class NzButtonComponent implements AfterContentInit {
export class NzButtonComponent implements AfterContentInit, OnInit, OnDestroy {
private _ghost = false;
private _search = false;
private _type: NzButtonType;
Expand All @@ -33,7 +34,6 @@ export class NzButtonComponent implements AfterContentInit {
private el: HTMLElement;
private iconElement: HTMLElement;
private iconOnly = false;
private clicked = false;
private prefixCls = 'ant-btn';
private sizeMap = { large: 'lg', small: 'sm' };
@ViewChild('contentElement') contentElement: ElementRef;
Expand Down Expand Up @@ -99,16 +99,7 @@ export class NzButtonComponent implements AfterContentInit {
return this._loading;
}

/** toggle button clicked animation */
@HostListener('click')
onClick(): void {
this.clicked = true;
this.setClassMap();
setTimeout(() => {
this.clicked = false;
this.setClassMap();
}, 300);
}
@HostBinding('attr.nz-wave') nzWave = new NzWaveDirective(this.ngZone, this.elementRef);

updateIconDisplay(value: boolean): void {
if (this.iconElement) {
Expand All @@ -123,7 +114,6 @@ export class NzButtonComponent implements AfterContentInit {
[ `${this.prefixCls}-${this.nzShape}` ] : this.nzShape,
[ `${this.prefixCls}-${this.sizeMap[ this.nzSize ]}` ]: this.sizeMap[ this.nzSize ],
[ `${this.prefixCls}-loading` ] : this.nzLoading,
[ `${this.prefixCls}-clicked` ] : this.clicked,
[ `${this.prefixCls}-icon-only` ] : this.iconOnly,
[ `${this.prefixCls}-background-ghost` ] : this.nzGhost,
[ `ant-input-search-button` ] : this.nzSearch
Expand Down Expand Up @@ -187,12 +177,20 @@ export class NzButtonComponent implements AfterContentInit {
return null;
}

constructor(private elementRef: ElementRef, private cdr: ChangeDetectorRef, private renderer: Renderer2, private nzUpdateHostClassService: NzUpdateHostClassService) {
constructor(private elementRef: ElementRef, private cdr: ChangeDetectorRef, private renderer: Renderer2, private nzUpdateHostClassService: NzUpdateHostClassService, private ngZone: NgZone) {
this.el = this.elementRef.nativeElement;
this.renderer.addClass(this.el, this.prefixCls);
}

ngAfterContentInit(): void {
this.checkContent();
}

ngOnInit(): void {
this.nzWave.ngOnInit();
}

ngOnDestroy(): void {
this.nzWave.ngOnDestroy();
}
}
7 changes: 4 additions & 3 deletions components/button/nz-button.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { ObserversModule } from '@angular/cdk/observers';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { NzWaveModule } from '../core/wave/nz-wave.module';

import { NzButtonGroupComponent } from './nz-button-group.component';
import { NzButtonComponent } from './nz-button.component';

@NgModule({
declarations: [ NzButtonComponent, NzButtonGroupComponent ],
exports : [ NzButtonComponent, NzButtonGroupComponent ],
imports : [ CommonModule, ObserversModule ]
declarations : [ NzButtonComponent, NzButtonGroupComponent ],
exports : [ NzButtonComponent, NzButtonGroupComponent ],
imports : [ CommonModule, ObserversModule, NzWaveModule ]
})
export class NzButtonModule {
}
1 change: 1 addition & 0 deletions components/core/wave/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './public-api';
124 changes: 124 additions & 0 deletions components/core/wave/nz-wave-renderer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { Platform } from '@angular/cdk/platform';
import { NgZone } from '@angular/core';

export class NzWaveRenderer {

readonly waveTransitionDuration = 400;
private styleForPseudo: HTMLStyleElement | null;
private extraNode: HTMLDivElement | null;
private lastTime = 0;

get waveAttributeName(): string {
return this.insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
}

constructor(private triggerElement: HTMLElement, private ngZone: NgZone, private insertExtraNode: boolean) {
const platform = new Platform();
if (platform.isBrowser) {
this.bindTriggerEvent();
}
}

onClick = (event: MouseEvent) => {
if (
!this.triggerElement ||
!this.triggerElement.getAttribute ||
this.triggerElement.getAttribute('disabled') ||
(event.target as HTMLElement).tagName === 'INPUT' ||
this.triggerElement.className.indexOf('disabled') >= 0) {
return;
}
this.fadeOutWave();
}

bindTriggerEvent(): void {
this.ngZone.runOutsideAngular(() => {
if (this.triggerElement) {
this.triggerElement.addEventListener('click', this.onClick, true);
}
});
}

removeTriggerEvent(): void {
if (this.triggerElement) {
this.triggerElement.removeEventListener('click', this.onClick, true);
}
}

removeStyleAndExtraNode(): void {
if (this.styleForPseudo && document.body.contains(this.styleForPseudo)) {
document.body.removeChild(this.styleForPseudo);
this.styleForPseudo = null;
}
if (this.insertExtraNode && this.triggerElement.contains(this.extraNode)) {
this.triggerElement.removeChild(this.extraNode);
}
}

destroy(): void {
this.removeTriggerEvent();
this.removeStyleAndExtraNode();
}

private fadeOutWave(): void {
const node = this.triggerElement;
const waveColor = this.getWaveColor(node);
node.setAttribute(this.waveAttributeName, 'true');
if (Date.now() < this.lastTime + this.waveTransitionDuration) {
return;
}

if (this.isValidColor(waveColor)) {
if (!this.styleForPseudo) {
this.styleForPseudo = document.createElement('style');
}

this.styleForPseudo.innerHTML =
`[ant-click-animating-without-extra-node]:after { border-color: ${waveColor}; }`;
document.body.appendChild(this.styleForPseudo);
}

if (this.insertExtraNode) {
if (!this.extraNode) {
this.extraNode = document.createElement('div');
}
this.extraNode.className = 'ant-click-animating-node';
node.appendChild(this.extraNode);
}

this.lastTime = Date.now();

this.runTimeoutOutsideZone(() => {
node.removeAttribute(this.waveAttributeName);
this.removeStyleAndExtraNode();
}, this.waveTransitionDuration);
}

private isValidColor(color: string): boolean {
return color
&& color !== '#ffffff'
&& color !== 'rgb(255, 255, 255)'
&& this.isNotGrey(color)
&& !/rgba\(\d*, \d*, \d*, 0\)/.test(color)
&& color !== 'transparent';
}

private isNotGrey(color: string): boolean {
const match = color.match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/);
if (match && match[ 1 ] && match[ 2 ] && match[ 3 ]) {
return !(match[ 1 ] === match[ 2 ] && match[ 2 ] === match[ 3 ]);
}
return true;
}

private getWaveColor(node: HTMLElement): string {
const nodeStyle = getComputedStyle(node);
return nodeStyle.getPropertyValue('border-top-color') || // Firefox Compatible
nodeStyle.getPropertyValue('border-color') ||
nodeStyle.getPropertyValue('background-color');
}

private runTimeoutOutsideZone(fn: () => void, delay: number): void {
this.ngZone.runOutsideAngular(() => setTimeout(fn, delay));
}
}
27 changes: 27 additions & 0 deletions components/core/wave/nz-wave.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Directive, ElementRef, Input, NgZone, OnDestroy, OnInit } from '@angular/core';
import { NzWaveRenderer } from './nz-wave-renderer';

@Directive({
selector: '[nz-wave]'
})
export class NzWaveDirective implements OnInit, OnDestroy {

private waveRenderer: NzWaveRenderer;

@Input() nzWaveExtraNode = false;

constructor(private ngZone: NgZone, private elementRef: ElementRef) {
}

ngOnDestroy(): void {
if (this.waveRenderer) {
this.waveRenderer.destroy();
}
}

ngOnInit(): void {
if (this.elementRef.nativeElement) {
this.waveRenderer = new NzWaveRenderer(this.elementRef.nativeElement, this.ngZone, this.nzWaveExtraNode);
}
}
}
10 changes: 10 additions & 0 deletions components/core/wave/nz-wave.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { PlatformModule } from '@angular/cdk/platform';
import { NgModule } from '@angular/core';
import { NzWaveDirective } from './nz-wave.directive';

@NgModule({
imports : [ PlatformModule ],
exports : [ NzWaveDirective ],
declarations: [ NzWaveDirective ]
})
export class NzWaveModule { }
Loading

0 comments on commit c3d16fb

Please sign in to comment.