diff --git a/components/core/interface/interface.ts b/components/core/interface/interface.ts new file mode 100644 index 00000000000..91c9b7af44a --- /dev/null +++ b/components/core/interface/interface.ts @@ -0,0 +1,3 @@ +export interface ClassMap { + [ key: string ]: boolean; +} diff --git a/components/core/style/map.ts b/components/core/style/map.ts new file mode 100644 index 00000000000..a344f42a3d5 --- /dev/null +++ b/components/core/style/map.ts @@ -0,0 +1,3 @@ +export function classMapToString(map: { [ key: string ]: boolean }): string { + return Object.keys(map).filter(item => !!map[ item ]).join(' '); +} diff --git a/components/steps/demo/icon.md b/components/steps/demo/icon.md index 85c04bece6a..369ab044b2f 100755 --- a/components/steps/demo/icon.md +++ b/components/steps/demo/icon.md @@ -7,7 +7,7 @@ title: ## zh-CN -通过设置 `nz-step` 的 `nzIcon` 属性,可以启用自定义图标。旧的 API 仍然可用,但我们建议您迁移到新的 API。 +通过设置 `nz-step` 的 `nzIcon` 属性,可以启用自定义图标。 ## en-US diff --git a/components/steps/doc/index.en-US.md b/components/steps/doc/index.en-US.md index f2055d79e97..06ec7ea22c1 100755 --- a/components/steps/doc/index.en-US.md +++ b/components/steps/doc/index.en-US.md @@ -27,12 +27,13 @@ The whole of the step bar. | Property | Description | Type | Default | | -------- | ----------- | ---- | ------- | -| `[nzCurrent]` | to set the current step, counting from 0. You can overwrite this state by using `nzStatus` of `nz-step` | number | 0 | -| `[nzDirection]` | to specify the direction of the step bar, `horizontal` and `vertical` are currently supported | string | `horizontal` | +| `[nzCurrent]` | To set the current step, counting from 0. You can overwrite this state by using `nzStatus` of `nz-step` | number | 0 | +| `[nzDirection]` | To specify the direction of the step bar, `horizontal` and `vertical` are currently supported | string | `horizontal` | +| `[nzLabelPlacement]` | Support vertical title and description | string | `horizontal` | | `[nzProgressDot]` | Steps with progress dot style, customize the progress dot by setting it with TemplateRef | Boolean 丨 `TemplateRef<{ $implicit: TemplateRef, status: string, index: number }>` | false | -| `[nzSize]` | to specify the size of the step bar, `default` and `small` are currently supported | string | `default` | -| `[nzStatus]` | to specify the status of current step, can be set to one of the following values: `wait` `process` `finish` `error` | string | `process` | -| `[nzStartIndex]` | to specify the starting number | number | 0 | +| `[nzSize]` | To specify the size of the step bar, `default` and `small` are currently supported | string | `default` | +| `[nzStatus]` | To specify the status of current step, can be set to one of the following values: `wait` `process` `finish` `error` | string | `process` | +| `[nzStartIndex]` | To specify the starting number | number | 0 | ### nz-step diff --git a/components/steps/doc/index.zh-CN.md b/components/steps/doc/index.zh-CN.md index 52247958a12..56b8ee63447 100755 --- a/components/steps/doc/index.zh-CN.md +++ b/components/steps/doc/index.zh-CN.md @@ -29,7 +29,8 @@ title: Steps | 参数 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | `[nzCurrent]` | 指定当前步骤,从 0 开始记数。在子 `nz-step` 元素中,可以通过 `nzStatus` 属性覆盖状态 | number | 0 | -| `[nzDirection]` | 指定步骤条方向。目前支持水平(`horizontal`)和竖直(`vertical`)两种方向 | string | horizontal | +| `[nzDirection]` | 指定步骤条方向。目前支持水平(`horizontal`)和竖直(`vertical`)两种方向 | string | `horizontal` | +| `[nzLabelPlacement]` | 指定标签放置位置,默认水平放图标右侧,可选 `vertical` 放图标下方 | string | `horizontal` | | `[nzProgressDot]` | 点状步骤条,可以设置为一个 TemplateRef | Boolean 丨 `TemplateRef<{ $implicit: TemplateRef, status: string, index: number }>` | false | | `[nzSize]` | 指定大小,目前支持普通(`default`)和迷你(`small`) | string | default | | `[nzStatus]` | 指定当前步骤的状态,可选 `wait` `process` `finish` `error` | string | process | diff --git a/components/steps/nz-step.component.html b/components/steps/nz-step.component.html index f08ae7594e4..790cb7fb03d 100644 --- a/components/steps/nz-step.component.html +++ b/components/steps/nz-step.component.html @@ -7,15 +7,9 @@
- - - - - - - - {{ index + 1 }} - + + + {{ index + 1 }} @@ -41,4 +35,4 @@
{{ nzDescription }}
-
\ No newline at end of file + diff --git a/components/steps/nz-step.component.ts b/components/steps/nz-step.component.ts index c555a3a606b..2a53fd90c0c 100644 --- a/components/steps/nz-step.component.ts +++ b/components/steps/nz-step.component.ts @@ -1,52 +1,33 @@ -import { - Component, - ElementRef, - Input, - TemplateRef, - ViewChild -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, SimpleChanges, TemplateRef, ViewChild } from '@angular/core'; -import { NzUpdateHostClassService } from '../core/services/update-host-class.service'; +import { classMapToString } from '../core/style/map'; import { NgClassType } from '../core/types/ng-class'; @Component({ + changeDetection : ChangeDetectionStrategy.OnPush, selector : 'nz-step', - providers : [ NzUpdateHostClassService ], preserveWhitespaces: false, - templateUrl : './nz-step.component.html' + templateUrl : './nz-step.component.html', + host : { + '[class]': 'classString' + } }) -export class NzStepComponent { - private _status = 'wait'; - private _currentIndex = 0; - private _description: string | TemplateRef; - private _icon: NgClassType | TemplateRef; - private _title: string | TemplateRef; - private el: HTMLElement = this.elementRef.nativeElement; - oldAPIIcon = true; // Make the user defined icon compatible to old API. Should be removed in 2.0. - isCustomStatus = false; - isDescriptionString = true; - isTitleString = true; - isIconString = true; - last = false; - showProcessDot = false; - direction = 'horizontal'; - outStatus = 'process'; - index = 0; +export class NzStepComponent implements OnChanges { @ViewChild('processDotTemplate') processDotTemplate: TemplateRef; - customProcessTemplate: TemplateRef<{ $implicit: TemplateRef, status: string, index: number }>; @Input() + get nzTitle(): string | TemplateRef { return this._title; } set nzTitle(value: string | TemplateRef) { this.isTitleString = !(value instanceof TemplateRef); this._title = value; } - - get nzTitle(): string | TemplateRef { - return this._title; - } + isTitleString = true; + private _title: string | TemplateRef; @Input() + get nzIcon(): NgClassType | TemplateRef { return this._icon; } set nzIcon(value: NgClassType | TemplateRef) { + // TODO: old API compatibility should be abstracted. if (!(value instanceof TemplateRef)) { this.isIconString = true; if (typeof value === 'string') { @@ -60,54 +41,65 @@ export class NzStepComponent { } this._icon = value; } - - get nzIcon(): NgClassType | TemplateRef { - return this._icon; - } + oldAPIIcon = true; + isIconString = true; + private _icon: NgClassType | TemplateRef; @Input() + get nzStatus(): string { return this._status; } set nzStatus(status: string) { this._status = status; this.isCustomStatus = true; - this.updateClassMap(); - } - - get nzStatus(): string { - return this._status; } + isCustomStatus = false; + private _status = 'wait'; @Input() + get nzDescription(): string | TemplateRef { return this._description; } set nzDescription(value: string | TemplateRef) { this.isDescriptionString = !(value instanceof TemplateRef); this._description = value; } + isDescriptionString = true; + private _description: string | TemplateRef; - get nzDescription(): string | TemplateRef { - return this._description; - } - - get currentIndex(): number { - return this._currentIndex; - } + classString: string; + customProcessTemplate: TemplateRef<{ $implicit: TemplateRef, status: string, index: number }>; // Set by parent. + direction = 'horizontal'; + index = 0; + last = false; + outStatus = 'process'; + showProcessDot = false; + get currentIndex(): number { return this._currentIndex; } set currentIndex(current: number) { this._currentIndex = current; if (!this.isCustomStatus) { - if (current > this.index) { - this._status = 'finish'; - } else if (current === this.index) { - if (this.outStatus) { - this._status = this.outStatus; - } - } else { - this._status = 'wait'; - } + this._status = current > this.index + ? 'finish' : current === this.index + ? this.outStatus || '' : 'wait'; + } + } + private _currentIndex = 0; + + constructor(private cdr: ChangeDetectorRef) { } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.nzStatus || changes.nzIcon || changes.currentIndex) { + this.setClassString(); } - this.updateClassMap(); } - updateClassMap(): void { - const classMap = { + /** + * Sometimes status changes in `nz-steps` would affect `nz-step`'s behavior. + */ + detectChanges(): void { + this.setClassString(); + this.cdr.detectChanges(); + } + + private setClassString(): void { + this.classString = classMapToString({ [ 'ant-steps-item' ] : true, [ `ant-steps-item-wait` ] : this.nzStatus === 'wait', [ `ant-steps-item-process` ]: this.nzStatus === 'process', @@ -115,10 +107,6 @@ export class NzStepComponent { [ `ant-steps-item-error` ] : this.nzStatus === 'error', [ 'ant-steps-custom' ] : !!this.nzIcon, [ 'ant-steps-next-error' ] : (this.outStatus === 'error') && (this.currentIndex === this.index + 1) - }; - this.nzUpdateHostClassService.updateHostClass(this.el, classMap); - } - - constructor(private elementRef: ElementRef, private nzUpdateHostClassService: NzUpdateHostClassService) { + }); } } diff --git a/components/steps/nz-steps.component.html b/components/steps/nz-steps.component.html deleted file mode 100644 index d652bf05a73..00000000000 --- a/components/steps/nz-steps.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
\ No newline at end of file diff --git a/components/steps/nz-steps.component.ts b/components/steps/nz-steps.component.ts index 1df1af168b6..b1932dbc864 100644 --- a/components/steps/nz-steps.component.ts +++ b/components/steps/nz-steps.component.ts @@ -1,18 +1,20 @@ import { AfterContentInit, + ChangeDetectionStrategy, Component, ContentChildren, Input, - OnDestroy, - OnInit, + OnChanges, + OnDestroy, OnInit, QueryList, + SimpleChanges, TemplateRef } from '@angular/core'; - import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { NzSizeDSType } from '../core/types/size'; +import { ClassMap } from '../core/interface/interface'; +import { NzSizeDSType } from '../core/types/size'; import { toBoolean } from '../core/util/convert'; import { NzStepComponent } from './nz-step.component'; @@ -21,52 +23,24 @@ export type NzDirectionType = 'horizontal' | 'vertical'; export type NzStatusType = 'wait' | 'process' | 'finish' | 'error'; @Component({ + changeDetection : ChangeDetectionStrategy.OnPush, selector : 'nz-steps', preserveWhitespaces: false, - templateUrl : './nz-steps.component.html' + template : ` +
+ +
+ ` }) -export class NzStepsComponent implements OnInit, OnDestroy, AfterContentInit { - private _status: NzStatusType = 'process'; - private _current = 0; - private _size: NzSizeDSType = 'default'; - private _direction: NzDirectionType = 'horizontal'; - private _startIndex = 0; - private unsubscribe$ = new Subject(); - - stepsClassMap: object; - showProcessDot = false; - customProcessDotTemplate: TemplateRef<{ $implicit: TemplateRef, status: string, index: number }>; +export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterContentInit { @ContentChildren(NzStepComponent) steps: QueryList; - @Input() set nzSize(value: NzSizeDSType) { - this._size = value; - this.updateClassMap(); - } - - get nzSize(): NzSizeDSType { - return this._size; - } - - @Input() - set nzStartIndex(value: number) { - this._startIndex = value; - this.updateChildrenSteps(); - } - - get nzStartIndex(): number { - return this._startIndex; - } - - @Input() - set nzDirection(value: NzDirectionType) { - this._direction = value; - this.updateClassMap(); - this.updateChildrenSteps(); - } - - get nzDirection(): NzDirectionType { - return this._direction; - } + @Input() nzCurrent = 0; + @Input() nzDirection: NzDirectionType = 'horizontal'; + @Input() nzLabelPlacement: 'horizontal' | 'vertical' = 'horizontal'; + @Input() nzSize: NzSizeDSType = 'default'; + @Input() nzStartIndex = 0; + @Input() nzStatus: NzStatusType = 'process'; @Input() set nzProgressDot(value: boolean | TemplateRef<{ $implicit: TemplateRef, status: string, index: number }>) { @@ -77,42 +51,44 @@ export class NzStepsComponent implements OnInit, OnDestroy, AfterContentInit { this.showProcessDot = toBoolean(value); } this.updateChildrenSteps(); - this.updateClassMap(); } + showProcessDot = false; + customProcessDotTemplate: TemplateRef<{ $implicit: TemplateRef, status: string, index: number }>; - @Input() - set nzStatus(status: NzStatusType) { - this._status = status; - this.updateChildrenSteps(); - } + classMap: ClassMap; - get nzStatus(): NzStatusType { - return this._status; + private unsubscribe$ = new Subject(); + + ngOnChanges(changes: SimpleChanges): void { + if (changes.nzStartIndex || changes.nzDirection || changes.nzStatus || changes.nzCurrent) { + this.updateChildrenSteps(); + } + if (changes.nzDirection || changes.nzProgressDot || changes.nzLabelPlacement || changes.nzSize) { + this.setClassMap(); + } } - @Input() - set nzCurrent(current: number) { - this._current = current; + ngOnInit(): void { + this.setClassMap(); this.updateChildrenSteps(); } - get nzCurrent(): number { - return this._current; + ngOnDestroy(): void { + this.unsubscribe$.next(); + this.unsubscribe$.complete(); } - updateClassMap(): void { - this.stepsClassMap = { - [ `ant-steps-${this.nzDirection}` ]: true, - [ `ant-steps-label-horizontal` ] : this.nzDirection === 'horizontal', - [ `ant-steps-label-vertical` ] : this.showProcessDot && (this.nzDirection === 'horizontal'), - [ `ant-steps-dot` ] : this.showProcessDot, - [ 'ant-steps-small' ] : this.nzSize === 'small' - }; + ngAfterContentInit(): void { + this.updateChildrenSteps(); + if (this.steps) { + this.steps.changes.pipe(takeUntil(this.unsubscribe$)).subscribe(this.updateChildrenSteps); + } } - updateChildrenSteps = () => { + private updateChildrenSteps(): void { if (this.steps) { - this.steps.toArray().forEach((step, index, arr) => { + const length = this.steps.length; + this.steps.toArray().forEach((step, index) => { Promise.resolve().then(() => { step.outStatus = this.nzStatus; step.showProcessDot = this.showProcessDot; @@ -122,26 +98,20 @@ export class NzStepsComponent implements OnInit, OnDestroy, AfterContentInit { step.direction = this.nzDirection; step.index = index + this.nzStartIndex; step.currentIndex = this.nzCurrent; - step.last = arr.length === index + 1; - step.updateClassMap(); + step.last = length === index + 1; + step.detectChanges(); }); }); } } - ngOnInit(): void { - this.updateClassMap(); - } - - ngOnDestroy(): void { - this.unsubscribe$.next(); - this.unsubscribe$.complete(); - } - - ngAfterContentInit(): void { - this.updateChildrenSteps(); - if (this.steps) { - this.steps.changes.pipe(takeUntil(this.unsubscribe$)).subscribe(this.updateChildrenSteps); - } + private setClassMap(): void { + this.classMap = { + [ `ant-steps-${this.nzDirection}` ]: true, + [ `ant-steps-label-horizontal` ] : this.nzDirection === 'horizontal', + [ `ant-steps-label-vertical` ] : (this.showProcessDot || this.nzLabelPlacement === 'vertical') && this.nzDirection === 'horizontal', + [ `ant-steps-dot` ] : this.showProcessDot, + [ 'ant-steps-small' ] : this.nzSize === 'small' + }; } } diff --git a/components/steps/nz-steps.spec.ts b/components/steps/nz-steps.spec.ts index 6b2591f20bf..3f4df83da05 100644 --- a/components/steps/nz-steps.spec.ts +++ b/components/steps/nz-steps.spec.ts @@ -86,6 +86,12 @@ describe('steps', () => { fixture.detectChanges(); expect(outStep.nativeElement.firstElementChild.className).toBe('ant-steps ant-steps-vertical'); }); + it('should label placement display correct', () => { + fixture.detectChanges(); + testComponent.labelPlacement = 'vertical'; + fixture.detectChanges(); + expect(outStep.nativeElement.firstElementChild.classList).toContain('ant-steps-label-vertical'); + }); it('should status display correct', fakeAsync(() => { fixture.detectChanges(); tick(); @@ -241,7 +247,7 @@ describe('steps', () => { @Component({ selector: 'nz-test-outer-steps', template: ` - + @@ -256,6 +262,7 @@ export class NzTestOuterStepsComponent { @ViewChild('progressTemplate') progressTemplate: TemplateRef; current = 0; direction = 'horizontal'; + labelPlacement = 'horizontal'; size = 'default'; status = 'process'; progressDot = false;