Skip to content

Commit

Permalink
feat(module:steps): steps support circular progress bar (#6132)
Browse files Browse the repository at this point in the history
close #5684
  • Loading branch information
yangjunhan authored Jun 11, 2021
1 parent 7e2fba3 commit 466a093
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 5 deletions.
2 changes: 1 addition & 1 deletion components/progress/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions components/steps/demo/progress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
order: 12
title:
zh-CN: 带有进度的步骤
en-US: Steps with progress
---

## zh-CN

异步执行的步骤带有圆形进度条。

## en-US

Asynchronous steps with circular progress bar.


136 changes: 136 additions & 0 deletions components/steps/demo/progress.ts
Original file line number Diff line number Diff line change
@@ -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<number> {
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: `
<nz-steps [nzCurrent]="current">
<nz-step
*ngFor="let step of this.steps; trackBy: trackById"
[nzTitle]="step.title"
[nzDescription]="step.description"
[nzPercentage]="step.async ? step.percentage : null"
></nz-step>
</nz-steps>
<div class="steps-action">
<button nz-button nzType="default" (click)="pre()" *ngIf="current > 0">
<span>Previous</span>
</button>
<button nz-button nzType="default" (click)="next()" [nzLoading]="processing" *ngIf="current < 2">
<span>Next</span>
</button>
<button nz-button nzType="primary" (click)="done()" [nzLoading]="processing" *ngIf="current === 2">
<span>Done</span>
</button>
</div>
`,
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);
}
}
3 changes: 2 additions & 1 deletion components/steps/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>` | - |
| `[nzSubtitle]` | subTitle of the step | `string \| TemplateRef<void>` | - |
| `[nzDisabled]` | disable click | `boolean` | `false` |
| `[nzDisabled]` | disable click | `boolean` | `false` |
| `[nzPercentage]` | Progress percentage of the step in `process` status (only works on basic Steps) | `number` | - |
3 changes: 2 additions & 1 deletion components/steps/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>` | - |
| `[nzSubtitle]` | 子标题 | `string \| TemplateRef<void>` | - |
| `[nzDisabled]` | 禁用点击 | `boolean` | `false` |
| `[nzDisabled]` | 禁用点击 | `boolean` | `false` |
| `[nzPercentage]` | 当前状态为 `process` 的步骤所显示的进度条进度(只对基本类型的 `nz-steps` 生效) | `number` | - |
23 changes: 23 additions & 0 deletions components/steps/step.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -34,6 +35,15 @@ import { Subject } from 'rxjs';
<div class="ant-steps-item-tail" *ngIf="last !== true"></div>
<div class="ant-steps-item-icon">
<ng-template [ngIf]="!showProcessDot">
<div *ngIf="showProgress" class="ant-steps-progress-icon">
<nz-progress
[nzPercent]="nzPercentage"
nzType="circle"
[nzWidth]="40"
[nzFormat]="nullProcessFormat"
[nzStrokeWidth]="4"
></nz-progress>
</div>
<span class="ant-steps-icon" *ngIf="nzStatus === 'finish' && !nzIcon"><i nz-icon nzType="check"></i></span>
<span class="ant-steps-icon" *ngIf="nzStatus === 'error'"><i nz-icon nzType="close"></i></span>
<span class="ant-steps-icon" *ngIf="(nzStatus === 'process' || nzStatus === 'wait') && !nzIcon">
Expand Down Expand Up @@ -95,6 +105,7 @@ export class NzStepComponent implements OnDestroy {
@Input() nzSubtitle?: string | TemplateRef<void>;
@Input() nzDescription?: string | TemplateRef<void>;
@Input() @InputBoolean() nzDisabled = false;
@Input() nzPercentage: number | null = null;

@Input()
get nzStatus(): string {
Expand Down Expand Up @@ -134,6 +145,18 @@ export class NzStepComponent implements OnDestroy {
clickable = false;
click$ = new Subject<number>();

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;
}
Expand Down
23 changes: 22 additions & 1 deletion components/steps/steps.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ChangeDetectorRef,
Component,
ContentChildren,
ElementRef,
EventEmitter,
Input,
OnChanges,
Expand All @@ -17,6 +18,7 @@ import {
Optional,
Output,
QueryList,
Renderer2,
SimpleChanges,
TemplateRef,
ViewEncapsulation
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion components/steps/steps.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]
})
Expand Down

0 comments on commit 466a093

Please sign in to comment.