diff --git a/components/progress/typings.ts b/components/progress/typings.ts index 985718b50d3..27f75c0d076 100644 --- a/components/progress/typings.ts +++ b/components/progress/typings.ts @@ -27,7 +27,7 @@ export type NzProgressColorGradient = { direction?: string } & (NzProgressGradie export type NzProgressStrokeColorType = string | NzProgressColorGradient; -export type NzProgressFormatter = ((percent: number) => string) | TemplateRef<{ $implicit: number }>; +export type NzProgressFormatter = ((percent: number) => string | null) | TemplateRef<{ $implicit: number }>; export interface NzProgressCirclePath { stroke: string | null; diff --git a/components/steps/demo/progress.md b/components/steps/demo/progress.md new file mode 100644 index 00000000000..a0bb0275978 --- /dev/null +++ b/components/steps/demo/progress.md @@ -0,0 +1,16 @@ +--- +order: 12 +title: + zh-CN: 带有进度的步骤 + en-US: Steps with progress +--- + +## zh-CN + +异步执行的步骤带有圆形进度条。 + +## en-US + +Asynchronous steps with circular progress bar. + + diff --git a/components/steps/demo/progress.ts b/components/steps/demo/progress.ts new file mode 100644 index 00000000000..4d69c36cb61 --- /dev/null +++ b/components/steps/demo/progress.ts @@ -0,0 +1,136 @@ +import { Component, OnDestroy } from '@angular/core'; +import { merge, Observable, timer } from 'rxjs'; +import { delay, finalize, map, scan } from 'rxjs/operators'; + +interface SyncStep { + id: number; + title: string; + description: string; + async: false; + percentage: null; +} + +interface AsyncStep { + id: number; + title: string; + description: string; + async: true; + percentage: number; +} + +type Step = SyncStep | AsyncStep; + +function mockAsyncStep(): Observable { + const subStep1 = timer(600).pipe(map(() => 25)); + const subStep2 = subStep1.pipe(delay(600)); + const subStep3 = subStep2.pipe(delay(600)); + const subStep4 = subStep3.pipe(delay(600)); + return merge(subStep1, subStep2, subStep3, subStep4).pipe(scan((a, b) => a + b)); +} + +@Component({ + selector: 'nz-demo-steps-progress', + template: ` + + + +
+ + + +
+ `, + styles: [ + ` + .steps-action { + margin-top: 36px; + } + + button { + margin-right: 8px; + } + ` + ] +}) +export class NzDemoStepsProgressComponent implements OnDestroy { + steps: Step[] = [ + { + id: 1, + title: `Step 1`, + description: `This step is synchronous.`, + async: false, + percentage: null + }, + { + id: 2, + title: `Step 2`, + description: `This step is asynchronous.`, + async: true, + percentage: 0 + }, + { + id: 3, + title: `Step 3`, + description: `This step is asynchronous.`, + async: true, + percentage: 0 + } + ]; + intervalId = -1; + current = 0; + processing = false; + + pre(): void { + this.current -= 1; + } + + next(): void { + this.loadingAndStep(); + } + + done(): void { + this.loadingAndStep(); + console.log('done'); + } + + trackById(_: number, item: Step): number { + return item.id; + } + + loadingAndStep(): void { + if (this.current < this.steps.length) { + const step = this.steps[this.current]; + if (step.async) { + this.processing = true; + mockAsyncStep() + .pipe( + finalize(() => { + step.percentage = 0; + this.processing = false; + this.current += 1; + }) + ) + .subscribe(p => { + step.percentage = p; + }); + } else { + this.current += 1; + } + } + } + + ngOnDestroy(): void { + clearInterval(this.intervalId); + } +} diff --git a/components/steps/doc/index.en-US.md b/components/steps/doc/index.en-US.md index f2b1f9ee35a..dd16848d191 100755 --- a/components/steps/doc/index.en-US.md +++ b/components/steps/doc/index.en-US.md @@ -53,4 +53,5 @@ A single step in the step bar. | `[nzStatus]` | to specify the status. It will be automatically set by `nzCurrent` of `nz-steps` if not configured. Optional values are: `wait` `process` `finish` `error` | `'wait' \| 'process' \| 'finish' \| 'error'` | `'wait'` | | `[nzTitle]` | title of the step | `string \| TemplateRef` | - | | `[nzSubtitle]` | subTitle of the step | `string \| TemplateRef` | - | -| `[nzDisabled]` | disable click | `boolean` | `false` | \ No newline at end of file +| `[nzDisabled]` | disable click | `boolean` | `false` | +| `[nzPercentage]` | Progress percentage of the step in `process` status (only works on basic Steps) | `number` | - | \ No newline at end of file diff --git a/components/steps/doc/index.zh-CN.md b/components/steps/doc/index.zh-CN.md index c2803565b63..5fa8b230bb3 100755 --- a/components/steps/doc/index.zh-CN.md +++ b/components/steps/doc/index.zh-CN.md @@ -54,4 +54,5 @@ import { NzStepsModule } from 'ng-zorro-antd/steps'; | `[nzStatus]` | 指定状态。当不配置该属性时,会使用 `nz-steps` 的 `nzCurrent` 来自动指定状态。可选:`wait` `process` `finish` `error` | `'wait' \| 'process' \| 'finish' \| 'error'` | `'wait'` | | `[nzTitle]` | 标题 | `string \| TemplateRef` | - | | `[nzSubtitle]` | 子标题 | `string \| TemplateRef` | - | -| `[nzDisabled]` | 禁用点击 | `boolean` | `false` | \ No newline at end of file +| `[nzDisabled]` | 禁用点击 | `boolean` | `false` | +| `[nzPercentage]` | 当前状态为 `process` 的步骤所显示的进度条进度(只对基本类型的 `nz-steps` 生效) | `number` | - | \ No newline at end of file diff --git a/components/steps/step.component.ts b/components/steps/step.component.ts index 3ffdac28eaa..bd8047f325b 100644 --- a/components/steps/step.component.ts +++ b/components/steps/step.component.ts @@ -15,6 +15,7 @@ import { } from '@angular/core'; import { BooleanInput, NgClassType } from 'ng-zorro-antd/core/types'; import { InputBoolean } from 'ng-zorro-antd/core/util'; +import { NzProgressFormatter } from 'ng-zorro-antd/progress'; import { Subject } from 'rxjs'; @@ -34,6 +35,15 @@ import { Subject } from 'rxjs';
+
+ +
@@ -95,6 +105,7 @@ export class NzStepComponent implements OnDestroy { @Input() nzSubtitle?: string | TemplateRef; @Input() nzDescription?: string | TemplateRef; @Input() @InputBoolean() nzDisabled = false; + @Input() nzPercentage: number | null = null; @Input() get nzStatus(): string { @@ -134,6 +145,18 @@ export class NzStepComponent implements OnDestroy { clickable = false; click$ = new Subject(); + readonly nullProcessFormat: NzProgressFormatter = () => null; + + get showProgress(): boolean { + return ( + this.nzPercentage !== null && + !this.nzIcon && + this.nzStatus === 'process' && + this.nzPercentage >= 0 && + this.nzPercentage <= 100 + ); + } + get currentIndex(): number { return this._currentIndex; } diff --git a/components/steps/steps.component.ts b/components/steps/steps.component.ts index 99d529b4cc7..25c50fe34b0 100644 --- a/components/steps/steps.component.ts +++ b/components/steps/steps.component.ts @@ -9,6 +9,7 @@ import { ChangeDetectorRef, Component, ContentChildren, + ElementRef, EventEmitter, Input, OnChanges, @@ -17,6 +18,7 @@ import { Optional, Output, QueryList, + Renderer2, SimpleChanges, TemplateRef, ViewEncapsulation @@ -79,7 +81,12 @@ export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterCont classMap: NgClassType = {}; dir: Direction = 'ltr'; - constructor(private cdr: ChangeDetectorRef, @Optional() private directionality: Directionality) { + constructor( + private elementRef: ElementRef, + private renderer: Renderer2, + private cdr: ChangeDetectorRef, + @Optional() private directionality: Directionality + ) { this.setClassMap(); } @@ -115,11 +122,25 @@ export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterCont ngAfterContentInit(): void { if (this.steps) { this.steps.changes.pipe(startWith(null), takeUntil(this.destroy$)).subscribe(() => { + this.updateHostProgressClass(); this.updateChildrenSteps(); }); } } + private updateHostProgressClass(): void { + if (this.steps && !this.showProcessDot) { + const hasPercent = !!this.steps.toArray().find(step => step.nzPercentage !== null); + const className = 'ant-steps-with-progress'; + const hasClass = this.elementRef.nativeElement.classList.contains(className); + if (hasPercent && !hasClass) { + this.renderer.addClass(this.elementRef.nativeElement, className); + } else if (!hasPercent && hasClass) { + this.renderer.removeClass(this.elementRef.nativeElement, className); + } + } + } + private updateChildrenSteps(): void { if (this.steps) { const length = this.steps.length; diff --git a/components/steps/steps.module.ts b/components/steps/steps.module.ts index e8d4643c868..e4581eb64f5 100644 --- a/components/steps/steps.module.ts +++ b/components/steps/steps.module.ts @@ -9,12 +9,13 @@ import { NgModule } from '@angular/core'; import { NzOutletModule } from 'ng-zorro-antd/core/outlet'; import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NzProgressModule } from 'ng-zorro-antd/progress'; import { NzStepComponent } from './step.component'; import { NzStepsComponent } from './steps.component'; @NgModule({ - imports: [BidiModule, CommonModule, NzIconModule, NzOutletModule], + imports: [BidiModule, CommonModule, NzIconModule, NzOutletModule, NzProgressModule], exports: [NzStepsComponent, NzStepComponent], declarations: [NzStepsComponent, NzStepComponent] })