@libs-ui/components-process-bar-circle 0.2.356-37

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 ADDED
@@ -0,0 +1,124 @@
1
+ # @libs-ui/components-process-bar-circle
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ị.
4
+
5
+ ## Giới thiệu
6
+
7
+ `LibsUiComponentsProcessBarCircleComponent` là một giải pháp hiển thị tiến trình dạng SVG hình tròn. Nó nhẹ, có khả năng mở rộng cao và 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).
8
+
9
+ ### Tính năng
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 cơ chế phản ứng mới của Angular.
17
+
18
+ ## Khi nào sử dụng
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.
24
+
25
+ ## Cài đặt
26
+
27
+ ```bash
28
+ # npm
29
+ npm install @libs-ui/components-process-bar-circle
30
+
31
+ # yarn
32
+ yarn add @libs-ui/components-process-bar-circle
33
+ ```
34
+
35
+ ## Import
36
+
37
+ ```typescript
38
+ import { LibsUiComponentsProcessBarCircleComponent } from '@libs-ui/components-process-bar-circle';
39
+
40
+ @Component({
41
+ standalone: true,
42
+ imports: [LibsUiComponentsProcessBarCircleComponent],
43
+ // ...
44
+ })
45
+ export class YourComponent {}
46
+ ```
47
+
48
+ ## Ví dụ
49
+
50
+ ### Cơ bản
51
+
52
+ ```html
53
+ <libs_ui-components-process_bar-circle [config]="{ percent: 75 }" />
54
+ ```
55
+
56
+ ### Tùy chỉnh kích thước và màu sắc
57
+
58
+ ```html
59
+ <libs_ui-components-process_bar-circle
60
+ [config]="{
61
+ percent: 45,
62
+ size: 150,
63
+ strokeWidth: 12,
64
+ color: '#10b981',
65
+ backgroundColor: '#ecfdf5',
66
+ labelClass: 'text-xl font-bold text-emerald-600'
67
+ }" />
68
+ ```
69
+
70
+ ## API
71
+
72
+ ### libs_ui-components-process_bar-circle
73
+
74
+ #### Inputs
75
+
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. |
79
+
80
+ ## Types & Interfaces
81
+
82
+ ```typescript
83
+ export interface IProcessBarCircleInterface {
84
+ /** Phần trăm hoàn thành (0-100) */
85
+ percent: number;
86
+ /** Kích thước của vòng tròn (px), mặc định 100 */
87
+ size?: number;
88
+ /** Độ dày của đường tiến trình (px), mặc định 8 */
89
+ strokeWidth?: number;
90
+ /** Màu của đường tiến trình, mặc định #3b82f6 (Blue 500) */
91
+ color?: string;
92
+ /** Màu nền của vòng tròn, mặc định #f1f5f9 (Slate 100) */
93
+ backgroundColor?: string;
94
+ /** Có hiển thị chữ số phần trăm ở giữa không, mặc định true */
95
+ showLabel?: boolean;
96
+ /** Class CSS tùy chỉnh cho nhãn phần trăm */
97
+ labelClass?: string;
98
+ /** Class CSS tùy chỉnh cho container */
99
+ classInclude?: string;
100
+ }
101
+ ```
102
+
103
+ ## Hidden Logic
104
+
105
+ ### 1. SVG Calculations
106
+
107
+ Component tự động tính toán các thuộc tính SVG (`stroke-dasharray`, `stroke-dashoffset`) dựa trên `size` và `strokeWidth` để đảm bảo vòng tròn luôn căn giữa và không bị cắt mất viền khi thay dổi độ dày.
108
+
109
+ ### 2. Transition Smoothing
110
+
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 mà mà không cần đến logic JavaScript phức tạp.
112
+
113
+ ## Demo
114
+
115
+ ```bash
116
+ # Xem demo trực tiếp
117
+ npx nx serve core-ui
118
+ ```
119
+
120
+ Truy cập: `http://localhost:4200/process-bar/circle`
121
+
122
+ ## License
123
+
124
+ MIT
@@ -0,0 +1,3 @@
1
+ export * from './process-bar-circle.component';
2
+ export * from './process-bar-circle.interface';
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvcHJvY2Vzcy1iYXIvY2lyY2xlL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGdDQUFnQyxDQUFDO0FBQy9DLGNBQWMsZ0NBQWdDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgKiBmcm9tICcuL3Byb2Nlc3MtYmFyLWNpcmNsZS5jb21wb25lbnQnO1xuZXhwb3J0ICogZnJvbSAnLi9wcm9jZXNzLWJhci1jaXJjbGUuaW50ZXJmYWNlJztcbiJdfQ==
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ export * from './index';
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlicy11aS1jb21wb25lbnRzLXByb2Nlc3MtYmFyLWNpcmNsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvY29tcG9uZW50cy9wcm9jZXNzLWJhci9jaXJjbGUvc3JjL2xpYnMtdWktY29tcG9uZW50cy1wcm9jZXNzLWJhci1jaXJjbGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLFNBQVMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2VuZXJhdGVkIGJ1bmRsZSBpbmRleC4gRG8gbm90IGVkaXQuXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9pbmRleCc7XG4iXX0=
@@ -0,0 +1,27 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
3
+ import * as i0 from "@angular/core";
4
+ export class LibsUiComponentsProcessBarCircleComponent {
5
+ /** Cấu hình cho vòng tiến trình */
6
+ config = input.required();
7
+ // Computed values for SVG calculations
8
+ size = computed(() => this.config().size ?? 100);
9
+ strokeWidth = computed(() => this.config().strokeWidth ?? 8);
10
+ radius = computed(() => (this.size() - this.strokeWidth()) / 2);
11
+ center = computed(() => this.size() / 2);
12
+ circumference = computed(() => 2 * Math.PI * this.radius());
13
+ offset = computed(() => {
14
+ const p = Math.max(0, Math.min(100, this.config().percent));
15
+ return this.circumference() - (p / 100) * this.circumference();
16
+ });
17
+ color = computed(() => this.config().color ?? '#3b82f6');
18
+ backgroundColor = computed(() => this.config().backgroundColor ?? '#f1f5f9');
19
+ showLabel = computed(() => this.config().showLabel ?? true);
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 });
22
+ }
23
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiComponentsProcessBarCircleComponent, decorators: [{
24
+ type: Component,
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
+ }] });
27
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvY2Vzcy1iYXItY2lyY2xlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvY29tcG9uZW50cy9wcm9jZXNzLWJhci9jaXJjbGUvc3JjL3Byb2Nlc3MtYmFyLWNpcmNsZS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9saWJzLXVpL2NvbXBvbmVudHMvcHJvY2Vzcy1iYXIvY2lyY2xlL3NyYy9wcm9jZXNzLWJhci1jaXJjbGUuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQzs7QUFZcEYsTUFBTSxPQUFPLHlDQUF5QztJQUNwRCxtQ0FBbUM7SUFDMUIsTUFBTSxHQUFHLEtBQUssQ0FBQyxRQUFRLEVBQThCLENBQUM7SUFFL0QsdUNBQXVDO0lBQzdCLElBQUksR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLElBQUksSUFBSSxHQUFHLENBQUMsQ0FBQztJQUNqRCxXQUFXLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxXQUFXLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDN0QsTUFBTSxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNoRSxNQUFNLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUN6QyxhQUFhLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzVELE1BQU0sR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQy9CLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQzVELE9BQU8sSUFBSSxDQUFDLGFBQWEsRUFBRSxHQUFHLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztJQUNqRSxDQUFDLENBQUMsQ0FBQztJQUVPLEtBQUssR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQUssSUFBSSxTQUFTLENBQUMsQ0FBQztJQUN6RCxlQUFlLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxlQUFlLElBQUksU0FBUyxDQUFDLENBQUM7SUFDN0UsU0FBUyxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxDQUFDO3dHQWpCM0QseUNBQXlDOzRGQUF6Qyx5Q0FBeUMsdU9DYnRELHN1Q0EwQ0EseUREbENZLFlBQVk7OzRGQUtYLHlDQUF5QztrQkFUckQsU0FBUzsrQkFFRSx1Q0FBdUMsY0FDckMsSUFBSSxXQUNQLENBQUMsWUFBWSxDQUFDLG1CQUdOLHVCQUF1QixDQUFDLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgY29tcHV0ZWQsIGlucHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBJUHJvY2Vzc0JhckNpcmNsZUludGVyZmFjZSB9IGZyb20gJy4vcHJvY2Vzcy1iYXItY2lyY2xlLmludGVyZmFjZSc7XG5cbkBDb21wb25lbnQoe1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQGFuZ3VsYXItZXNsaW50L2NvbXBvbmVudC1zZWxlY3RvclxuICBzZWxlY3RvcjogJ2xpYnNfdWktY29tcG9uZW50cy1wcm9jZXNzX2Jhci1jaXJjbGUnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbQ29tbW9uTW9kdWxlXSxcbiAgdGVtcGxhdGVVcmw6ICcuL3Byb2Nlc3MtYmFyLWNpcmNsZS5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3Byb2Nlc3MtYmFyLWNpcmNsZS5jb21wb25lbnQuY3NzJ10sXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxufSlcbmV4cG9ydCBjbGFzcyBMaWJzVWlDb21wb25lbnRzUHJvY2Vzc0JhckNpcmNsZUNvbXBvbmVudCB7XG4gIC8qKiBD4bqldSBow6xuaCBjaG8gdsOybmcgdGnhur9uIHRyw6xuaCAqL1xuICByZWFkb25seSBjb25maWcgPSBpbnB1dC5yZXF1aXJlZDxJUHJvY2Vzc0JhckNpcmNsZUludGVyZmFjZT4oKTtcblxuICAvLyBDb21wdXRlZCB2YWx1ZXMgZm9yIFNWRyBjYWxjdWxhdGlvbnNcbiAgcHJvdGVjdGVkIHNpemUgPSBjb21wdXRlZCgoKSA9PiB0aGlzLmNvbmZpZygpLnNpemUgPz8gMTAwKTtcbiAgcHJvdGVjdGVkIHN0cm9rZVdpZHRoID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jb25maWcoKS5zdHJva2VXaWR0aCA/PyA4KTtcbiAgcHJvdGVjdGVkIHJhZGl1cyA9IGNvbXB1dGVkKCgpID0+ICh0aGlzLnNpemUoKSAtIHRoaXMuc3Ryb2tlV2lkdGgoKSkgLyAyKTtcbiAgcHJvdGVjdGVkIGNlbnRlciA9IGNvbXB1dGVkKCgpID0+IHRoaXMuc2l6ZSgpIC8gMik7XG4gIHByb3RlY3RlZCBjaXJjdW1mZXJlbmNlID0gY29tcHV0ZWQoKCkgPT4gMiAqIE1hdGguUEkgKiB0aGlzLnJhZGl1cygpKTtcbiAgcHJvdGVjdGVkIG9mZnNldCA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBjb25zdCBwID0gTWF0aC5tYXgoMCwgTWF0aC5taW4oMTAwLCB0aGlzLmNvbmZpZygpLnBlcmNlbnQpKTtcbiAgICByZXR1cm4gdGhpcy5jaXJjdW1mZXJlbmNlKCkgLSAocCAvIDEwMCkgKiB0aGlzLmNpcmN1bWZlcmVuY2UoKTtcbiAgfSk7XG5cbiAgcHJvdGVjdGVkIGNvbG9yID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jb25maWcoKS5jb2xvciA/PyAnIzNiODJmNicpO1xuICBwcm90ZWN0ZWQgYmFja2dyb3VuZENvbG9yID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jb25maWcoKS5iYWNrZ3JvdW5kQ29sb3IgPz8gJyNmMWY1ZjknKTtcbiAgcHJvdGVjdGVkIHNob3dMYWJlbCA9IGNvbXB1dGVkKCgpID0+IHRoaXMuY29uZmlnKCkuc2hvd0xhYmVsID8/IHRydWUpO1xufVxuIiwiPGRpdlxuICBbY2xhc3NdPVwiY29uZmlnKCkuY2xhc3NJbmNsdWRlXCJcbiAgW3N0eWxlLndpZHRoLnB4XT1cInNpemUoKVwiXG4gIFtzdHlsZS5oZWlnaHQucHhdPVwic2l6ZSgpXCJcbiAgY2xhc3M9XCJyZWxhdGl2ZSBmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlclwiPlxuICA8c3ZnXG4gICAgW2F0dHIud2lkdGhdPVwic2l6ZSgpXCJcbiAgICBbYXR0ci5oZWlnaHRdPVwic2l6ZSgpXCJcbiAgICBjbGFzcz1cInRyYW5zZm9ybSAtcm90YXRlLTkwXCI+XG4gICAgPCEtLSBCYWNrZ3JvdW5kIENpcmNsZSAtLT5cbiAgICA8Y2lyY2xlXG4gICAgICBbYXR0ci5jeF09XCJjZW50ZXIoKVwiXG4gICAgICBbYXR0ci5jeV09XCJjZW50ZXIoKVwiXG4gICAgICBbYXR0ci5yXT1cInJhZGl1cygpXCJcbiAgICAgIFthdHRyLnN0cm9rZV09XCJiYWNrZ3JvdW5kQ29sb3IoKVwiXG4gICAgICBbYXR0ci5zdHJva2Utd2lkdGhdPVwic3Ryb2tlV2lkdGgoKVwiXG4gICAgICBmaWxsPVwidHJhbnNwYXJlbnRcIiAvPlxuICAgIDwhLS0gUHJvZ3Jlc3MgQ2lyY2xlIC0tPlxuICAgIDxjaXJjbGVcbiAgICAgIFthdHRyLmN4XT1cImNlbnRlcigpXCJcbiAgICAgIFthdHRyLmN5XT1cImNlbnRlcigpXCJcbiAgICAgIFthdHRyLnJdPVwicmFkaXVzKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlXT1cImNvbG9yKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlLXdpZHRoXT1cInN0cm9rZVdpZHRoKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlLWRhc2hhcnJheV09XCJjaXJjdW1mZXJlbmNlKClcIlxuICAgICAgW2F0dHIuc3Ryb2tlLWRhc2hvZmZzZXRdPVwib2Zmc2V0KClcIlxuICAgICAgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiXG4gICAgICBmaWxsPVwidHJhbnNwYXJlbnRcIlxuICAgICAgY2xhc3M9XCJ0cmFuc2l0aW9uLWFsbCBkdXJhdGlvbi0zMDAgZWFzZS1pbi1vdXRcIiAvPlxuICA8L3N2Zz5cblxuICA8IS0tIExhYmVsIC0tPlxuICBAaWYgKHNob3dMYWJlbCgpKSB7XG4gICAgPGRpdiBjbGFzcz1cImFic29sdXRlIGluc2V0LTAgZmxleCBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXJcIj5cbiAgICAgIDxzcGFuXG4gICAgICAgIFtjbGFzc109XCJjb25maWcoKS5sYWJlbENsYXNzXCJcbiAgICAgICAgY2xhc3M9XCJ0ZXh0LXNtIGZvbnQtc2VtaWJvbGQgdGV4dC1ncmF5LTcwMFwiPlxuICAgICAgICB7eyBjb25maWcoKS5wZXJjZW50IH19JVxuICAgICAgPC9zcGFuPlxuICAgIDwvZGl2PlxuICB9XG48L2Rpdj5cbiJdfQ==
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvY2Vzcy1iYXItY2lyY2xlLmludGVyZmFjZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMtdWkvY29tcG9uZW50cy9wcm9jZXNzLWJhci9jaXJjbGUvc3JjL3Byb2Nlc3MtYmFyLWNpcmNsZS5pbnRlcmZhY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBpbnRlcmZhY2UgSVByb2Nlc3NCYXJDaXJjbGVJbnRlcmZhY2Uge1xuICAvKiogUGjhuqduIHRyxINtIGhvw6BuIHRow6BuaCAoMC0xMDApICovXG4gIHBlcmNlbnQ6IG51bWJlcjtcbiAgLyoqIEvDrWNoIHRoxrDhu5tjIGPhu6dhIHbDsm5nIHRyw7JuIChweCksIG3hurdjIMSR4buLbmggMTAwICovXG4gIHNpemU/OiBudW1iZXI7XG4gIC8qKiDEkOG7mSBkw6B5IGPhu6dhIMSRxrDhu51uZyB0aeG6v24gdHLDrG5oIChweCksIG3hurdjIMSR4buLbmggOCAqL1xuICBzdHJva2VXaWR0aD86IG51bWJlcjtcbiAgLyoqIE3DoHUgY+G7p2EgxJHGsOG7nW5nIHRp4bq/biB0csOsbmgsIG3hurdjIMSR4buLbmggIzAwN2JmZiAqL1xuICBjb2xvcj86IHN0cmluZztcbiAgLyoqIE3DoHUgbuG7gW4gY+G7p2EgdsOybmcgdHLDsm4sIG3hurdjIMSR4buLbmggI2Y4ZjlmYSAqL1xuICBiYWNrZ3JvdW5kQ29sb3I/OiBzdHJpbmc7XG4gIC8qKiBDw7MgaGnhu4NuIHRo4buLIGNo4buvIHPhu5EgcGjhuqduIHRyxINtIOG7nyBnaeG7r2Ega2jDtG5nLCBt4bq3YyDEkeG7i25oIHRydWUgKi9cbiAgc2hvd0xhYmVsPzogYm9vbGVhbjtcbiAgLyoqIENsYXNzIENTUyB0w7l5IGNo4buJbmggY2hvIG5ow6NuIHBo4bqnbiB0csSDbSAqL1xuICBsYWJlbENsYXNzPzogc3RyaW5nO1xuICAvKiogQ2xhc3MgQ1NTIHTDuXkgY2jhu4luaCBjaG8gY29tcG9uZW50ICovXG4gIGNsYXNzSW5jbHVkZT86IHN0cmluZztcbn1cbiJdfQ==
@@ -0,0 +1,34 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
4
+
5
+ class LibsUiComponentsProcessBarCircleComponent {
6
+ /** Cấu hình cho vòng tiến trình */
7
+ config = input.required();
8
+ // Computed values for SVG calculations
9
+ size = computed(() => this.config().size ?? 100);
10
+ strokeWidth = computed(() => this.config().strokeWidth ?? 8);
11
+ radius = computed(() => (this.size() - this.strokeWidth()) / 2);
12
+ center = computed(() => this.size() / 2);
13
+ circumference = computed(() => 2 * Math.PI * this.radius());
14
+ offset = computed(() => {
15
+ const p = Math.max(0, Math.min(100, this.config().percent));
16
+ return this.circumference() - (p / 100) * this.circumference();
17
+ });
18
+ color = computed(() => this.config().color ?? '#3b82f6');
19
+ backgroundColor = computed(() => this.config().backgroundColor ?? '#f1f5f9');
20
+ showLabel = computed(() => this.config().showLabel ?? true);
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 });
23
+ }
24
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiComponentsProcessBarCircleComponent, decorators: [{
25
+ type: Component,
26
+ 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" }]
27
+ }] });
28
+
29
+ /**
30
+ * Generated bundle index. Do not edit.
31
+ */
32
+
33
+ export { LibsUiComponentsProcessBarCircleComponent };
34
+ //# sourceMappingURL=libs-ui-components-process-bar-circle.mjs.map
@@ -0,0 +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;;;;"}
package/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './process-bar-circle.component';
2
+ export * from './process-bar-circle.interface';
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@libs-ui/components-process-bar-circle",
3
+ "version": "0.2.356-37",
4
+ "peerDependencies": {
5
+ "@angular/common": ">=18.0.0",
6
+ "@angular/core": ">=18.0.0"
7
+ },
8
+ "sideEffects": false,
9
+ "module": "fesm2022/libs-ui-components-process-bar-circle.mjs",
10
+ "typings": "index.d.ts",
11
+ "exports": {
12
+ "./package.json": {
13
+ "default": "./package.json"
14
+ },
15
+ ".": {
16
+ "types": "./index.d.ts",
17
+ "esm2022": "./esm2022/libs-ui-components-process-bar-circle.mjs",
18
+ "esm": "./esm2022/libs-ui-components-process-bar-circle.mjs",
19
+ "default": "./fesm2022/libs-ui-components-process-bar-circle.mjs"
20
+ }
21
+ },
22
+ "dependencies": {
23
+ "tslib": "^2.3.0"
24
+ }
25
+ }
@@ -0,0 +1,17 @@
1
+ import { IProcessBarCircleInterface } from './process-bar-circle.interface';
2
+ import * as i0 from "@angular/core";
3
+ export declare class LibsUiComponentsProcessBarCircleComponent {
4
+ /** Cấu hình cho vòng tiến trình */
5
+ readonly config: import("@angular/core").InputSignal<IProcessBarCircleInterface>;
6
+ protected size: import("@angular/core").Signal<number>;
7
+ protected strokeWidth: import("@angular/core").Signal<number>;
8
+ protected radius: import("@angular/core").Signal<number>;
9
+ protected center: import("@angular/core").Signal<number>;
10
+ protected circumference: import("@angular/core").Signal<number>;
11
+ protected offset: import("@angular/core").Signal<number>;
12
+ protected color: import("@angular/core").Signal<string>;
13
+ protected backgroundColor: import("@angular/core").Signal<string>;
14
+ protected showLabel: import("@angular/core").Signal<boolean>;
15
+ static ɵfac: i0.ɵɵFactoryDeclaration<LibsUiComponentsProcessBarCircleComponent, never>;
16
+ static ɵcmp: i0.ɵɵComponentDeclaration<LibsUiComponentsProcessBarCircleComponent, "libs_ui-components-process_bar-circle", never, { "config": { "alias": "config"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
17
+ }
@@ -0,0 +1,18 @@
1
+ export interface IProcessBarCircleInterface {
2
+ /** Phần trăm hoàn thành (0-100) */
3
+ percent: number;
4
+ /** Kích thước của vòng tròn (px), mặc định 100 */
5
+ size?: number;
6
+ /** Độ dày của đường tiến trình (px), mặc định 8 */
7
+ strokeWidth?: number;
8
+ /** Màu của đường tiến trình, mặc định #007bff */
9
+ color?: string;
10
+ /** Màu nền của vòng tròn, mặc định #f8f9fa */
11
+ backgroundColor?: string;
12
+ /** Có hiển thị chữ số phần trăm ở giữa không, mặc định true */
13
+ showLabel?: boolean;
14
+ /** Class CSS tùy chỉnh cho nhãn phần trăm */
15
+ labelClass?: string;
16
+ /** Class CSS tùy chỉnh cho component */
17
+ classInclude?: string;
18
+ }