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..5b9f71e4e77 100644 --- a/components/steps/nz-step.component.html +++ b/components/steps/nz-step.component.html @@ -1,21 +1,9 @@ - - - - - -
- - - - - - - - {{ index + 1 }} - + + + {{ index + 1 }} @@ -30,15 +18,18 @@ - + +
- {{ nzTitle }} + {{ nzTitle }}
- {{ nzDescription }} + {{ 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..998b31f538e 100644 --- a/components/steps/nz-step.component.ts +++ b/components/steps/nz-step.component.ts @@ -1,124 +1,76 @@ -import { - Component, - ElementRef, - Input, - TemplateRef, - ViewChild -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; -import { NzUpdateHostClassService } from '../core/services/update-host-class.service'; import { NgClassType } from '../core/types/ng-class'; @Component({ + changeDetection : ChangeDetectionStrategy.OnPush, + encapsulation : ViewEncapsulation.None, selector : 'nz-step', - providers : [ NzUpdateHostClassService ], preserveWhitespaces: false, - templateUrl : './nz-step.component.html' + templateUrl : './nz-step.component.html', + host : { + '[class.ant-steps-item]' : 'true', + '[class.ant-steps-item-wait]' : 'nzStatus === "wait"', + '[class.ant-steps-item-process]': 'nzStatus === "process"', + '[class.ant-steps-item-finish]' : 'nzStatus === "finish"', + '[class.ant-steps-item-error]' : 'nzStatus === "error"', + '[class.ant-steps-custom]' : '!!nzIcon', + '[class.ant-steps-next-error]' : '(outStatus === "error") && (currentIndex === index + 1)' + } }) 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; @ViewChild('processDotTemplate') processDotTemplate: TemplateRef; - customProcessTemplate: TemplateRef<{ $implicit: TemplateRef, status: string, index: number }>; - @Input() - set nzTitle(value: string | TemplateRef) { - this.isTitleString = !(value instanceof TemplateRef); - this._title = value; - } + @Input() nzTitle: string | TemplateRef; + @Input() nzDescription: string | TemplateRef; - get nzTitle(): string | TemplateRef { - return this._title; + @Input() + get nzStatus(): string { return this._status; } + set nzStatus(status: string) { + this._status = status; + this.isCustomStatus = true; } + isCustomStatus = false; + private _status = 'wait'; @Input() + get nzIcon(): NgClassType | TemplateRef { return this._icon; } set nzIcon(value: NgClassType | TemplateRef) { if (!(value instanceof TemplateRef)) { this.isIconString = true; - if (typeof value === 'string') { - const str = value as string; - this.oldAPIIcon = str.indexOf('anticon') > -1; - } else { - this.oldAPIIcon = true; - } + this.oldAPIIcon = typeof value === 'string' && value.indexOf('anticon') > -1; } else { this.isIconString = false; } this._icon = value; } + oldAPIIcon = true; + isIconString = true; + private _icon: NgClassType | TemplateRef; - get nzIcon(): NgClassType | TemplateRef { - return this._icon; - } - - @Input() - set nzStatus(status: string) { - this._status = status; - this.isCustomStatus = true; - this.updateClassMap(); - } - - get nzStatus(): string { - return this._status; - } - - @Input() - set nzDescription(value: string | TemplateRef) { - this.isDescriptionString = !(value instanceof TemplateRef); - this._description = value; - } - - get nzDescription(): string | TemplateRef { - return this._description; - } - - get currentIndex(): number { - return this._currentIndex; - } + 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'; } - this.updateClassMap(); } + private _currentIndex = 0; - updateClassMap(): void { - const classMap = { - [ 'ant-steps-item' ] : true, - [ `ant-steps-item-wait` ] : this.nzStatus === 'wait', - [ `ant-steps-item-process` ]: this.nzStatus === 'process', - [ `ant-steps-item-finish` ] : this.nzStatus === 'finish', - [ `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 cdr: ChangeDetectorRef) {} - constructor(private elementRef: ElementRef, private nzUpdateHostClassService: NzUpdateHostClassService) { + detectChanges(): void { + this.cdr.detectChanges(); } } diff --git a/components/steps/nz-steps.component.html b/components/steps/nz-steps.component.html index d652bf05a73..9b72ba3dc08 100644 --- a/components/steps/nz-steps.component.html +++ b/components/steps/nz-steps.component.html @@ -1,3 +1,3 @@ -
+
\ No newline at end of file diff --git a/components/steps/nz-steps.component.ts b/components/steps/nz-steps.component.ts index 1df1af168b6..2a77d55e1b7 100644 --- a/components/steps/nz-steps.component.ts +++ b/components/steps/nz-steps.component.ts @@ -1,18 +1,22 @@ import { AfterContentInit, + ChangeDetectionStrategy, Component, ContentChildren, Input, + OnChanges, OnDestroy, OnInit, QueryList, - TemplateRef + SimpleChanges, + TemplateRef, + ViewEncapsulation } 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 +25,21 @@ export type NzDirectionType = 'horizontal' | 'vertical'; export type NzStatusType = 'wait' | 'process' | 'finish' | 'error'; @Component({ - selector : 'nz-steps', + changeDetection : ChangeDetectionStrategy.OnPush, + encapsulation : ViewEncapsulation.None, preserveWhitespaces: false, + selector : 'nz-steps', templateUrl : './nz-steps.component.html' }) -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 +50,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; + + private destroy$ = new Subject(); - get nzStatus(): NzStatusType { - return this._status; + 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.destroy$.next(); + this.destroy$.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.destroy$)).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 +97,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.module.ts b/components/steps/nz-steps.module.ts index 044f4f6bf19..25319376f2e 100644 --- a/components/steps/nz-steps.module.ts +++ b/components/steps/nz-steps.module.ts @@ -1,12 +1,14 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { NzAddOnModule } from '../core/addon/addon.module'; import { NzIconModule } from '../icon/nz-icon.module'; + import { NzStepComponent } from './nz-step.component'; import { NzStepsComponent } from './nz-steps.component'; @NgModule({ - imports : [ CommonModule, NzIconModule ], + imports : [ CommonModule, NzIconModule, NzAddOnModule ], exports : [ NzStepsComponent, NzStepComponent ], declarations: [ NzStepsComponent, NzStepComponent ] }) diff --git a/components/steps/nz-steps.spec.ts b/components/steps/nz-steps.spec.ts index 6b2591f20bf..781d741df30 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(); @@ -172,9 +178,9 @@ describe('steps', () => { expect(innerSteps[ 2 ].nativeElement.className).toBe('ant-steps-item ant-steps-item-process ant-steps-custom'); testComponent.status = 'wait'; fixture.detectChanges(); - expect(innerSteps[ 0 ].nativeElement.className).toBe('ant-steps-item ant-steps-item-wait ant-steps-custom'); - expect(innerSteps[ 1 ].nativeElement.className).toBe('ant-steps-item ant-steps-item-wait ant-steps-custom'); - expect(innerSteps[ 2 ].nativeElement.className).toBe('ant-steps-item ant-steps-item-wait ant-steps-custom'); + expect(innerSteps[ 0 ].nativeElement.className).toBe('ant-steps-item ant-steps-custom ant-steps-item-wait'); + expect(innerSteps[ 1 ].nativeElement.className).toBe('ant-steps-item ant-steps-custom ant-steps-item-wait'); + expect(innerSteps[ 2 ].nativeElement.className).toBe('ant-steps-item ant-steps-custom ant-steps-item-wait'); }); it('should title display correct', () => { fixture.detectChanges(); @@ -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;