@libs-ui/components-process-bar-circle 0.2.356-37 → 0.2.357-22

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,41 +1,38 @@
1
1
  # @libs-ui/components-process-bar-circle
2
2
 
3
- > Component hiển thị tiến trình dạng vòng tròn (Circular Progress Bar) trực quan, hỗ trợ tùy biến kích thước, màu sắc và nhãn hiển thị.
3
+ > Component hiển thị tiến trình dạng vòng tròn (Circular Progress Bar) SVG-based, hỗ trợ tùy biến linh hoạt về kích thước, màu sắc và nhãn hiển thị.
4
4
 
5
5
  ## Giới thiệu
6
6
 
7
- `LibsUiComponentsProcessBarCircleComponent` là một giải pháp hiển thị tiến trình dạng SVG hình tròn. nhẹ, khả năng mở rộng cao cho phép tùy chỉnh sâu về giao diện thông qua hệ thống Input dựa trên cấu hình (config object).
7
+ `LibsUiComponentsProcessBarCircleComponent` là component vẽ vòng tròn tiến trình sử dụng SVG thuần, đảm bảo độ sắc nét cao trên mọi độ phân giải màn hình. Component nhận toàn bộ cấu hình qua một input duy nhất `[config]` kiểu `IProcessBarCircleInterface`, hỗ trợ tùy chỉnh kích thước, màu nền, màu tiến trình, độ dày nét vẽ hiển thị nhãn phần trăm ở giữa. Mọi thuộc tính SVG được tính toán tự động từ `size` và `strokeWidth`, đảm bảo vòng tròn luôn căn giữa và không bị méo.
8
8
 
9
- ### Tính năng
9
+ ## Tính năng
10
10
 
11
- - ✅ **SVG-based**: Độ sắc nét cao trên mọi độ phân giải.
12
- - ✅ **Cấu hình linh hoạt**: Thay đổi kích thước (size), độ dày (stroke width), màu sắc (color) dễ dàng.
13
- - ✅ **Smooth Animations**: Chuyển đổi mượt mà khi giá trị phần trăm thay đổi.
14
- - ✅ **Label Control**: Tùy chọn hiển thị hoặc ẩn nhãn phần trăm ở giữa vòng tròn.
15
- - ✅ **OnPush Change Detection**: Tối ưu hóa hiệu suất render.
16
- - ✅ **Angular Signals**: Tận dụng chế phản ứng mới của Angular.
11
+ - ✅ **SVG-based**: Vẽ bằng SVG, sắc nét trên mọi độ phân giải và màn hình Retina.
12
+ - ✅ **Cấu hình linh hoạt**: Tùy chỉnh `size`, `strokeWidth`, `color`, `backgroundColor`, `showLabel`, `labelClass`, `classInclude` qua một object duy nhất.
13
+ - ✅ **Smooth Animation**: CSS transition trên `stroke-dashoffset` giúp vòng tròn chuyển đổi mượt mà khi phần trăm thay đổi.
14
+ - ✅ **Label tùy chỉnh**: Hiển thị nhãn phần trăm ở giữa, thể ẩn hoặc ghi đè class CSS.
15
+ - ✅ **OnPush + Signals**: Sử dụng `ChangeDetectionStrategy.OnPush` và Angular Signals, tối ưu hóa hiệu năng render.
16
+ - ✅ **Percent Clamping**: Tự động giới hạn giá trị percent trong khoảng [0, 100], không bị lỗi SVG khi nhận giá trị ngoài phạm vi.
17
17
 
18
18
  ## Khi nào sử dụng
19
19
 
20
- - Hiển thị phần trăm hoàn thành của một tác vụ cụ thể.
21
- - Biểu diễn số liệu thống kê (ví dụ: dung lượng lưu trữ đã dùng).
22
- - Làm bộ chỉ báo tải (loading indicator) có trạng thái.
23
- - Trang trí dashboard với các biểu đồ tiến trình nhỏ gọn.
20
+ - Hiển thị phần trăm hoàn thành của một tác vụ (upload, xử lý, tải dữ liệu).
21
+ - Biểu diễn số liệu thống kê dạng vòng tròn trên dashboard (dung lượng đĩa, tiến độ dự án, điểm số).
22
+ - Làm chỉ báo tiến trình (progress indicator) có trạng thái rõ ràng thay cho spinner đơn giản.
23
+ - Nhúng vào card, bảng dữ liệu hoặc widget nhỏ gọn để trực quan hóa KPI.
24
24
 
25
25
  ## Cài đặt
26
26
 
27
27
  ```bash
28
- # npm
29
28
  npm install @libs-ui/components-process-bar-circle
30
-
31
- # yarn
32
- yarn add @libs-ui/components-process-bar-circle
33
29
  ```
34
30
 
35
31
  ## Import
36
32
 
37
33
  ```typescript
38
34
  import { LibsUiComponentsProcessBarCircleComponent } from '@libs-ui/components-process-bar-circle';
35
+ import { IProcessBarCircleInterface } from '@libs-ui/components-process-bar-circle';
39
36
 
40
37
  @Component({
41
38
  standalone: true,
@@ -45,80 +42,176 @@ import { LibsUiComponentsProcessBarCircleComponent } from '@libs-ui/components-p
45
42
  export class YourComponent {}
46
43
  ```
47
44
 
48
- ## Ví dụ
45
+ ## Ví dụ sử dụng
46
+
47
+ ### 1. Sử dụng cơ bản
49
48
 
50
- ### bản
49
+ Hiển thị vòng tiến trình với các thiết lập mặc định.
51
50
 
52
51
  ```html
53
- <libs_ui-components-process_bar-circle [config]="{ percent: 75 }" />
52
+ <libs_ui-components-process_bar-circle
53
+ [config]="{ percent: 75 }" />
54
+ ```
55
+
56
+ ```typescript
57
+ // Không yêu cầu logic TS
54
58
  ```
55
59
 
56
- ### Tùy chỉnh kích thước và màu sắc
60
+ ---
61
+
62
+ ### 2. Các biến thể
63
+
64
+ Tùy chỉnh kích thước, độ dày và màu sắc khác nhau.
57
65
 
58
66
  ```html
67
+ <!-- Kích thước lớn, màu xanh lá -->
59
68
  <libs_ui-components-process_bar-circle
60
69
  [config]="{
61
- percent: 45,
70
+ percent: 100,
62
71
  size: 150,
63
- strokeWidth: 12,
72
+ strokeWidth: 15,
64
73
  color: '#10b981',
65
- backgroundColor: '#ecfdf5',
66
- labelClass: 'text-xl font-bold text-emerald-600'
74
+ labelClass: 'text-2xl font-bold text-emerald-600'
75
+ }" />
76
+
77
+ <!-- Kích thước nhỏ, màu cam, ẩn label -->
78
+ <libs_ui-components-process_bar-circle
79
+ [config]="{
80
+ percent: 30,
81
+ size: 60,
82
+ strokeWidth: 6,
83
+ color: '#f59e0b',
84
+ showLabel: false
67
85
  }" />
68
86
  ```
69
87
 
70
- ## API
88
+ ```typescript
89
+ // Cấu hình các style khác nhau qua thuộc tính config
90
+ ```
91
+
92
+ ---
93
+
94
+ ### 3. Demo tương tác
71
95
 
72
- ### libs_ui-components-process_bar-circle
96
+ Thay đổi các giá trị trực tiếp để thấy sự phản hồi của component.
73
97
 
74
- #### Inputs
98
+ ```html
99
+ <div class="flex flex-col items-center gap-6">
100
+ <libs_ui-components-process_bar-circle
101
+ [config]="{
102
+ percent: interactivePercent(),
103
+ size: interactiveSize(),
104
+ strokeWidth: interactiveStrokeWidth()
105
+ }" />
106
+
107
+ <div class="w-full max-w-xs space-y-4">
108
+ <div>
109
+ <label for="percent-range" class="block text-xs text-gray-400 mb-1">Percent: {{ interactivePercent() }}%</label>
110
+ <input id="percent-range" type="range" min="0" max="100" [value]="interactivePercent()" (input)="updatePercent($event)" class="w-full">
111
+ </div>
112
+ <div>
113
+ <label for="size-range" class="block text-xs text-gray-400 mb-1">Size: {{ interactiveSize() }}px</label>
114
+ <input id="size-range" type="range" min="50" max="250" [value]="interactiveSize()" (input)="updateSize($event)" class="w-full">
115
+ </div>
116
+ </div>
117
+ </div>
118
+ ```
75
119
 
76
- | Property | Type | Default | Description |
77
- | ---------- | ---------------------------- | ------------ | --------------------------------------- |
78
- | `[config]` | `IProcessBarCircleInterface` | **Bắt buộc** | Đối tượng cấu hình cho vòng tiến trình. |
120
+ ```typescript
121
+ import { signal } from '@angular/core';
122
+
123
+ // [config]="{ percent: interactivePercent(), size: interactiveSize(), strokeWidth: interactiveStrokeWidth() }"
124
+ readonly interactivePercent = signal(65);
125
+ readonly interactiveSize = signal(120);
126
+ readonly interactiveStrokeWidth = signal(10);
127
+
128
+ // Handler đổi percent — dùng cho (input)="updatePercent($event)"
129
+ updatePercent(e: Event): void {
130
+ this.interactivePercent.set(+(e.target as HTMLInputElement).value);
131
+ }
132
+
133
+ // Handler đổi size — dùng cho (input)="updateSize($event)"
134
+ updateSize(e: Event): void {
135
+ this.interactiveSize.set(+(e.target as HTMLInputElement).value);
136
+ }
137
+ ```
138
+
139
+ ## @Input()
140
+
141
+ Component nhận một input duy nhất là `[config]` kiểu `IProcessBarCircleInterface` (bắt buộc).
142
+
143
+ | Input | Type | Default | Mô tả | Ví dụ |
144
+ |---|---|---|---|---|
145
+ | `[config]` | `IProcessBarCircleInterface` | **Bắt buộc** | Object chứa toàn bộ cấu hình hiển thị của vòng tròn tiến trình. | `[config]="{ percent: 75 }"` |
146
+
147
+ ### Các thuộc tính của `IProcessBarCircleInterface`
148
+
149
+ | Thuộc tính | Type | Default | Bắt buộc | Mô tả | Ví dụ |
150
+ |---|---|---|---|---|---|
151
+ | `percent` | `number` | — | ✅ Có | Phần trăm hoàn thành. Tự động bị giới hạn trong khoảng [0, 100]. | `percent: 75` |
152
+ | `size` | `number` | `100` | Không | Kích thước cạnh của vùng chứa SVG (px). Áp dụng cho cả width lẫn height. | `size: 150` |
153
+ | `strokeWidth` | `number` | `8` | Không | Độ dày của nét vẽ vòng tròn (px). Ảnh hưởng đến bán kính thực của đường kẻ. | `strokeWidth: 12` |
154
+ | `color` | `string` | `'#3b82f6'` | Không | Màu của đường tiến trình (HEX, RGB, CSS color). | `color: '#10b981'` |
155
+ | `backgroundColor` | `string` | `'#f1f5f9'` | Không | Màu nền của vòng tròn phía sau (phần chưa hoàn thành). | `backgroundColor: '#e0f2fe'` |
156
+ | `showLabel` | `boolean` | `true` | Không | Có hiển thị nhãn phần trăm ở giữa vòng tròn hay không. | `showLabel: false` |
157
+ | `labelClass` | `string` | `''` | Không | Class CSS tùy chỉnh cho thẻ `<span>` chứa nhãn phần trăm. Ghi đè style mặc định `text-sm font-semibold text-gray-700`. | `labelClass: 'text-xl font-bold text-blue-600'` |
158
+ | `classInclude` | `string` | `''` | Không | Class CSS tùy chỉnh thêm vào thẻ `<div>` bọc ngoài cùng của component. | `classInclude: 'mx-auto'` |
159
+
160
+ ## @Output()
161
+
162
+ Component này không phát ra Output event.
79
163
 
80
164
  ## Types & Interfaces
81
165
 
82
166
  ```typescript
167
+ import { IProcessBarCircleInterface } from '@libs-ui/components-process-bar-circle';
168
+
83
169
  export interface IProcessBarCircleInterface {
84
- /** Phần trăm hoàn thành (0-100) */
170
+ /** Phần trăm hoàn thành (0-100) — bắt buộc */
85
171
  percent: number;
86
- /** Kích thước của vòng tròn (px), mặc định 100 */
172
+ /** Kích thước vùng SVG (px), mặc định 100 */
87
173
  size?: number;
88
- /** Độ dày của đường tiến trình (px), mặc định 8 */
174
+ /** Độ dày nét vẽ (px), mặc định 8 */
89
175
  strokeWidth?: number;
90
- /** Màu của đường tiến trình, mặc định #3b82f6 (Blue 500) */
176
+ /** Màu đường tiến trình, mặc định '#3b82f6' */
91
177
  color?: string;
92
- /** Màu nền của vòng tròn, mặc định #f1f5f9 (Slate 100) */
178
+ /** Màu nền vòng tròn, mặc định '#f1f5f9' */
93
179
  backgroundColor?: string;
94
- /** hiển thị chữ số phần trăm ở giữa không, mặc định true */
180
+ /** Hiển thị nhãn phần trăm ở giữa, mặc định true */
95
181
  showLabel?: boolean;
96
- /** Class CSS tùy chỉnh cho nhãn phần trăm */
182
+ /** Class CSS bổ sung cho thẻ <span> nhãn phần trăm */
97
183
  labelClass?: string;
98
- /** Class CSS tùy chỉnh cho container */
184
+ /** Class CSS bổ sung cho thẻ <div> bọc ngoài cùng */
99
185
  classInclude?: string;
100
186
  }
101
187
  ```
102
188
 
103
- ## Hidden Logic
189
+ ## Lưu ý quan trọng
104
190
 
105
- ### 1. SVG Calculations
191
+ ⚠️ **Tính toán SVG tự động**: Component tự tính `radius = (size - strokeWidth) / 2` và `center = size / 2`. Nếu `strokeWidth` quá lớn so với `size`, vòng tròn có thể bị tràn ra ngoài — hãy đảm bảo `strokeWidth < size / 2`.
106
192
 
107
- Component tự động tính toán các thuộc tính SVG (`stroke-dasharray`, `stroke-dashoffset`) dựa trên `size` `strokeWidth` để đảm bảo vòng tròn luôn căn giữa không bị cắt mất viền khi thay dổi độ dày.
193
+ ⚠️ **Percent ngoài phạm vi**: Giá trị `percent` được tự động kẹp vào [0, 100] qua `Math.max(0, Math.min(100, percent))`. Không cần validate thủ công trước khi truyền vào.
108
194
 
109
- ### 2. Transition Smoothing
195
+ ⚠️ **labelClass ghi đè hoàn toàn**: Khi truyền `labelClass`, các class mặc định `text-sm font-semibold text-gray-700` vẫn còn nhưng `labelClass` được bind thêm qua `[class]`. Để thay thế hoàn toàn style chữ, hãy dùng `!important` hoặc class có specificity cao hơn.
110
196
 
111
- Sử dụng CSS transitions trên thuộc tính `stroke-dashoffset` giúp việc cập nhật giá trị tiến trình diễn ra mượt không cần đến logic JavaScript phức tạp.
197
+ ⚠️ **SVG rotate**: Component áp dụng `class="transform -rotate-90"` lên thẻ `<svg>` để vòng tròn bắt đầu từ đỉnh (12 giờ) thay từ bên phải (3 giờ). Đây là hành vi mặc định, không thể tắt qua config.
112
198
 
113
- ## Demo
199
+ ## Hidden Logic
114
200
 
115
- ```bash
116
- # Xem demo trực tiếp
117
- npx nx serve core-ui
201
+ ### 1. SVG Dash Array Calculation
202
+
203
+ Component tính `circumference = 2 * Math.PI * radius` để xác định chu vi đường tròn, sau đó đặt `stroke-dasharray = circumference` và điều chỉnh `stroke-dashoffset = circumference - (percent / 100) * circumference`. Khi `percent = 0`, `dashoffset = circumference` (ẩn toàn bộ nét vẽ). Khi `percent = 100`, `dashoffset = 0` (hiện toàn bộ vòng tròn).
204
+
205
+ ```
206
+ circumference = 2 * Math.PI * ((size - strokeWidth) / 2)
207
+ offset = circumference - (percent / 100) * circumference
118
208
  ```
119
209
 
120
- Truy cập: `http://localhost:4200/process-bar/circle`
210
+ ### 2. Dynamic Coordinate Handling
121
211
 
122
- ## License
212
+ Tất cả thuộc tính SVG (`cx`, `cy`, `r`) được tính toán động bằng computed signals, đảm bảo vòng tròn luôn căn giữa chính xác dù `size` hay `strokeWidth` thay đổi runtime.
123
213
 
124
- MIT
214
+ ```
215
+ radius = (size - strokeWidth) / 2
216
+ center = size / 2
217
+ ```
@@ -18,10 +18,10 @@ export class LibsUiComponentsProcessBarCircleComponent {
18
18
  backgroundColor = computed(() => this.config().backgroundColor ?? '#f1f5f9');
19
19
  showLabel = computed(() => this.config().showLabel ?? true);
20
20
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiComponentsProcessBarCircleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
21
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LibsUiComponentsProcessBarCircleComponent, isStandalone: true, selector: "libs_ui-components-process_bar-circle", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div\n [class]=\"config().classInclude\"\n [style.width.px]=\"size()\"\n [style.height.px]=\"size()\"\n class=\"relative flex items-center justify-center\">\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n class=\"transform -rotate-90\">\n <!-- Background Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"backgroundColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\" />\n <!-- Progress Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n [attr.stroke-dasharray]=\"circumference()\"\n [attr.stroke-dashoffset]=\"offset()\"\n stroke-linecap=\"round\"\n fill=\"transparent\"\n class=\"transition-all duration-300 ease-in-out\" />\n </svg>\n\n <!-- Label -->\n @if (showLabel()) {\n <div class=\"absolute inset-0 flex items-center justify-center\">\n <span\n [class]=\"config().labelClass\"\n class=\"text-sm font-semibold text-gray-700\">\n {{ config().percent }}%\n </span>\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
21
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LibsUiComponentsProcessBarCircleComponent, isStandalone: true, selector: "libs_ui-components-process_bar-circle", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div\n [class]=\"config().classInclude\"\n [style.width.px]=\"size()\"\n [style.height.px]=\"size()\"\n class=\"relative flex items-center justify-center\">\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n class=\"transform -rotate-90\">\n <!-- Background Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"backgroundColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\" />\n <!-- Progress Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n [attr.stroke-dasharray]=\"circumference()\"\n [attr.stroke-dashoffset]=\"offset()\"\n stroke-linecap=\"round\"\n fill=\"transparent\"\n class=\"transition-all duration-300 ease-in-out\" />\n </svg>\n\n <!-- Label -->\n @if (showLabel()) {\n <div class=\"absolute inset-0 flex items-center justify-center\">\n <span\n [class]=\"config().labelClass\"\n class=\"text-sm font-semibold text-gray-700\">\n {{ config().percent }}%\n </span>\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
22
22
  }
23
23
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiComponentsProcessBarCircleComponent, decorators: [{
24
24
  type: Component,
25
25
  args: [{ selector: 'libs_ui-components-process_bar-circle', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n [class]=\"config().classInclude\"\n [style.width.px]=\"size()\"\n [style.height.px]=\"size()\"\n class=\"relative flex items-center justify-center\">\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n class=\"transform -rotate-90\">\n <!-- Background Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"backgroundColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\" />\n <!-- Progress Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n [attr.stroke-dasharray]=\"circumference()\"\n [attr.stroke-dashoffset]=\"offset()\"\n stroke-linecap=\"round\"\n fill=\"transparent\"\n class=\"transition-all duration-300 ease-in-out\" />\n </svg>\n\n <!-- Label -->\n @if (showLabel()) {\n <div class=\"absolute inset-0 flex items-center justify-center\">\n <span\n [class]=\"config().labelClass\"\n class=\"text-sm font-semibold text-gray-700\">\n {{ config().percent }}%\n </span>\n </div>\n }\n</div>\n" }]
26
26
  }] });
27
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvY2Vzcy1iYXItY2lyY2xlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvY29tcG9uZW50cy9wcm9jZXNzLWJhci9jaXJjbGUvc3JjL3Byb2Nlc3MtYmFyLWNpcmNsZS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvcHJvY2Vzcy1iYXIvY2lyY2xlL3NyYy9wcm9jZXNzLWJhci1jaXJjbGUuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQzs7QUFZcEYsTUFBTSxPQUFPLHlDQUF5QztJQUNwRCxtQ0FBbUM7SUFDMUIsTUFBTSxHQUFHLEtBQUssQ0FBQyxRQUFRLEVBQThCLENBQUM7SUFFL0QsdUNBQXVDO0lBQzdCLElBQUksR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLElBQUksSUFBSSxHQUFHLENBQUMsQ0FBQztJQUNqRCxXQUFXLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxXQUFXLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDN0QsTUFBTSxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNoRSxNQUFNLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUN6QyxhQUFhLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzVELE1BQU0sR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQy9CLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQzVELE9BQU8sSUFBSSxDQUFDLGFBQWEsRUFBRSxHQUFHLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztJQUNqRSxDQUFDLENBQUMsQ0FBQztJQUVPLEtBQUssR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQUssSUFBSSxTQUFTLENBQUMsQ0FBQztJQUN6RCxlQUFlLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxlQUFlLElBQUksU0FBUyxDQUFDLENBQUM7SUFDN0UsU0FBUyxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxDQUFDO3dHQWpCM0QseUNBQXlDOzRGQUF6Qyx5Q0FBeUMsdU9DYnRELHN1Q0EwQ0EseUREbENZLFlBQVk7OzRGQUtYLHlDQUF5QztrQkFUckQsU0FBUzsrQkFFRSx1Q0FBdUMsY0FDckMsSUFBSSxXQUNQLENBQUMsWUFBWSxDQUFDLG1CQUdOLHVCQUF1QixDQUFDLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgY29tcHV0ZWQsIGlucHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBJUHJvY2Vzc0JhckNpcmNsZUludGVyZmFjZSB9IGZyb20gJy4vcHJvY2Vzcy1iYXItY2lyY2xlLmludGVyZmFjZSc7XG5cbkBDb21wb25lbnQoe1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQGFuZ3VsYXItZXNsaW50L2NvbXBvbmVudC1zZWxlY3RvclxuICBzZWxlY3RvcjogJ2xpYnNfdWktY29tcG9uZW50cy1wcm9jZXNzX2Jhci1jaXJjbGUnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbQ29tbW9uTW9kdWxlXSxcbiAgdGVtcGxhdGVVcmw6ICcuL3Byb2Nlc3MtYmFyLWNpcmNsZS5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3Byb2Nlc3MtYmFyLWNpcmNsZS5jb21wb25lbnQuY3NzJ10sXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxufSlcbmV4cG9ydCBjbGFzcyBMaWJzVWlDb21wb25lbnRzUHJvY2Vzc0JhckNpcmNsZUNvbXBvbmVudCB7XG4gIC8qKiBD4bqldSBow6xuaCBjaG8gdsOybmcgdGnhur9uIHRyw6xuaCAqL1xuICByZWFkb25seSBjb25maWcgPSBpbnB1dC5yZXF1aXJlZDxJUHJvY2Vzc0JhckNpcmNsZUludGVyZmFjZT4oKTtcblxuICAvLyBDb21wdXRlZCB2YWx1ZXMgZm9yIFNWRyBjYWxjdWxhdGlvbnNcbiAgcHJvdGVjdGVkIHNpemUgPSBjb21wdXRlZCgoKSA9PiB0aGlzLmNvbmZpZygpLnNpemUgPz8gMTAwKTtcbiAgcHJvdGVjdGVkIHN0cm9rZVdpZHRoID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jb25maWcoKS5zdHJva2VXaWR0aCA/PyA4KTtcbiAgcHJvdGVjdGVkIHJhZGl1cyA9IGNvbXB1dGVkKCgpID0+ICh0aGlzLnNpemUoKSAtIHRoaXMuc3Ryb2tlV2lkdGgoKSkgLyAyKTtcbiAgcHJvdGVjdGVkIGNlbnRlciA9IGNvbXB1dGVkKCgpID0+IHRoaXMuc2l6ZSgpIC8gMik7XG4gIHByb3RlY3RlZCBjaXJjdW1mZXJlbmNlID0gY29tcHV0ZWQoKCkgPT4gMiAqIE1hdGguUEkgKiB0aGlzLnJhZGl1cygpKTtcbiAgcHJvdGVjdGVkIG9mZnNldCA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBjb25zdCBwID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oMTAwLCB0aGlzLmNvbmZpZygpLnBlcmNlbnQpKTtcbiAgICByZXR1cm4gdGhpcy5jaXJjdW1mZXJlbmNlKCkgLSAocCAvIDEwMCkgKiB0aGlzLmNpcmN1bWZlcmVuY2UoKTtcbiAgfSk7XG5cbiAgcHJvdGVjdGVkIGNvbG9yID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jb25maWcoKS5jb2xvciA/PyAnIzNiODJmNicpO1xuICBwcm90ZWN0ZWQgYmFja2dyb3VuZENvbG9yID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jb25maWcoKS5iYWNrZ3JvdW5kQ29sb3IgPz8gJyNmMWY1ZjknKTtcbiAgcHJvdGVjdGVkIHNob3dMYWJlbCA9IGNvbXB1dGVkKCgpID0+IHRoaXMuY29uZmlnKCkuc2hvd0xhYmVsID8/IHRydWUpO1xufVxuIiwiPGRpdlxuICBbY2xhc3NdPVwiY29uZmlnKCkuY2xhc3NJbmNsdWRlXCJcbiAgW3N0eWxlLndpZHRoLnB4XT1cInNpemUoKVwiXG4gIFtzdHlsZS5oZWlnaHQucHhdPVwic2l6ZSgpXCJcbiAgY2xhc3M9XCJyZWxhdGl2ZSBmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlclwiPlxuICA8c3ZnXG4gICAgW2F0dHIud2lkdGhdPVwic2l6ZSgpXCJcbiAgICBbYXR0ci5oZWlnaHRdPVwic2l6ZSgpXCJcbiAgICBjbGFzcz1cInRyYW5zZm9ybSAtcm90YXRlLTkwXCI+XG4gICAgPCEtLSBCYWNrZ3JvdW5kIENpcmNsZSAtLT5cbiAgICA8Y2lyY2xlXG4gICAgICBbYXR0ci5jeF09XCJjZW50ZXIoKVwiXG4gICAgICBbYXR0ci5jeV09XCJjZW50ZXIoKVwiXG4gICAgICBbYXR0ci5yXT1cInJhZGl1cygpXCJcbiAgICAgIFthdHRyLnN0cm9rZV09XCJiYWNrZ3JvdW5kQ29sb3IoKVwiXG4gICAgICBbYXR0ci5zdHJva2Utd2lkdGhdPVwic3Ryb2tlV2lkdGgoKVwiXG4gICAgICBmaWxsPVwidHJhbnNwYXJlbnRcIiAvPlxuICAgIDwhLS0gUHJvZ3Jlc3MgQ2lyY2xlIC0tPlxuICAgIDxjaXJjbGVcbiAgICAgIFthdHRyLmN4XT1cImNlbnRlcigpXCJcbiAgICAgIFthdHRyLmN5XT1cImNlbnRlcigpXCJcbiAgICAgIFthdHRyLnJdPVwicmFkaXVzKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlXT1cImNvbG9yKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlLXdpZHRoXT1cInN0cm9rZVdpZHRoKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlLWRhc2hhcnJheV09XCJjaXJjdW1mZXJlbmNlKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlLWRhc2hvZmZzZXRdPVwib2Zmc2V0KClcIlxuICAgICAgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiXG4gICAgICBmaWxsPVwidHJhbnNwYXJlbnRcIlxuICAgICAgY2xhc3M9XCJ0cmFuc2l0aW9uLWFsbCBkdXJhdGlvbi0zMDAgZWFzZS1pbi1vdXRcIiAvPlxuICA8L3N2Zz5cblxuICA8IS0tIExhYmVsIC0tPlxuICBAaWYgKHNob3dMYWJlbCgpKSB7XG4gICAgPGRpdiBjbGFzcz1cImFic29sdXRlIGluc2V0LTAgZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXJcIj5cbiAgICAgIDxzcGFuXG4gICAgICAgIFtjbGFzc109XCJjb25maWcoKS5sYWJlbENsYXNzXCJcbiAgICAgICAgY2xhc3M9XCJ0ZXh0LXNtIGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMFwiPlxuICAgICAgICB7eyBjb25maWcoKS5wZXJjZW50IH19JVxuICAgICAgPC9zcGFuPlxuICAgIDwvZGl2PlxuICB9XG48L2Rpdj5cbiJdfQ==
27
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvY2Vzcy1iYXItY2lyY2xlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvY29tcG9uZW50cy9wcm9jZXNzLWJhci9jaXJjbGUvc3JjL3Byb2Nlc3MtYmFyLWNpcmNsZS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvcHJvY2Vzcy1iYXIvY2lyY2xlL3NyYy9wcm9jZXNzLWJhci1jaXJjbGUuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQzs7QUFXcEYsTUFBTSxPQUFPLHlDQUF5QztJQUNwRCxtQ0FBbUM7SUFDMUIsTUFBTSxHQUFHLEtBQUssQ0FBQyxRQUFRLEVBQThCLENBQUM7SUFFL0QsdUNBQXVDO0lBQzdCLElBQUksR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLElBQUksSUFBSSxHQUFHLENBQUMsQ0FBQztJQUNqRCxXQUFXLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxXQUFXLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDN0QsTUFBTSxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNoRSxNQUFNLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUN6QyxhQUFhLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzVELE1BQU0sR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQy9CLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQzVELE9BQU8sSUFBSSxDQUFDLGFBQWEsRUFBRSxHQUFHLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztJQUNqRSxDQUFDLENBQUMsQ0FBQztJQUVPLEtBQUssR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQUssSUFBSSxTQUFTLENBQUMsQ0FBQztJQUN6RCxlQUFlLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxlQUFlLElBQUksU0FBUyxDQUFDLENBQUM7SUFDN0UsU0FBUyxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxDQUFDO3dHQWpCM0QseUNBQXlDOzRGQUF6Qyx5Q0FBeUMsdU9DWnRELHN1Q0EwQ0EsMkNEbENZLFlBQVk7OzRGQUlYLHlDQUF5QztrQkFSckQsU0FBUzsrQkFFRSx1Q0FBdUMsY0FDckMsSUFBSSxXQUNQLENBQUMsWUFBWSxDQUFDLG1CQUVOLHVCQUF1QixDQUFDLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgY29tcHV0ZWQsIGlucHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBJUHJvY2Vzc0JhckNpcmNsZUludGVyZmFjZSB9IGZyb20gJy4vcHJvY2Vzcy1iYXItY2lyY2xlLmludGVyZmFjZSc7XG5cbkBDb21wb25lbnQoe1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQGFuZ3VsYXItZXNsaW50L2NvbXBvbmVudC1zZWxlY3RvclxuICBzZWxlY3RvcjogJ2xpYnNfdWktY29tcG9uZW50cy1wcm9jZXNzX2Jhci1jaXJjbGUnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbQ29tbW9uTW9kdWxlXSxcbiAgdGVtcGxhdGVVcmw6ICcuL3Byb2Nlc3MtYmFyLWNpcmNsZS5jb21wb25lbnQuaHRtbCcsXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxufSlcbmV4cG9ydCBjbGFzcyBMaWJzVWlDb21wb25lbnRzUHJvY2Vzc0JhckNpcmNsZUNvbXBvbmVudCB7XG4gIC8qKiBD4bqldSBow6xuaCBjaG8gdsOybmcgdGnhur9uIHRyw6xuaCAqL1xuICByZWFkb25seSBjb25maWcgPSBpbnB1dC5yZXF1aXJlZDxJUHJvY2Vzc0JhckNpcmNsZUludGVyZmFjZT4oKTtcblxuICAvLyBDb21wdXRlZCB2YWx1ZXMgZm9yIFNWRyBjYWxjdWxhdGlvbnNcbiAgcHJvdGVjdGVkIHNpemUgPSBjb21wdXRlZCgoKSA9PiB0aGlzLmNvbmZpZygpLnNpemUgPz8gMTAwKTtcbiAgcHJvdGVjdGVkIHN0cm9rZVdpZHRoID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jb25maWcoKS5zdHJva2VXaWR0aCA/PyA4KTtcbiAgcHJvdGVjdGVkIHJhZGl1cyA9IGNvbXB1dGVkKCgpID0+ICh0aGlzLnNpemUoKSAtIHRoaXMuc3Ryb2tlV2lkdGgoKSkgLyAyKTtcbiAgcHJvdGVjdGVkIGNlbnRlciA9IGNvbXB1dGVkKCgpID0+IHRoaXMuc2l6ZSgpIC8gMik7XG4gIHByb3RlY3RlZCBjaXJjdW1mZXJlbmNlID0gY29tcHV0ZWQoKCkgPT4gMiAqIE1hdGguUEkgKiB0aGlzLnJhZGl1cygpKTtcbiAgcHJvdGVjdGVkIG9mZnNldCA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBjb25zdCBwID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oMTAwLCB0aGlzLmNvbmZpZygpLnBlcmNlbnQpKTtcbiAgICByZXR1cm4gdGhpcy5jaXJjdW1mZXJlbmNlKCkgLSAocCAvIDEwMCkgKiB0aGlzLmNpcmN1bWZlcmVuY2UoKTtcbiAgfSk7XG5cbiAgcHJvdGVjdGVkIGNvbG9yID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jb25maWcoKS5jb2xvciA/PyAnIzNiODJmNicpO1xuICBwcm90ZWN0ZWQgYmFja2dyb3VuZENvbG9yID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jb25maWcoKS5iYWNrZ3JvdW5kQ29sb3IgPz8gJyNmMWY1ZjknKTtcbiAgcHJvdGVjdGVkIHNob3dMYWJlbCA9IGNvbXB1dGVkKCgpID0+IHRoaXMuY29uZmlnKCkuc2hvd0xhYmVsID8/IHRydWUpO1xufVxuIiwiPGRpdlxuICBbY2xhc3NdPVwiY29uZmlnKCkuY2xhc3NJbmNsdWRlXCJcbiAgW3N0eWxlLndpZHRoLnB4XT1cInNpemUoKVwiXG4gIFtzdHlsZS5oZWlnaHQucHhdPVwic2l6ZSgpXCJcbiAgY2xhc3M9XCJyZWxhdGl2ZSBmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlclwiPlxuICA8c3ZnXG4gICAgW2F0dHIud2lkdGhdPVwic2l6ZSgpXCJcbiAgICBbYXR0ci5oZWlnaHRdPVwic2l6ZSgpXCJcbiAgICBjbGFzcz1cInRyYW5zZm9ybSAtcm90YXRlLTkwXCI+XG4gICAgPCEtLSBCYWNrZ3JvdW5kIENpcmNsZSAtLT5cbiAgICA8Y2lyY2xlXG4gICAgICBbYXR0ci5jeF09XCJjZW50ZXIoKVwiXG4gICAgICBbYXR0ci5jeV09XCJjZW50ZXIoKVwiXG4gICAgICBbYXR0ci5yXT1cInJhZGl1cygpXCJcbiAgICAgIFthdHRyLnN0cm9rZV09XCJiYWNrZ3JvdW5kQ29sb3IoKVwiXG4gICAgICBbYXR0ci5zdHJva2Utd2lkdGhdPVwic3Ryb2tlV2lkdGgoKVwiXG4gICAgICBmaWxsPVwidHJhbnNwYXJlbnRcIiAvPlxuICAgIDwhLS0gUHJvZ3Jlc3MgQ2lyY2xlIC0tPlxuICAgIDxjaXJjbGVcbiAgICAgIFthdHRyLmN4XT1cImNlbnRlcigpXCJcbiAgICAgIFthdHRyLmN5XT1cImNlbnRlcigpXCJcbiAgICAgIFthdHRyLnJdPVwicmFkaXVzKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlXT1cImNvbG9yKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlLXdpZHRoXT1cInN0cm9rZVdpZHRoKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlLWRhc2hhcnJheV09XCJjaXJjdW1mZXJlbmNlKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlLWRhc2hvZmZzZXRdPVwib2Zmc2V0KClcIlxuICAgICAgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiXG4gICAgICBmaWxsPVwidHJhbnNwYXJlbnRcIlxuICAgICAgY2xhc3M9XCJ0cmFuc2l0aW9uLWFsbCBkdXJhdGlvbi0zMDAgZWFzZS1pbi1vdXRcIiAvPlxuICA8L3N2Zz5cblxuICA8IS0tIExhYmVsIC0tPlxuICBAaWYgKHNob3dMYWJlbCgpKSB7XG4gICAgPGRpdiBjbGFzcz1cImFic29sdXRlIGluc2V0LTAgZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXJcIj5cbiAgICAgIDxzcGFuXG4gICAgICAgIFtjbGFzc109XCJjb25maWcoKS5sYWJlbENsYXNzXCJcbiAgICAgICAgY2xhc3M9XCJ0ZXh0LXNtIGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMFwiPlxuICAgICAgICB7eyBjb25maWcoKS5wZXJjZW50IH19JVxuICAgICAgPC9zcGFuPlxuICAgIDwvZGl2PlxuICB9XG48L2Rpdj5cbiJdfQ==
@@ -1,6 +1,6 @@
1
1
  import { CommonModule } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { input, computed, Component, ChangeDetectionStrategy } from '@angular/core';
4
4
 
5
5
  class LibsUiComponentsProcessBarCircleComponent {
6
6
  /** Cấu hình cho vòng tiến trình */
@@ -19,7 +19,7 @@ class LibsUiComponentsProcessBarCircleComponent {
19
19
  backgroundColor = computed(() => this.config().backgroundColor ?? '#f1f5f9');
20
20
  showLabel = computed(() => this.config().showLabel ?? true);
21
21
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiComponentsProcessBarCircleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
22
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LibsUiComponentsProcessBarCircleComponent, isStandalone: true, selector: "libs_ui-components-process_bar-circle", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div\n [class]=\"config().classInclude\"\n [style.width.px]=\"size()\"\n [style.height.px]=\"size()\"\n class=\"relative flex items-center justify-center\">\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n class=\"transform -rotate-90\">\n <!-- Background Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"backgroundColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\" />\n <!-- Progress Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n [attr.stroke-dasharray]=\"circumference()\"\n [attr.stroke-dashoffset]=\"offset()\"\n stroke-linecap=\"round\"\n fill=\"transparent\"\n class=\"transition-all duration-300 ease-in-out\" />\n </svg>\n\n <!-- Label -->\n @if (showLabel()) {\n <div class=\"absolute inset-0 flex items-center justify-center\">\n <span\n [class]=\"config().labelClass\"\n class=\"text-sm font-semibold text-gray-700\">\n {{ config().percent }}%\n </span>\n </div>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
22
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LibsUiComponentsProcessBarCircleComponent, isStandalone: true, selector: "libs_ui-components-process_bar-circle", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div\n [class]=\"config().classInclude\"\n [style.width.px]=\"size()\"\n [style.height.px]=\"size()\"\n class=\"relative flex items-center justify-center\">\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n class=\"transform -rotate-90\">\n <!-- Background Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"backgroundColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\" />\n <!-- Progress Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n [attr.stroke-dasharray]=\"circumference()\"\n [attr.stroke-dashoffset]=\"offset()\"\n stroke-linecap=\"round\"\n fill=\"transparent\"\n class=\"transition-all duration-300 ease-in-out\" />\n </svg>\n\n <!-- Label -->\n @if (showLabel()) {\n <div class=\"absolute inset-0 flex items-center justify-center\">\n <span\n [class]=\"config().labelClass\"\n class=\"text-sm font-semibold text-gray-700\">\n {{ config().percent }}%\n </span>\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
23
23
  }
24
24
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiComponentsProcessBarCircleComponent, decorators: [{
25
25
  type: Component,
@@ -1 +1 @@
1
- {"version":3,"file":"libs-ui-components-process-bar-circle.mjs","sources":["../../../../../../libs-ui/components/process-bar/circle/src/process-bar-circle.component.ts","../../../../../../libs-ui/components/process-bar/circle/src/process-bar-circle.component.html","../../../../../../libs-ui/components/process-bar/circle/src/libs-ui-components-process-bar-circle.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { IProcessBarCircleInterface } from './process-bar-circle.interface';\n\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: 'libs_ui-components-process_bar-circle',\n standalone: true,\n imports: [CommonModule],\n templateUrl: './process-bar-circle.component.html',\n styleUrls: ['./process-bar-circle.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class LibsUiComponentsProcessBarCircleComponent {\n /** Cấu hình cho vòng tiến trình */\n readonly config = input.required<IProcessBarCircleInterface>();\n\n // Computed values for SVG calculations\n protected size = computed(() => this.config().size ?? 100);\n protected strokeWidth = computed(() => this.config().strokeWidth ?? 8);\n protected radius = computed(() => (this.size() - this.strokeWidth()) / 2);\n protected center = computed(() => this.size() / 2);\n protected circumference = computed(() => 2 * Math.PI * this.radius());\n protected offset = computed(() => {\n const p = Math.max(0, Math.min(100, this.config().percent));\n return this.circumference() - (p / 100) * this.circumference();\n });\n\n protected color = computed(() => this.config().color ?? '#3b82f6');\n protected backgroundColor = computed(() => this.config().backgroundColor ?? '#f1f5f9');\n protected showLabel = computed(() => this.config().showLabel ?? true);\n}\n","<div\n [class]=\"config().classInclude\"\n [style.width.px]=\"size()\"\n [style.height.px]=\"size()\"\n class=\"relative flex items-center justify-center\">\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n class=\"transform -rotate-90\">\n <!-- Background Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"backgroundColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\" />\n <!-- Progress Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n [attr.stroke-dasharray]=\"circumference()\"\n [attr.stroke-dashoffset]=\"offset()\"\n stroke-linecap=\"round\"\n fill=\"transparent\"\n class=\"transition-all duration-300 ease-in-out\" />\n </svg>\n\n <!-- Label -->\n @if (showLabel()) {\n <div class=\"absolute inset-0 flex items-center justify-center\">\n <span\n [class]=\"config().labelClass\"\n class=\"text-sm font-semibold text-gray-700\">\n {{ config().percent }}%\n </span>\n </div>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;MAaa,yCAAyC,CAAA;;AAE3C,IAAA,MAAM,GAAG,KAAK,CAAC,QAAQ,EAA8B;;AAGpD,IAAA,IAAI,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC;AAChD,IAAA,WAAW,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,IAAI,CAAC,CAAC;IAC5D,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;AAC/D,IAAA,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AACxC,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;AAC3D,IAAA,MAAM,GAAG,QAAQ,CAAC,MAAK;QAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC;AAC3D,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,aAAa,EAAE;AAChE,IAAA,CAAC,CAAC;AAEQ,IAAA,KAAK,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,SAAS,CAAC;AACxD,IAAA,eAAe,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,eAAe,IAAI,SAAS,CAAC;AAC5E,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,SAAS,IAAI,IAAI,CAAC;wGAjB1D,yCAAyC,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;4FAAzC,yCAAyC,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uCAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECbtD,suCA0CA,EAAA,MAAA,EAAA,CAAA,EAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDlCY,YAAY,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAKX,yCAAyC,EAAA,UAAA,EAAA,CAAA;kBATrD,SAAS;+BAEE,uCAAuC,EAAA,UAAA,EACrC,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,CAAC,EAAA,eAAA,EAGN,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,suCAAA,EAAA;;;AEXjD;;AAEG;;;;"}
1
+ {"version":3,"file":"libs-ui-components-process-bar-circle.mjs","sources":["../../../../../../libs-ui/components/process-bar/circle/src/process-bar-circle.component.ts","../../../../../../libs-ui/components/process-bar/circle/src/process-bar-circle.component.html","../../../../../../libs-ui/components/process-bar/circle/src/libs-ui-components-process-bar-circle.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { IProcessBarCircleInterface } from './process-bar-circle.interface';\n\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: 'libs_ui-components-process_bar-circle',\n standalone: true,\n imports: [CommonModule],\n templateUrl: './process-bar-circle.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class LibsUiComponentsProcessBarCircleComponent {\n /** Cấu hình cho vòng tiến trình */\n readonly config = input.required<IProcessBarCircleInterface>();\n\n // Computed values for SVG calculations\n protected size = computed(() => this.config().size ?? 100);\n protected strokeWidth = computed(() => this.config().strokeWidth ?? 8);\n protected radius = computed(() => (this.size() - this.strokeWidth()) / 2);\n protected center = computed(() => this.size() / 2);\n protected circumference = computed(() => 2 * Math.PI * this.radius());\n protected offset = computed(() => {\n const p = Math.max(0, Math.min(100, this.config().percent));\n return this.circumference() - (p / 100) * this.circumference();\n });\n\n protected color = computed(() => this.config().color ?? '#3b82f6');\n protected backgroundColor = computed(() => this.config().backgroundColor ?? '#f1f5f9');\n protected showLabel = computed(() => this.config().showLabel ?? true);\n}\n","<div\n [class]=\"config().classInclude\"\n [style.width.px]=\"size()\"\n [style.height.px]=\"size()\"\n class=\"relative flex items-center justify-center\">\n <svg\n [attr.width]=\"size()\"\n [attr.height]=\"size()\"\n class=\"transform -rotate-90\">\n <!-- Background Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"backgroundColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"transparent\" />\n <!-- Progress Circle -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"radius()\"\n [attr.stroke]=\"color()\"\n [attr.stroke-width]=\"strokeWidth()\"\n [attr.stroke-dasharray]=\"circumference()\"\n [attr.stroke-dashoffset]=\"offset()\"\n stroke-linecap=\"round\"\n fill=\"transparent\"\n class=\"transition-all duration-300 ease-in-out\" />\n </svg>\n\n <!-- Label -->\n @if (showLabel()) {\n <div class=\"absolute inset-0 flex items-center justify-center\">\n <span\n [class]=\"config().labelClass\"\n class=\"text-sm font-semibold text-gray-700\">\n {{ config().percent }}%\n </span>\n </div>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;MAYa,yCAAyC,CAAA;;AAE3C,IAAA,MAAM,GAAG,KAAK,CAAC,QAAQ,EAA8B,CAAC;;AAGrD,IAAA,IAAI,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;AACjD,IAAA,WAAW,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IAC7D,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC;AAChE,IAAA,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AACzC,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AAC5D,IAAA,MAAM,GAAG,QAAQ,CAAC,MAAK;QAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5D,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;AACjE,KAAC,CAAC,CAAC;AAEO,IAAA,KAAK,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC;AACzD,IAAA,eAAe,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,eAAe,IAAI,SAAS,CAAC,CAAC;AAC7E,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC;wGAjB3D,yCAAyC,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;4FAAzC,yCAAyC,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uCAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECZtD,suCA0CA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDlCY,YAAY,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;4FAIX,yCAAyC,EAAA,UAAA,EAAA,CAAA;kBARrD,SAAS;+BAEE,uCAAuC,EAAA,UAAA,EACrC,IAAI,EACP,OAAA,EAAA,CAAC,YAAY,CAAC,EAAA,eAAA,EAEN,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,suCAAA,EAAA,CAAA;;;AEVjD;;AAEG;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libs-ui/components-process-bar-circle",
3
- "version": "0.2.356-37",
3
+ "version": "0.2.357-22",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=18.0.0",
6
6
  "@angular/core": ">=18.0.0"