diff --git a/components/button/demo/basic.ts b/components/button/demo/basic.ts index 71347a31618..00d9a4f743d 100644 --- a/components/button/demo/basic.ts +++ b/components/button/demo/basic.ts @@ -7,6 +7,7 @@ import { Component } from '@angular/core'; + `, styles: [ ` diff --git a/components/button/demo/block.ts b/components/button/demo/block.ts index edc33da7993..e185dae8c75 100644 --- a/components/button/demo/block.ts +++ b/components/button/demo/block.ts @@ -7,6 +7,7 @@ import { Component } from '@angular/core'; + `, styles: [ ` diff --git a/components/button/demo/disabled.ts b/components/button/demo/disabled.ts index 35751830d5a..5c9f45b0f8f 100644 --- a/components/button/demo/disabled.ts +++ b/components/button/demo/disabled.ts @@ -11,6 +11,9 @@ import { Component } from '@angular/core';
+
+ +
diff --git a/components/button/demo/ghost.ts b/components/button/demo/ghost.ts index cccaa5120f8..8a804a748c0 100644 --- a/components/button/demo/ghost.ts +++ b/components/button/demo/ghost.ts @@ -8,6 +8,7 @@ import { Component } from '@angular/core'; +
`, styles: [ diff --git a/components/button/demo/size.ts b/components/button/demo/size.ts index 6fc47ee6867..c685095542e 100644 --- a/components/button/demo/size.ts +++ b/components/button/demo/size.ts @@ -14,6 +14,7 @@ import { Component } from '@angular/core'; +
diff --git a/components/button/doc/index.en-US.md b/components/button/doc/index.en-US.md index e816e47475b..1cac8365add 100644 --- a/components/button/doc/index.en-US.md +++ b/components/button/doc/index.en-US.md @@ -30,5 +30,5 @@ To get a customized button, just set `nzType`/`nzShape`/`nzSize`/`nzLoading`/`di | `[nzLoading]` | set the loading status of button | `boolean` | `false` | | `[nzShape]` | can be set to `circle` `round` or omitted | `'circle'|'round'` | - | | `[nzSize]` | can be set to `small` `large` or omitted | `'large'|'small'|'default'` | `'default'` | -| `[nzType]` | can be set to `primary` `dashed` `danger` or omitted (meaning `default`) | `'primary'|'dashed'|'danger'|'default'` | `'default'` | +| `[nzType]` | can be set to `primary` `dashed` `danger` or omitted (meaning `default`) | `'primary'|'dashed'|'danger'|'default'|'link'` | `'default'` | | `[nzBlock]` | option to fit button width to its parent width | `boolean` | `false` | diff --git a/components/button/doc/index.zh-CN.md b/components/button/doc/index.zh-CN.md index fedf532f9b9..8fade534be5 100644 --- a/components/button/doc/index.zh-CN.md +++ b/components/button/doc/index.zh-CN.md @@ -34,5 +34,5 @@ import { NzButtonModule } from 'ng-zorro-antd'; | `[nzLoading]` | 设置按钮载入状态 | `boolean` | `false` | | `[nzShape]` | 设置按钮形状,可选值为 `circle` `round` 或者不设 | `'circle'|'round'` | - | | `[nzSize]` | 设置按钮大小,可选值为 `small` `large` 或者不设 | `'large'|'small'|'default'` | `'default'` | -| `[nzType]` | 设置按钮类型,可选值为 `primary` `dashed` `danger` 或者不设 | `'primary'|'dashed'|'danger'|'default'` | `'default'` | +| `[nzType]` | 设置按钮类型,可选值为 `primary` `dashed` `danger` 或者不设 | `'primary'|'dashed'|'danger'|'default'|'link'` | `'default'` | | `[nzBlock]` | 将按钮宽度调整为其父宽度的选项 | `boolean` | `false` | diff --git a/components/button/nz-button.component.ts b/components/button/nz-button.component.ts index a1d113c07db..62ede32067f 100644 --- a/components/button/nz-button.component.ts +++ b/components/button/nz-button.component.ts @@ -43,7 +43,7 @@ import { } from 'ng-zorro-antd/core'; import { NzIconDirective } from 'ng-zorro-antd/icon'; -export type NzButtonType = 'primary' | 'dashed' | 'danger' | 'default'; +export type NzButtonType = 'primary' | 'dashed' | 'danger' | 'default' | 'link'; export type NzButtonShape = 'circle' | 'round' | null; @Component({ diff --git a/components/button/nz-button.spec.ts b/components/button/nz-button.spec.ts index ea185ad8efa..b04b0de0e37 100644 --- a/components/button/nz-button.spec.ts +++ b/components/button/nz-button.spec.ts @@ -40,6 +40,7 @@ describe('button', () => { expect(buttons[1].nativeElement.classList.contains('ant-btn-default')).toBe(true); expect(buttons[2].nativeElement.classList.contains('ant-btn-dashed')).toBe(true); expect(buttons[3].nativeElement.classList.contains('ant-btn-danger')).toBe(true); + expect(buttons[4].nativeElement.classList.contains('ant-btn-link')).toBe(true); }); }); @@ -115,10 +116,7 @@ describe('button', () => { it('should have correct style', () => { fixture.detectChanges(); - expect(buttons[0].nativeElement.classList.contains('ant-btn-background-ghost')).toBe(true); - expect(buttons[1].nativeElement.classList.contains('ant-btn-background-ghost')).toBe(true); - expect(buttons[2].nativeElement.classList.contains('ant-btn-background-ghost')).toBe(true); - expect(buttons[3].nativeElement.classList.contains('ant-btn-background-ghost')).toBe(true); + expect(buttons.every(button => button.nativeElement.classList.contains('ant-btn-background-ghost'))).toBe(true); }); }); @@ -299,11 +297,9 @@ describe('button', () => { expect(buttons[1].nativeElement.classList.contains('ant-btn-default')).toBe(true); expect(buttons[2].nativeElement.classList.contains('ant-btn-dashed')).toBe(true); expect(buttons[3].nativeElement.classList.contains('ant-btn-danger')).toBe(true); + expect(buttons[4].nativeElement.classList.contains('ant-btn-link')).toBe(true); - expect(buttons[0].nativeElement.classList.contains('ant-btn-block')).toBe(true); - expect(buttons[1].nativeElement.classList.contains('ant-btn-block')).toBe(true); - expect(buttons[2].nativeElement.classList.contains('ant-btn-block')).toBe(true); - expect(buttons[3].nativeElement.classList.contains('ant-btn-block')).toBe(true); + expect(buttons.every(button => button.nativeElement.classList.contains('ant-btn-block'))).toBe(true); }); }); diff --git a/components/button/style/index.less b/components/button/style/index.less index ef371977504..8692cf86d9c 100644 --- a/components/button/style/index.less +++ b/components/button/style/index.less @@ -69,6 +69,10 @@ .btn-danger; } + &-link { + .btn-link; + } + &-round { .btn-round(@btn-prefix-cls); } @@ -90,8 +94,8 @@ border-radius: inherit; opacity: 0.35; transition: opacity 0.2s; - pointer-events: none; content: ''; + pointer-events: none; } .@{iconfont-css-prefix} { @@ -107,14 +111,17 @@ } } + &&-loading { + position: relative; + pointer-events: none; + } + &&-loading::before { display: block; } &&-loading:not(&-circle):not(&-circle-outline):not(&-icon-only) { - position: relative; padding-left: 29px; - pointer-events: none; .@{iconfont-css-prefix}:not(:last-child) { margin-left: -14px; } @@ -162,6 +169,12 @@ .button-variant-ghost(@btn-danger-color); } + &-background-ghost&-link { + .button-variant-ghost(@link-color; transparent); + + color: @component-background; + } + &-two-chinese-chars::first-letter { letter-spacing: 0.34em; } @@ -189,4 +202,4 @@ a.@{btn-prefix-cls} { &-sm { line-height: @btn-height-sm - 2px; } -} +} \ No newline at end of file diff --git a/components/button/style/mixin.less b/components/button/style/mixin.less index 58b0f6fd0f1..19a83b6e3ae 100644 --- a/components/button/style/mixin.less +++ b/components/button/style/mixin.less @@ -7,7 +7,7 @@ border-radius: @border-radius; } -.button-disabled() { +.button-disabled(@color: @btn-disable-color; @background: @btn-disable-bg; @border: @btn-disable-border) { &-disabled, &.disabled, &[disabled] { @@ -16,7 +16,8 @@ &:focus, &:active, &.active { - .button-color(@btn-disable-color; @btn-disable-bg; @btn-disable-border); + .button-color(@color; @background; @border); + text-shadow: none; box-shadow: none; } @@ -25,6 +26,7 @@ .button-variant-primary(@color; @background) { .button-color(@color; @background; @background); + text-shadow: @btn-text-shadow; box-shadow: @btn-primary-shadow; @@ -84,16 +86,26 @@ } .button-disabled(); } -.button-variant-ghost(@color) { - .button-color(@color; transparent; @color); +.button-variant-ghost(@color; @border: @color) { + .button-color(@color; transparent; @border); text-shadow: none; &:hover, &:focus { - .button-color(~`colorPalette('@{color}', 5) `; transparent; ~`colorPalette('@{color}', 5) `); + & when (@border = transparent) { + .button-color(~`colorPalette('@{color}', 5) `; transparent; transparent); + } + & when not(@border = transparent) { + .button-color(~`colorPalette('@{color}', 5) `; transparent; ~`colorPalette('@{color}', 5) `); + } } &:active, &.active { - .button-color(~`colorPalette('@{color}', 7) `; transparent; ~`colorPalette('@{color}', 7) `); + & when (@border = transparent) { + .button-color(~`colorPalette('@{color}', 7) `; transparent; transparent); + } + & when not(@border = transparent) { + .button-color(~`colorPalette('@{color}', 7) `; transparent; ~`colorPalette('@{color}', 7) `); + } } .button-disabled(); } @@ -220,6 +232,17 @@ .btn-danger() { .button-variant-danger(@btn-danger-color, @btn-danger-bg, @btn-danger-border); } +// link button style +.btn-link() { + .button-variant-other(@link-color, transparent, transparent); + box-shadow: none; + &:hover, + &:focus, + &:active { + border-color: transparent; + } + .button-disabled(@disabled-color; transparent; transparent); +} // round button .btn-round(@btnClassName: btn) { .button-size(@btn-circle-size; 0 @btn-circle-size / 2; @font-size-base + 2px; @btn-circle-size); @@ -247,7 +270,7 @@ .button-size(@btn-circle-size-sm; 0; @font-size-base; 50%); } } -// Horizontal button groups styl +// Horizontal button groups style // -------------------------------------------------- .btn-group(@btnClassName: btn) { .button-group-base(@btnClassName); @@ -322,4 +345,4 @@ border-top-left-radius: 0; border-bottom-left-radius: 0; } -} +} \ No newline at end of file diff --git a/components/core/wave/nz-wave-renderer.ts b/components/core/wave/nz-wave-renderer.ts index 88dc486b166..08bf5020ef8 100644 --- a/components/core/wave/nz-wave-renderer.ts +++ b/components/core/wave/nz-wave-renderer.ts @@ -14,16 +14,13 @@ export class NzWaveRenderer { private styleForPseudo: HTMLStyleElement | null; private extraNode: HTMLDivElement | null; private lastTime = 0; - + private platform = new Platform(); 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(); - } + this.bindTriggerEvent(); } onClick = (event: MouseEvent) => { @@ -40,11 +37,13 @@ export class NzWaveRenderer { }; bindTriggerEvent(): void { - this.ngZone.runOutsideAngular(() => { - if (this.triggerElement) { - this.triggerElement.addEventListener('click', this.onClick, true); - } - }); + if (this.platform.isBrowser) { + this.ngZone.runOutsideAngular(() => { + if (this.triggerElement) { + this.triggerElement.addEventListener('click', this.onClick, true); + } + }); + } } removeTriggerEvent(): void { diff --git a/components/core/wave/nz-wave.directive.ts b/components/core/wave/nz-wave.directive.ts index 2a30546a765..35817cedc16 100644 --- a/components/core/wave/nz-wave.directive.ts +++ b/components/core/wave/nz-wave.directive.ts @@ -47,6 +47,14 @@ export class NzWaveDirective implements OnInit, OnDestroy { private waveRenderer: NzWaveRenderer; private waveDisabled: boolean = false; + get disabled(): boolean { + return this.waveDisabled; + } + + get rendererRef(): NzWaveRenderer { + return this.waveRenderer; + } + constructor( private ngZone: NgZone, private elementRef: ElementRef, @@ -76,4 +84,21 @@ export class NzWaveDirective implements OnInit, OnDestroy { this.waveRenderer = new NzWaveRenderer(this.elementRef.nativeElement, this.ngZone, this.nzWaveExtraNode); } } + + disable(): void { + this.waveDisabled = true; + if (this.waveRenderer) { + this.waveRenderer.removeTriggerEvent(); + this.waveRenderer.removeStyleAndExtraNode(); + } + } + + enable(): void { + this.waveDisabled = false; + if (this.waveRenderer) { + this.waveRenderer.bindTriggerEvent(); + } else { + this.renderWaveIfEnabled(); + } + } } diff --git a/components/core/wave/nz-wave.spec.ts b/components/core/wave/nz-wave.spec.ts index 86ac40a77fc..ae6f01281ea 100644 --- a/components/core/wave/nz-wave.spec.ts +++ b/components/core/wave/nz-wave.spec.ts @@ -1,5 +1,6 @@ import { Component, ElementRef, ViewChild } from '@angular/core'; import { fakeAsync, tick, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { dispatchMouseEvent } from '../testing'; import { NzWaveDirective } from './nz-wave.directive'; import { NzWaveModule } from './nz-wave.module'; @@ -8,14 +9,14 @@ const WAVE_ATTRIBUTE_NAME = 'ant-click-animating-without-extra-node'; const WAVE_ATTRIBUTE_NAME_EXTRA_NODE = 'ant-click-animating'; const EXTRA_NODE_CLASS_NAME = '.ant-click-animating-node'; -describe('nz-wave', () => { - let fixture: ComponentFixture; +describe('nz-wave base', () => { + let fixture: ComponentFixture; let waveTarget: HTMLElement; beforeEach(() => { TestBed.configureTestingModule({ imports: [NzWaveModule], - declarations: [WaveContainerWithButtonComponent, WaveContainerWithExtraNodeComponent] + declarations: [WaveContainerWithButtonComponent] }); }); @@ -52,7 +53,7 @@ describe('nz-wave', () => { }); it('should not create wave on click when disabled', () => { - (fixture.componentInstance as WaveContainerWithButtonComponent).disabled = true; + fixture.componentInstance.disabled = true; fixture.detectChanges(); dispatchMouseEvent(waveTarget, 'click'); expect(waveTarget.hasAttribute(WAVE_ATTRIBUTE_NAME)).toBe(false); @@ -101,6 +102,18 @@ describe('nz-wave', () => { expect(document.body.querySelector('style') !== null).toBe(false); }); }); +}); + +describe('nz-wave extra', () => { + let fixture: ComponentFixture; + let waveTarget: HTMLElement; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [NzWaveModule], + declarations: [WaveContainerWithExtraNodeComponent] + }); + }); describe('extra node wave', () => { beforeEach(() => { @@ -172,6 +185,58 @@ describe('nz-wave', () => { }); }); +describe('nz-wave disable/enable', () => { + let fixture: ComponentFixture; + let waveTarget: HTMLElement; + let waveRef: NzWaveDirective; + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [NzWaveModule, NoopAnimationsModule], + declarations: [WaveContainerWithButtonComponent] + }); + }); + + describe('disable/enable', () => { + beforeEach(() => { + fixture = TestBed.createComponent(WaveContainerWithButtonComponent); + fixture.detectChanges(); + waveTarget = fixture.componentInstance.trigger.nativeElement; + waveRef = fixture.componentInstance.wave; + }); + + it('should disable by NoopAnimationsModule ', () => { + expect(waveRef.disabled).toBe(true); + expect(waveRef.rendererRef).toBeFalsy(); + waveRef.disable(); + expect(waveRef.rendererRef).toBeFalsy(); + }); + + it('should create waveRenderer when called enable', () => { + waveRef.enable(); + expect(waveRef.disabled).toBe(false); + expect(waveRef.rendererRef).toBeTruthy(); + }); + + it('should enable work', () => { + waveRef.enable(); + expect(waveRef.disabled).toBe(false); + expect(waveRef.rendererRef).toBeTruthy(); + dispatchMouseEvent(waveTarget, 'click'); + expect(waveTarget.hasAttribute(WAVE_ATTRIBUTE_NAME)).toBe(true); + expect(document.body.querySelector('style') !== null).toBe(true); + }); + + it('should disable work', () => { + waveRef.disable(); + expect(waveRef.disabled).toBe(true); + expect(waveRef.rendererRef).toBeFalsy(); + dispatchMouseEvent(waveTarget, 'click'); + expect(waveTarget.hasAttribute(WAVE_ATTRIBUTE_NAME)).toBe(false); + expect(document.body.querySelector('style') === null).toBe(true); + }); + }); +}); + @Component({ template: `