@libs-ui/components-process-bar-steps 0.2.356-9 → 0.2.357-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -1,39 +1,44 @@
|
|
|
1
1
|
# @libs-ui/components-process-bar-steps
|
|
2
2
|
|
|
3
|
-
> Component hiển thị
|
|
3
|
+
> Component hiển thị thanh tiến trình dạng bước (stepper) với hỗ trợ Simple Mode và Advanced Mode.
|
|
4
4
|
|
|
5
5
|
## Giới thiệu
|
|
6
6
|
|
|
7
|
-
`LibsUiComponentsProcessBarStepsComponent` là một standalone Angular component dùng để hiển thị các
|
|
7
|
+
`LibsUiComponentsProcessBarStepsComponent` là một standalone Angular component dùng để hiển thị các bước tiến trình (stepper/process bar). Component hỗ trợ hai chế độ cấu hình: **Simple Mode** cho phép cấu hình nhanh bằng tổng số bước và bước hiện tại, và **Advanced Mode** cho phép tùy chỉnh chi tiết từng bước bao gồm label, màu sắc, và trạng thái riêng biệt. Toàn bộ state được quản lý qua Angular Signals và tích hợp OnPush để tối ưu hiệu năng.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Tính năng
|
|
10
10
|
|
|
11
|
-
- ✅ **Simple Mode**: Cấu hình nhanh
|
|
12
|
-
- ✅ **Advanced Mode**: Tùy chỉnh chi tiết từng bước (
|
|
13
|
-
- ✅ **
|
|
14
|
-
- ✅ **
|
|
15
|
-
- ✅ **
|
|
11
|
+
- ✅ **Simple Mode**: Cấu hình nhanh với `totalStep`, `currentStep` và màu sắc chung
|
|
12
|
+
- ✅ **Advanced Mode**: Tùy chỉnh chi tiết từng bước (label, màu hoàn thành, màu đang xử lý, custom class)
|
|
13
|
+
- ✅ **Label Position**: Hỗ trợ 3 vị trí label — `top`, `bottom`, `inner`
|
|
14
|
+
- ✅ **Label Alignment**: Căn chỉnh `start`, `center`, `end`
|
|
15
|
+
- ✅ **Customizable Dimensions**: Tùy chỉnh `width`, `height`, `borderRadius` cho từng bước
|
|
16
|
+
- ✅ **Global Default Token**: Inject token để cấu hình default toàn app qua `PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT`
|
|
17
|
+
- ✅ **Reactive Signals**: Toàn bộ state đều là Signal — cập nhật realtime khi signal thay đổi
|
|
18
|
+
- ✅ **OnPush Change Detection**: Tối ưu hiệu năng render
|
|
16
19
|
|
|
17
20
|
## Khi nào sử dụng
|
|
18
21
|
|
|
19
|
-
- Hiển thị trạng thái đơn hàng (Order Tracking)
|
|
20
|
-
- Hiển thị các bước trong quy trình đăng k
|
|
21
|
-
- Hiển thị lộ trình học tập hoặc
|
|
22
|
+
- Hiển thị trạng thái đơn hàng (Order Tracking): Đặt hàng → Xử lý → Đã giao
|
|
23
|
+
- Hiển thị các bước trong quy trình đăng ký, onboarding hoặc wizard form
|
|
24
|
+
- Hiển thị lộ trình học tập, milestones hoặc checklist tiến trình
|
|
25
|
+
- Bất kỳ UI nào cần visualize tiến độ theo từng giai đoạn
|
|
22
26
|
|
|
23
27
|
## Cài đặt
|
|
24
28
|
|
|
25
29
|
```bash
|
|
26
|
-
# npm
|
|
27
30
|
npm install @libs-ui/components-process-bar-steps
|
|
28
|
-
|
|
29
|
-
# yarn
|
|
30
|
-
yarn add @libs-ui/components-process-bar-steps
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
## Import
|
|
34
34
|
|
|
35
35
|
```typescript
|
|
36
36
|
import { LibsUiComponentsProcessBarStepsComponent } from '@libs-ui/components-process-bar-steps';
|
|
37
|
+
import {
|
|
38
|
+
IProcessBarStepConfigInterface,
|
|
39
|
+
IProcessBarStepSimpleConfigInterface,
|
|
40
|
+
IProcessBarStepAdvancedConfigInterface,
|
|
41
|
+
} from '@libs-ui/components-process-bar-steps';
|
|
37
42
|
|
|
38
43
|
@Component({
|
|
39
44
|
standalone: true,
|
|
@@ -43,91 +48,276 @@ import { LibsUiComponentsProcessBarStepsComponent } from '@libs-ui/components-pr
|
|
|
43
48
|
export class YourComponent {}
|
|
44
49
|
```
|
|
45
50
|
|
|
46
|
-
## Ví dụ
|
|
51
|
+
## Ví dụ sử dụng
|
|
47
52
|
|
|
48
|
-
### Simple
|
|
53
|
+
### Ví dụ 1 — Simple Mode (cấu hình nhanh)
|
|
49
54
|
|
|
50
55
|
```html
|
|
51
|
-
|
|
56
|
+
<!-- your-component.component.html -->
|
|
57
|
+
<libs_ui-components-process_bar-steps [config]="configSimple()" />
|
|
52
58
|
```
|
|
53
59
|
|
|
54
60
|
```typescript
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
// your-component.component.ts
|
|
62
|
+
import { Component, signal } from '@angular/core';
|
|
63
|
+
import {
|
|
64
|
+
LibsUiComponentsProcessBarStepsComponent,
|
|
65
|
+
IProcessBarStepConfigInterface,
|
|
66
|
+
IProcessBarStepSimpleConfigInterface,
|
|
67
|
+
} from '@libs-ui/components-process-bar-steps';
|
|
68
|
+
|
|
69
|
+
@Component({
|
|
70
|
+
standalone: true,
|
|
71
|
+
imports: [LibsUiComponentsProcessBarStepsComponent],
|
|
72
|
+
templateUrl: './your-component.component.html',
|
|
73
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
74
|
+
})
|
|
75
|
+
export class YourComponent {
|
|
76
|
+
private readonly simpleConfigSignal = signal<IProcessBarStepSimpleConfigInterface>({
|
|
57
77
|
totalStep: 4,
|
|
58
78
|
currentStep: 2,
|
|
79
|
+
completedStep: 1,
|
|
59
80
|
currentStepColor: '#3B82F6',
|
|
60
|
-
stepCompletedColor: '#10B981'
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
|
|
81
|
+
stepCompletedColor: '#10B981',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
protected readonly configSimple = signal<IProcessBarStepConfigInterface>({
|
|
85
|
+
simpleConfig: this.simpleConfigSignal,
|
|
86
|
+
widthStep: '60px',
|
|
87
|
+
heightStep: '8px',
|
|
88
|
+
radiusStep: 4,
|
|
89
|
+
backgroundColorStep: '#E5E7EB',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
64
92
|
```
|
|
65
93
|
|
|
66
|
-
### Advanced
|
|
94
|
+
### Ví dụ 2 — Advanced Mode (tùy chỉnh từng bước, có label)
|
|
95
|
+
|
|
96
|
+
```html
|
|
97
|
+
<!-- your-component.component.html -->
|
|
98
|
+
<libs_ui-components-process_bar-steps [config]="configAdvanced()" />
|
|
99
|
+
```
|
|
67
100
|
|
|
68
101
|
```typescript
|
|
69
|
-
|
|
70
|
-
|
|
102
|
+
// your-component.component.ts
|
|
103
|
+
import { Component, signal } from '@angular/core';
|
|
104
|
+
import {
|
|
105
|
+
LibsUiComponentsProcessBarStepsComponent,
|
|
106
|
+
IProcessBarStepConfigInterface,
|
|
107
|
+
IProcessBarStepAdvancedConfigInterface,
|
|
108
|
+
} from '@libs-ui/components-process-bar-steps';
|
|
71
109
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
110
|
+
@Component({
|
|
111
|
+
standalone: true,
|
|
112
|
+
imports: [LibsUiComponentsProcessBarStepsComponent],
|
|
113
|
+
templateUrl: './your-component.component.html',
|
|
114
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
115
|
+
})
|
|
116
|
+
export class YourComponent {
|
|
117
|
+
private readonly step1 = signal<IProcessBarStepAdvancedConfigInterface>({
|
|
118
|
+
label: 'Đặt hàng',
|
|
119
|
+
processCompleted: true,
|
|
120
|
+
processCompletedColor: '#10B981',
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
private readonly step2 = signal<IProcessBarStepAdvancedConfigInterface>({
|
|
124
|
+
label: 'Đang xử lý',
|
|
125
|
+
isCurrentProcess: true,
|
|
126
|
+
currentProcessColor: '#3B82F6',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
private readonly step3 = signal<IProcessBarStepAdvancedConfigInterface>({
|
|
130
|
+
label: 'Đã giao',
|
|
131
|
+
processCompleted: false,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
private readonly step4 = signal<IProcessBarStepAdvancedConfigInterface>({
|
|
135
|
+
label: 'Hoàn thành',
|
|
136
|
+
processCompleted: false,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
protected readonly configAdvanced = signal<IProcessBarStepConfigInterface>({
|
|
140
|
+
advancedConfig: signal([this.step1, this.step2, this.step3, this.step4]),
|
|
141
|
+
positionLabel: 'bottom',
|
|
142
|
+
justifyLabel: 'center',
|
|
143
|
+
widthStep: '120px',
|
|
144
|
+
heightStep: '12px',
|
|
145
|
+
radiusStep: 6,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Cập nhật trạng thái khi sự kiện xảy ra
|
|
149
|
+
protected handlerMarkShipped(): void {
|
|
150
|
+
this.step3.update(s => ({ ...s, processCompleted: true, processCompletedColor: '#10B981' }));
|
|
151
|
+
this.step4.update(s => ({ ...s, isCurrentProcess: true, currentProcessColor: '#3B82F6' }));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Ví dụ 3 — Reactive với computed (tự động cập nhật)
|
|
157
|
+
|
|
158
|
+
```html
|
|
159
|
+
<!-- your-component.component.html -->
|
|
160
|
+
<libs_ui-components-process_bar-steps [config]="configInteractive()" />
|
|
76
161
|
```
|
|
77
162
|
|
|
78
|
-
|
|
163
|
+
```typescript
|
|
164
|
+
// your-component.component.ts
|
|
165
|
+
import { Component, computed, signal, WritableSignal, effect } from '@angular/core';
|
|
166
|
+
import {
|
|
167
|
+
LibsUiComponentsProcessBarStepsComponent,
|
|
168
|
+
IProcessBarStepConfigInterface,
|
|
169
|
+
IProcessBarStepSimpleConfigInterface,
|
|
170
|
+
} from '@libs-ui/components-process-bar-steps';
|
|
171
|
+
|
|
172
|
+
@Component({
|
|
173
|
+
standalone: true,
|
|
174
|
+
imports: [LibsUiComponentsProcessBarStepsComponent],
|
|
175
|
+
templateUrl: './your-component.component.html',
|
|
176
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
177
|
+
})
|
|
178
|
+
export class YourComponent {
|
|
179
|
+
protected currentStep = signal(1);
|
|
180
|
+
protected totalSteps = signal(5);
|
|
181
|
+
|
|
182
|
+
private readonly simpleConfigValues = computed<IProcessBarStepSimpleConfigInterface>(() => ({
|
|
183
|
+
totalStep: this.totalSteps(),
|
|
184
|
+
currentStep: this.currentStep(),
|
|
185
|
+
completedStep: this.currentStep() - 1,
|
|
186
|
+
currentStepColor: '#8B5CF6',
|
|
187
|
+
stepCompletedColor: '#F59E0B',
|
|
188
|
+
}));
|
|
189
|
+
|
|
190
|
+
// WritableSignal wrapper bắt buộc vì IProcessBarStepConfigInterface yêu cầu WritableSignal
|
|
191
|
+
private readonly simpleConfigSignal: WritableSignal<IProcessBarStepSimpleConfigInterface> =
|
|
192
|
+
signal(this.simpleConfigValues());
|
|
193
|
+
|
|
194
|
+
protected readonly configInteractive = computed<IProcessBarStepConfigInterface>(() => ({
|
|
195
|
+
simpleConfig: this.simpleConfigSignal,
|
|
196
|
+
positionLabel: 'top',
|
|
197
|
+
justifyLabel: 'center',
|
|
198
|
+
widthStep: '80px',
|
|
199
|
+
heightStep: '10px',
|
|
200
|
+
radiusStep: 5,
|
|
201
|
+
}));
|
|
202
|
+
|
|
203
|
+
constructor() {
|
|
204
|
+
// Đồng bộ computed → writableSignal để truyền vào config
|
|
205
|
+
effect(() => {
|
|
206
|
+
this.simpleConfigSignal.set(this.simpleConfigValues());
|
|
207
|
+
}, { allowSignalWrites: true });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
protected handlerNextStep(): void {
|
|
211
|
+
this.currentStep.update(v => Math.min(v + 1, this.totalSteps()));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
protected handlerPrevStep(): void {
|
|
215
|
+
this.currentStep.update(v => Math.max(v - 1, 1));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Ví dụ 4 — Cấu hình Global Default Token (toàn app)
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// app.config.ts hoặc providers của module root
|
|
224
|
+
import { ApplicationConfig } from '@angular/core';
|
|
225
|
+
import { PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT } from '@libs-ui/utils';
|
|
226
|
+
import { IProcessBarStepConfigInterface } from '@libs-ui/components-process-bar-steps';
|
|
227
|
+
|
|
228
|
+
export const appConfig: ApplicationConfig = {
|
|
229
|
+
providers: [
|
|
230
|
+
{
|
|
231
|
+
provide: PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT,
|
|
232
|
+
useValue: {
|
|
233
|
+
widthStep: '80px',
|
|
234
|
+
heightStep: '12px',
|
|
235
|
+
backgroundColorStep: '#F3F4F6',
|
|
236
|
+
radiusStep: 20,
|
|
237
|
+
} satisfies Partial<IProcessBarStepConfigInterface>,
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
};
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## @Input()
|
|
244
|
+
|
|
245
|
+
| Input | Type | Default | Mô tả | Ví dụ |
|
|
246
|
+
|---|---|---|---|---|
|
|
247
|
+
| `[config]` | `IProcessBarStepConfigInterface` | **Bắt buộc** | Object cấu hình toàn bộ process bar — bao gồm kích thước, màu sắc, vị trí label và dữ liệu bước | `[config]="configSimple()"` |
|
|
248
|
+
|
|
249
|
+
## Types & Interfaces
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import {
|
|
253
|
+
IProcessBarStepConfigInterface,
|
|
254
|
+
IProcessBarStepSimpleConfigInterface,
|
|
255
|
+
IProcessBarStepAdvancedConfigInterface,
|
|
256
|
+
} from '@libs-ui/components-process-bar-steps';
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### IProcessBarStepConfigInterface
|
|
260
|
+
|
|
261
|
+
Config chính truyền vào `[config]`. Cần chọn **một trong hai** chế độ: `simpleConfig` hoặc `advancedConfig`.
|
|
262
|
+
|
|
263
|
+
| Property | Type | Default | Mô tả | Ví dụ |
|
|
264
|
+
|---|---|---|---|---|
|
|
265
|
+
| `simpleConfig` | `WritableSignal<IProcessBarStepSimpleConfigInterface>` | `undefined` | Cấu hình Simple Mode — tự động sinh các bước từ `totalStep` | `simpleConfig: signal({ totalStep: 4, currentStep: 2, ... })` |
|
|
266
|
+
| `advancedConfig` | `WritableSignal<Array<WritableSignal<IProcessBarStepAdvancedConfigInterface>>>` | `undefined` | Cấu hình Advanced Mode — mảng signal cho từng bước | `advancedConfig: signal([step1, step2, step3])` |
|
|
267
|
+
| `positionLabel` | `'top' \| 'bottom' \| 'inner'` | `'top'` | Vị trí hiển thị label của từng bước | `positionLabel: 'bottom'` |
|
|
268
|
+
| `justifyLabel` | `'start' \| 'center' \| 'end'` | `'center'` | Căn chỉnh ngang của label và nội dung bên trong bước | `justifyLabel: 'start'` |
|
|
269
|
+
| `widthStep` | `string` | `'80px'` | Chiều rộng mỗi thanh bước | `widthStep: '60px'` |
|
|
270
|
+
| `heightStep` | `string` | `'12px'` | Chiều cao mỗi thanh bước | `heightStep: '8px'` |
|
|
271
|
+
| `backgroundColorStep` | `string` | `'#F8F9FA'` | Màu nền mặc định cho các bước chưa active và chưa hoàn thành | `backgroundColorStep: '#E5E7EB'` |
|
|
272
|
+
| `radiusStep` | `number` | `20` | Border radius (đơn vị px) — `20` tạo hình pill đầy đủ | `radiusStep: 4` |
|
|
273
|
+
| `classInclude` | `string` | `''` | Class CSS bổ sung cho thẻ container bao ngoài | `classInclude: 'my-[16px]'` |
|
|
274
|
+
|
|
275
|
+
### IProcessBarStepSimpleConfigInterface
|
|
276
|
+
|
|
277
|
+
Dùng trong `simpleConfig` — component tự động tạo các bước dựa trên config này.
|
|
278
|
+
|
|
279
|
+
| Property | Type | Default | Mô tả | Ví dụ |
|
|
280
|
+
|---|---|---|---|---|
|
|
281
|
+
| `totalStep` | `number` | **Bắt buộc** | Tổng số bước cần hiển thị | `totalStep: 5` |
|
|
282
|
+
| `currentStep` | `number` | **Bắt buộc** | Index bước hiện tại (bắt đầu từ 1) | `currentStep: 3` |
|
|
283
|
+
| `completedStep` | `number` | `= currentStep` | Số bước đã hoàn thành (index <= completedStep được tô màu completed). Mặc định bằng `currentStep` nếu không set | `completedStep: 2` |
|
|
284
|
+
| `currentStepColor` | `string` | **Bắt buộc** | Màu của thanh bước đang ở vị trí `currentStep` | `currentStepColor: '#3B82F6'` |
|
|
285
|
+
| `stepCompletedColor` | `string` | **Bắt buộc** | Màu của các thanh bước đã hoàn thành (`index <= completedStep`) | `stepCompletedColor: '#10B981'` |
|
|
286
|
+
|
|
287
|
+
### IProcessBarStepAdvancedConfigInterface
|
|
79
288
|
|
|
80
|
-
|
|
289
|
+
Dùng trong `advancedConfig` — mỗi bước là một `WritableSignal<IProcessBarStepAdvancedConfigInterface>`.
|
|
81
290
|
|
|
82
|
-
|
|
291
|
+
| Property | Type | Default | Mô tả | Ví dụ |
|
|
292
|
+
|---|---|---|---|---|
|
|
293
|
+
| `processCompleted` | `boolean` | `false` | Bước đã hoàn thành — áp dụng `processCompletedColor` | `processCompleted: true` |
|
|
294
|
+
| `processCompletedColor` | `string` | `'#33DA8A'` | Màu nền khi bước đã hoàn thành | `processCompletedColor: '#10B981'` |
|
|
295
|
+
| `isCurrentProcess` | `boolean` | `false` | Bước đang được xử lý — áp dụng `currentProcessColor` | `isCurrentProcess: true` |
|
|
296
|
+
| `currentProcessColor` | `string` | `'#4E8CF7'` | Màu nền khi bước đang được xử lý | `currentProcessColor: '#3B82F6'` |
|
|
297
|
+
| `label` | `string` | `undefined` | Text label hiển thị theo `positionLabel` | `label: 'Đặt hàng'` |
|
|
298
|
+
| `classLabel` | `string` | `''` | Class CSS bổ sung cho thẻ label | `classLabel: 'text-green-600'` |
|
|
299
|
+
| `classIncludeStep` | `string` | `''` | Class CSS bổ sung cho thanh bước (step bar) | `classIncludeStep: 'shadow-sm'` |
|
|
300
|
+
| `classIncludeContainer` | `string` | `''` | Class CSS bổ sung cho container bao bọc bước (step + label) | `classIncludeContainer: 'px-[4px]'` |
|
|
83
301
|
|
|
84
|
-
|
|
85
|
-
| ---------- | --------------------------------------------- | ---------- | ------------------------------------------- |
|
|
86
|
-
| `[config]` | `InputSignal<IProcessBarStepConfigInterface>` | `Required` | Object configuration chứa toàn bộ settings. |
|
|
302
|
+
## Thứ tự ưu tiên màu sắc
|
|
87
303
|
|
|
88
|
-
|
|
304
|
+
Với mỗi bước, màu nền được xác định theo thứ tự ưu tiên sau:
|
|
89
305
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
| `advancedConfig` | `WritableSignal<Signal<AdvancedConfig>[]>` | Cấu hình nâng cao (array các signals). |
|
|
94
|
-
| `positionLabel` | `'top' \| 'bottom' \| 'inner'` | Vị trí hiển thị label. |
|
|
95
|
-
| `justifyLabel` | `'start' \| 'center' \| 'end'` | Căn lề cho label và nội dung. |
|
|
96
|
-
| `widthStep` | `string` | Chiều rộng mỗi bước (vd: "80px"). |
|
|
97
|
-
| `heightStep` | `string` | Chiều cao mỗi bước. |
|
|
98
|
-
| `backgroundColorStep` | `string` | Màu nền mặc định cho các bước chưa active. |
|
|
99
|
-
| `radiusStep` | `number` | Bo góc (px). |
|
|
100
|
-
| `classInclude` | `string` | Custom class cho container. |
|
|
306
|
+
1. `processCompletedColor` (nếu `processCompleted: true`)
|
|
307
|
+
2. `currentProcessColor` (nếu `isCurrentProcess: true`)
|
|
308
|
+
3. `backgroundColorStep` từ config (màu mặc định cho bước chưa active)
|
|
101
309
|
|
|
102
|
-
|
|
310
|
+
## Lưu ý quan trọng
|
|
103
311
|
|
|
104
|
-
|
|
105
|
-
| -------------------- | -------- | ------------------------------------------------ |
|
|
106
|
-
| `totalStep` | `number` | Tổng số bước. |
|
|
107
|
-
| `currentStep` | `number` | Bước hiện tại (1-based index). |
|
|
108
|
-
| `completedStep` | `number` | Các bước đã hoàn thành (mặc định = currentStep). |
|
|
109
|
-
| `currentStepColor` | `string` | Màu của bước hiện tại. |
|
|
110
|
-
| `stepCompletedColor` | `string` | Màu của các bước đã hoàn thành. |
|
|
312
|
+
⚠️ **Chọn một trong hai mode**: `simpleConfig` và `advancedConfig` không dùng cùng nhau. Nếu cả hai đều được set, `advancedConfig` có độ ưu tiên cao hơn vì template sử dụng `config().advancedConfig?.() || simpleSteps()`.
|
|
111
313
|
|
|
112
|
-
|
|
314
|
+
⚠️ **WritableSignal bắt buộc**: Cả `simpleConfig` và `advancedConfig` đều phải là `WritableSignal`, không thể dùng `computed()` trực tiếp. Khi cần reactive từ computed, dùng `effect()` để sync giá trị vào `WritableSignal` (xem Ví dụ 3).
|
|
113
315
|
|
|
114
|
-
|
|
115
|
-
| ----------------------- | --------- | --------------------------- |
|
|
116
|
-
| `label` | `string` | Label hiển thị. |
|
|
117
|
-
| `processCompleted` | `boolean` | Trạng thái đã hoàn thành. |
|
|
118
|
-
| `processCompletedColor` | `string` | Màu khi hoàn thành. |
|
|
119
|
-
| `isCurrentProcess` | `boolean` | Trạng thái đang thực hiện. |
|
|
120
|
-
| `currentProcessColor` | `string` | Màu khi đang thực hiện. |
|
|
121
|
-
| `classIncludeStep` | `string` | Custom class cho từng bước. |
|
|
122
|
-
| `classLabel` | `string` | Custom class cho label. |
|
|
316
|
+
⚠️ **completedStep trong Simple Mode**: Nếu không set `completedStep`, nó mặc định bằng `currentStep`. Bước có `index <= completedStep` sẽ tô màu `stepCompletedColor`, bước có `index === currentStep` tô màu `currentStepColor`.
|
|
123
317
|
|
|
124
|
-
|
|
318
|
+
⚠️ **Advanced Mode — Color Priority**: Màu `processCompletedColor` ghi đè `currentProcessColor`. Nếu một bước vừa có `processCompleted: true` vừa có `isCurrentProcess: true`, màu hoàn thành sẽ được hiển thị.
|
|
125
319
|
|
|
126
|
-
|
|
127
|
-
| --------------- | ------- | ---------------- |
|
|
128
|
-
| Angular | 18+ | Framework |
|
|
129
|
-
| Angular Signals | - | State management |
|
|
130
|
-
| OnPush | - | Change Detection |
|
|
320
|
+
⚠️ **Global Default Token**: Dùng `PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT` (từ `@libs-ui/utils`) để thiết lập giá trị mặc định cho `widthStep`, `heightStep`, `backgroundColorStep`, `radiusStep` ở cấp app — tránh lặp lại config trong từng component.
|
|
131
321
|
|
|
132
322
|
## Demo
|
|
133
323
|
|
|
@@ -135,4 +325,4 @@ readonly config = signal<IProcessBarStepConfigInterface>({
|
|
|
135
325
|
npx nx serve core-ui
|
|
136
326
|
```
|
|
137
327
|
|
|
138
|
-
Truy cập
|
|
328
|
+
Truy cập: http://localhost:4500/components/process-bar/steps
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { computed, signal, input, inject,
|
|
2
|
+
import { computed, signal, input, inject, Component, ChangeDetectionStrategy } from '@angular/core';
|
|
3
3
|
import { isNil, PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT } from '@libs-ui/utils';
|
|
4
4
|
|
|
5
5
|
class LibsUiComponentsProcessBarStepsComponent {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"libs-ui-components-process-bar-steps.mjs","sources":["../../../../../../libs-ui/components/process-bar/steps/src/process-bar-steps.component.ts","../../../../../../libs-ui/components/process-bar/steps/src/process-bar-steps.component.html","../../../../../../libs-ui/components/process-bar/steps/src/libs-ui-components-process-bar-steps.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input, signal, WritableSignal } from '@angular/core';\nimport { IProcessBarStepAdvancedConfigInterface, IProcessBarStepConfigInterface } from './process-bar-step.interface';\nimport { isNil, PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT } from '@libs-ui/utils';\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: 'libs_ui-components-process_bar-steps',\n standalone: true,\n templateUrl: './process-bar-steps.component.html',\n styleUrl: './process-bar-steps.component.css',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class LibsUiComponentsProcessBarStepsComponent {\n protected width = computed(() => this.config().widthStep ?? this.processBarConfigDefault?.widthStep ?? '80px');\n protected height = computed(() => this.config().heightStep ?? this.processBarConfigDefault?.heightStep ?? '12px');\n protected backgroundColor = computed(() => this.config().backgroundColorStep ?? this.processBarConfigDefault?.backgroundColorStep ?? '#F8F9FA');\n protected radius = computed(() => `${this.config().radiusStep ?? this.processBarConfigDefault?.radiusStep ?? 20}px`);\n protected simpleSteps = computed<Array<WritableSignal<IProcessBarStepAdvancedConfigInterface>>>(() => {\n const simpleConfig = this.config().simpleConfig?.();\n if (!simpleConfig) {\n return [];\n }\n return Array.from({ length: simpleConfig.totalStep }, (_, index) => {\n index += 1;\n if (isNil(simpleConfig.completedStep)) {\n simpleConfig.completedStep = simpleConfig.currentStep;\n }\n return signal({\n isCurrentProcess: index === simpleConfig.currentStep,\n processCompleted: index <= simpleConfig.completedStep,\n currentProcessColor: simpleConfig.currentStepColor,\n processCompletedColor: simpleConfig.stepCompletedColor,\n });\n });\n });\n\n readonly config = input.required<IProcessBarStepConfigInterface>();\n\n private readonly processBarConfigDefault = inject<IProcessBarStepConfigInterface>(PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT, { optional: true });\n}\n","@let constHtmlPositionLabel = config().positionLabel || 'top';\n@let constHtmlJustifyLabel = config().justifyLabel || 'center';\n@let constHtmlSteps = config().advancedConfig?.() || simpleSteps();\n<div [class]=\"(config().classInclude || '') + ' libs-ui-process-bar-steps'\">\n @if (constHtmlSteps) {\n @for (step of constHtmlSteps; track step()) {\n @let constHtmlActiveBackground = step().processCompleted ? step().processCompletedColor || '#33DA8A' : '';\n @let constHtmlCurrentProcessBackground = step().isCurrentProcess ? step().currentProcessColor || '#4E8CF7' : backgroundColor();\n <div\n [class]=\"(step().classIncludeContainer || '') + ' flex'\"\n [class.flex-col]=\"constHtmlPositionLabel === 'top' || constHtmlPositionLabel === 'bottom'\"\n [style.alignItems]=\"constHtmlJustifyLabel\">\n @if (step().label && constHtmlPositionLabel === 'top') {\n <div [class]=\"(step().classLabel || '') + ' libs-ui-font-h5r flex mb-[4px]'\">\n {{ step().label }}\n </div>\n }\n <div\n [class]=\"(step().classIncludeStep || '') + ' process-bar-step flex items-center libs-ui-border-general'\"\n [class.mr-[4px]]=\"!$last\"\n [style.width]=\"width()\"\n [style.height]=\"height()\"\n [style.backgroundColor]=\"constHtmlActiveBackground || constHtmlCurrentProcessBackground\"\n [style.borderRadius]=\"radius()\"\n [style.justifyContent]=\"constHtmlJustifyLabel\">\n @if (step().label && constHtmlPositionLabel === 'inner') {\n <div [class]=\"(step().classLabel || '') + ' libs-ui-font-h5r flex'\">\n {{ step().label }}\n </div>\n }\n </div>\n @if (step().label && constHtmlPositionLabel === 'bottom') {\n <div [class]=\"(step().classLabel || '') + ' libs-ui-font-h5r flex mt-[4px]'\">\n {{ step().label }}\n </div>\n }\n </div>\n }\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;MAWa,wCAAwC,CAAA;IACzC,KAAK,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,SAAS,IAAI,IAAI,CAAC,uBAAuB,EAAE,SAAS,IAAI,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"libs-ui-components-process-bar-steps.mjs","sources":["../../../../../../libs-ui/components/process-bar/steps/src/process-bar-steps.component.ts","../../../../../../libs-ui/components/process-bar/steps/src/process-bar-steps.component.html","../../../../../../libs-ui/components/process-bar/steps/src/libs-ui-components-process-bar-steps.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input, signal, WritableSignal } from '@angular/core';\nimport { IProcessBarStepAdvancedConfigInterface, IProcessBarStepConfigInterface } from './process-bar-step.interface';\nimport { isNil, PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT } from '@libs-ui/utils';\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: 'libs_ui-components-process_bar-steps',\n standalone: true,\n templateUrl: './process-bar-steps.component.html',\n styleUrl: './process-bar-steps.component.css',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class LibsUiComponentsProcessBarStepsComponent {\n protected width = computed(() => this.config().widthStep ?? this.processBarConfigDefault?.widthStep ?? '80px');\n protected height = computed(() => this.config().heightStep ?? this.processBarConfigDefault?.heightStep ?? '12px');\n protected backgroundColor = computed(() => this.config().backgroundColorStep ?? this.processBarConfigDefault?.backgroundColorStep ?? '#F8F9FA');\n protected radius = computed(() => `${this.config().radiusStep ?? this.processBarConfigDefault?.radiusStep ?? 20}px`);\n protected simpleSteps = computed<Array<WritableSignal<IProcessBarStepAdvancedConfigInterface>>>(() => {\n const simpleConfig = this.config().simpleConfig?.();\n if (!simpleConfig) {\n return [];\n }\n return Array.from({ length: simpleConfig.totalStep }, (_, index) => {\n index += 1;\n if (isNil(simpleConfig.completedStep)) {\n simpleConfig.completedStep = simpleConfig.currentStep;\n }\n return signal({\n isCurrentProcess: index === simpleConfig.currentStep,\n processCompleted: index <= simpleConfig.completedStep,\n currentProcessColor: simpleConfig.currentStepColor,\n processCompletedColor: simpleConfig.stepCompletedColor,\n });\n });\n });\n\n readonly config = input.required<IProcessBarStepConfigInterface>();\n\n private readonly processBarConfigDefault = inject<IProcessBarStepConfigInterface>(PROCESS_BAR_STEPS_CONFIG_DEFAULT_TOKEN_INJECT, { optional: true });\n}\n","@let constHtmlPositionLabel = config().positionLabel || 'top';\n@let constHtmlJustifyLabel = config().justifyLabel || 'center';\n@let constHtmlSteps = config().advancedConfig?.() || simpleSteps();\n<div [class]=\"(config().classInclude || '') + ' libs-ui-process-bar-steps'\">\n @if (constHtmlSteps) {\n @for (step of constHtmlSteps; track step()) {\n @let constHtmlActiveBackground = step().processCompleted ? step().processCompletedColor || '#33DA8A' : '';\n @let constHtmlCurrentProcessBackground = step().isCurrentProcess ? step().currentProcessColor || '#4E8CF7' : backgroundColor();\n <div\n [class]=\"(step().classIncludeContainer || '') + ' flex'\"\n [class.flex-col]=\"constHtmlPositionLabel === 'top' || constHtmlPositionLabel === 'bottom'\"\n [style.alignItems]=\"constHtmlJustifyLabel\">\n @if (step().label && constHtmlPositionLabel === 'top') {\n <div [class]=\"(step().classLabel || '') + ' libs-ui-font-h5r flex mb-[4px]'\">\n {{ step().label }}\n </div>\n }\n <div\n [class]=\"(step().classIncludeStep || '') + ' process-bar-step flex items-center libs-ui-border-general'\"\n [class.mr-[4px]]=\"!$last\"\n [style.width]=\"width()\"\n [style.height]=\"height()\"\n [style.backgroundColor]=\"constHtmlActiveBackground || constHtmlCurrentProcessBackground\"\n [style.borderRadius]=\"radius()\"\n [style.justifyContent]=\"constHtmlJustifyLabel\">\n @if (step().label && constHtmlPositionLabel === 'inner') {\n <div [class]=\"(step().classLabel || '') + ' libs-ui-font-h5r flex'\">\n {{ step().label }}\n </div>\n }\n </div>\n @if (step().label && constHtmlPositionLabel === 'bottom') {\n <div [class]=\"(step().classLabel || '') + ' libs-ui-font-h5r flex mt-[4px]'\">\n {{ step().label }}\n </div>\n }\n </div>\n }\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;MAWa,wCAAwC,CAAA;IACzC,KAAK,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,SAAS,IAAI,IAAI,CAAC,uBAAuB,EAAE,SAAS,IAAI,MAAM,CAAC,CAAC;IACrG,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,UAAU,IAAI,IAAI,CAAC,uBAAuB,EAAE,UAAU,IAAI,MAAM,CAAC,CAAC;IACxG,eAAe,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,mBAAmB,IAAI,IAAI,CAAC,uBAAuB,EAAE,mBAAmB,IAAI,SAAS,CAAC,CAAC;IACtI,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAA,EAAG,IAAI,CAAC,MAAM,EAAE,CAAC,UAAU,IAAI,IAAI,CAAC,uBAAuB,EAAE,UAAU,IAAI,EAAE,CAAI,EAAA,CAAA,CAAC,CAAC;AAC3G,IAAA,WAAW,GAAG,QAAQ,CAAgE,MAAK;QACnG,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,YAAY,IAAI,CAAC;QACpD,IAAI,CAAC,YAAY,EAAE;AACjB,YAAA,OAAO,EAAE,CAAC;SACX;AACD,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,KAAI;YACjE,KAAK,IAAI,CAAC,CAAC;AACX,YAAA,IAAI,KAAK,CAAC,YAAY,CAAC,aAAa,CAAC,EAAE;AACrC,gBAAA,YAAY,CAAC,aAAa,GAAG,YAAY,CAAC,WAAW,CAAC;aACvD;AACD,YAAA,OAAO,MAAM,CAAC;AACZ,gBAAA,gBAAgB,EAAE,KAAK,KAAK,YAAY,CAAC,WAAW;AACpD,gBAAA,gBAAgB,EAAE,KAAK,IAAI,YAAY,CAAC,aAAa;gBACrD,mBAAmB,EAAE,YAAY,CAAC,gBAAgB;gBAClD,qBAAqB,EAAE,YAAY,CAAC,kBAAkB;AACvD,aAAA,CAAC,CAAC;AACL,SAAC,CAAC,CAAC;AACL,KAAC,CAAC,CAAC;AAEM,IAAA,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAkC,CAAC;IAElD,uBAAuB,GAAG,MAAM,CAAiC,6CAA6C,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;wGA1B1I,wCAAwC,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAxC,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,wCAAwC,sOCXrD,w9DAwCA,EAAA,MAAA,EAAA,CAAA,0DAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;4FD7Ba,wCAAwC,EAAA,UAAA,EAAA,CAAA;kBARpD,SAAS;AAEE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,sCAAsC,EACpC,UAAA,EAAA,IAAI,EAGC,eAAA,EAAA,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,w9DAAA,EAAA,MAAA,EAAA,CAAA,0DAAA,CAAA,EAAA,CAAA;;;AETjD;;AAEG;;;;"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libs-ui/components-process-bar-steps",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.357-0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@angular/
|
|
6
|
-
"@
|
|
5
|
+
"@angular/core": ">=18.0.0",
|
|
6
|
+
"@libs-ui/utils": "0.2.357-0"
|
|
7
7
|
},
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"module": "fesm2022/libs-ui-components-process-bar-steps.mjs",
|