diff --git a/components/button/nz-button.component.ts b/components/button/nz-button.component.ts index b3fcd2aad47..e2523274580 100644 --- a/components/button/nz-button.component.ts +++ b/components/button/nz-button.component.ts @@ -34,18 +34,25 @@ import { findLastNotEmptyNode, isEmpty, InputBoolean, + NzConfigService, NzSizeLDSType, NzSizeMap, NzUpdateHostClassService, NzWaveConfig, NzWaveDirective, - NZ_WAVE_GLOBAL_CONFIG + NZ_WAVE_GLOBAL_CONFIG, + PropWithConfig } from 'ng-zorro-antd/core'; import { NzIconDirective } from 'ng-zorro-antd/icon'; +import { Subject } from 'rxjs'; +import { filter, takeUntil } from 'rxjs/operators'; export type NzButtonType = 'primary' | 'dashed' | 'danger' | 'default' | 'link'; export type NzButtonShape = 'circle' | 'round' | null; +const componentName = 'nzButton'; +const WithConfig = PropWithConfig(componentName); + @Component({ selector: '[nz-button]', exportAs: 'nzButton', @@ -56,9 +63,6 @@ export type NzButtonShape = 'circle' | 'round' | null; templateUrl: './nz-button.component.html' }) export class NzButtonComponent implements AfterContentInit, OnInit, OnDestroy, OnChanges { - readonly el: HTMLElement = this.elementRef.nativeElement; - private iconElement: HTMLElement; - private iconOnly = false; @ViewChild('contentElement', { static: true }) contentElement: ElementRef; @ContentChildren(NzIconDirective, { read: ElementRef }) listOfIconElement: QueryList; @HostBinding('attr.nz-wave') nzWave = new NzWaveDirective( @@ -67,13 +71,20 @@ export class NzButtonComponent implements AfterContentInit, OnInit, OnDestroy, O this.waveConfig, this.animationType ); - @Input() @InputBoolean() nzBlock = false; - @Input() @InputBoolean() nzGhost = false; - @Input() @InputBoolean() nzSearch = false; - @Input() @InputBoolean() nzLoading = false; + + @Input() @WithConfig(false) @InputBoolean() nzBlock: boolean; + @Input() @InputBoolean() nzGhost: boolean = false; + @Input() @InputBoolean() nzSearch: boolean = false; + @Input() @InputBoolean() nzLoading: boolean = false; @Input() nzType: NzButtonType = 'default'; - @Input() nzShape: NzButtonShape = null; - @Input() nzSize: NzSizeLDSType = 'default'; + @Input() @WithConfig(null) nzShape: NzButtonShape; + @Input() @WithConfig('default') nzSize: NzSizeLDSType; + + readonly el: HTMLElement = this.elementRef.nativeElement; + + private iconElement: HTMLElement; + private iconOnly = false; + private destroy$ = new Subject(); /** temp solution since no method add classMap to host https://github.com/angular/angular/issues/7289 */ setClassMap(): void { @@ -135,10 +146,20 @@ export class NzButtonComponent implements AfterContentInit, OnInit, OnDestroy, O private renderer: Renderer2, private nzUpdateHostClassService: NzUpdateHostClassService, private ngZone: NgZone, + public nzConfigService: NzConfigService, @Optional() @Inject(NZ_WAVE_GLOBAL_CONFIG) private waveConfig: NzWaveConfig, @Optional() @Inject(ANIMATION_MODULE_TYPE) private animationType: string ) { this.renderer.addClass(elementRef.nativeElement, 'ant-btn'); + this.nzConfigService.configUpdated$ + .pipe( + takeUntil(this.destroy$), + filter(n => n === componentName) + ) + .subscribe(() => { + this.setClassMap(); + this.cdr.markForCheck(); + }); } ngAfterContentInit(): void { @@ -151,6 +172,8 @@ export class NzButtonComponent implements AfterContentInit, OnInit, OnDestroy, O } ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); this.nzWave.ngOnDestroy(); } diff --git a/components/core/config/config.service.ts b/components/core/config/config.service.ts new file mode 100644 index 00000000000..934e9c15a84 --- /dev/null +++ b/components/core/config/config.service.ts @@ -0,0 +1,36 @@ +/** + * @license + * Copyright Alibaba.com 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://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +// tslint:disable no-any + +import { Inject, Injectable, Optional } from '@angular/core'; +import { Subject } from 'rxjs'; + +import { NzConfig, NZ_CONFIG } from './config'; + +@Injectable({ + providedIn: 'root' +}) +export class NzConfigService { + configUpdated$ = new Subject(); + config: NzConfig; + + constructor(@Optional() @Inject(NZ_CONFIG) defaultConfig?: NzConfig) { + this.config = defaultConfig || {}; + } + + getDefaultConfig(componentName: keyof NzConfig, propName: string): any { + const componentConfig: any = this.config[componentName] || {}; + return componentConfig[propName] || null; + } + + set(componentName: keyof NzConfig, value?: any): void { + this.config[componentName] = { ...this.config[componentName], ...(value || {}) }; + this.configUpdated$.next(componentName); + } +} diff --git a/components/core/config/config.ts b/components/core/config/config.ts new file mode 100644 index 00000000000..bcde175e850 --- /dev/null +++ b/components/core/config/config.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright Alibaba.com 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://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +import { InjectionToken } from '@angular/core'; + +export interface NzConfig { + nzButton?: NzButtonConfig; +} + +export interface NzButtonConfig { + nzSize?: 'default' | 'large' | 'small'; + nzShape?: 'circle' | 'round'; + nzBlock?: boolean; +} + +/** + * User should provide an object implements this interface to set global configurations. + */ +export const NZ_CONFIG = new InjectionToken('nz-config'); diff --git a/components/core/config/index.ts b/components/core/config/index.ts new file mode 100644 index 00000000000..f17e95188c8 --- /dev/null +++ b/components/core/config/index.ts @@ -0,0 +1,9 @@ +/** + * @license + * Copyright Alibaba.com 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://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +export * from './public-api'; diff --git a/components/core/config/public-api.ts b/components/core/config/public-api.ts new file mode 100644 index 00000000000..38352bfbf97 --- /dev/null +++ b/components/core/config/public-api.ts @@ -0,0 +1,11 @@ +/** + * @license + * Copyright Alibaba.com 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://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +export * from './config.service'; +export * from './config'; +export * from './utils'; diff --git a/components/core/config/utils.ts b/components/core/config/utils.ts new file mode 100644 index 00000000000..5dea44156c3 --- /dev/null +++ b/components/core/config/utils.ts @@ -0,0 +1,50 @@ +/** + * @license + * Copyright Alibaba.com 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://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ +// import { Type } from '@angular/core'; + +// tslint:disable:no-invalid-this +// tslint:disable:no-any + +// export function ComponentWithConfig(type: Type): (t: any) => void { +// return function(target: any): void {}; +// } + +import { NzConfigService } from 'ng-zorro-antd'; +import { NzConfig } from './config'; + +/** + * This decorator is used to decorate properties. If a property is decorated, it would try to load default value from + * config. + */ +// tslint:disable-next-line:typedef +export function PropWithConfig(componentName: keyof NzConfig) { + return function(innerDefaultValue: T): (t: any, p: string) => void { + return function(target: any, propName: string): void { + const privatePropName = `$$__assignedValue__${propName}`; + + if (Object.prototype.hasOwnProperty.call(target, privatePropName)) { + console.warn(`The prop "${privatePropName}" is already exist, it will be override by ${name} decorator.`); + } + + Object.defineProperty(target, privatePropName, { + configurable: true, + writable: true + }); + + Object.defineProperty(target, propName, { + get(): T { + const defaultConfig = ((this.nzConfigService as NzConfigService).config[componentName] as any)[propName]; + return this[privatePropName] || defaultConfig || innerDefaultValue; + }, + set(value?: T): void { + this[privatePropName] = value; + } + }); + }; + }; +} diff --git a/components/core/public-api.ts b/components/core/public-api.ts index bf33ab9b4e6..b9647b5df02 100644 --- a/components/core/public-api.ts +++ b/components/core/public-api.ts @@ -22,3 +22,4 @@ export * from './wave/public-api'; export * from './dropdown/public-api'; export * from './logger/public-api'; export * from './responsive/public-api'; +export * from './config/public-api'; diff --git a/components/core/util/convert.ts b/components/core/util/convert.ts index 772bb96d977..ffb7fd2885b 100644 --- a/components/core/util/convert.ts +++ b/components/core/util/convert.ts @@ -39,7 +39,7 @@ function propDecoratorFactory(name: string, fallback: (v: T) => D): (targe const privatePropName = `$$__${propName}`; if (Object.prototype.hasOwnProperty.call(target, privatePropName)) { - console.warn(`The prop "${privatePropName}" is already exist, it will be overrided by ${name} decorator.`); + console.warn(`The prop "${privatePropName}" is already exist, it will be override by ${name} decorator.`); } Object.defineProperty(target, privatePropName, {