@libs-ui/components-buttons-button 0.2.356-9 → 0.2.357-1
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,45 +1,49 @@
|
|
|
1
1
|
# @libs-ui/components-buttons-button
|
|
2
2
|
|
|
3
|
-
> Component Button đa năng với nhiều kiểu dáng, kích thước và tích hợp popover.
|
|
3
|
+
> Component Button đa năng với nhiều kiểu dáng, kích thước, icon và tích hợp popover/spinner.
|
|
4
4
|
|
|
5
5
|
## Giới thiệu
|
|
6
6
|
|
|
7
|
-
`LibsUiComponentsButtonsButtonComponent` là một
|
|
7
|
+
`LibsUiComponentsButtonsButtonComponent` là một Angular Standalone Component được thiết kế để hiển thị nút bấm với đầy đủ tính năng: hỗ trợ 20+ kiểu màu sắc (primary, secondary, outline, danger, link...), 4 kích thước, loading spinner tích hợp, icon trái/phải, chế độ icon-only, custom color qua `buttonCustom`, và tích hợp Popover. Component sử dụng Angular Signals + `ChangeDetectionStrategy.OnPush` để tối ưu hiệu năng.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Tính năng
|
|
10
10
|
|
|
11
|
-
- ✅
|
|
12
|
-
- ✅ 4 kích thước: large
|
|
13
|
-
- ✅
|
|
14
|
-
- ✅
|
|
15
|
-
- ✅
|
|
16
|
-
- ✅ Custom
|
|
17
|
-
- ✅
|
|
18
|
-
- ✅
|
|
19
|
-
- ✅
|
|
11
|
+
- ✅ 20+ kiểu button: primary, secondary, third, outline, danger, green, violet, link và các biến thể
|
|
12
|
+
- ✅ 4 kích thước: `large`, `medium`, `small`, `smaller`
|
|
13
|
+
- ✅ Loading state với spinner tích hợp (`isPending`)
|
|
14
|
+
- ✅ Icon trái (`classIconLeft`), icon phải (`classIconRight`) hoặc icon-only (`iconOnlyType`)
|
|
15
|
+
- ✅ Hình ảnh bên trái (`imageLeft`)
|
|
16
|
+
- ✅ Custom color thông qua `buttonCustom` (khi `type = 'button-custom'` hoặc `'button-link-custom'`)
|
|
17
|
+
- ✅ Tích hợp Popover (hover/click) với đầy đủ control API
|
|
18
|
+
- ✅ Trạng thái disabled với ngăn pointer events tùy chọn
|
|
19
|
+
- ✅ Trạng thái active (`isActive`)
|
|
20
|
+
- ✅ Hỗ trợ Enter key trên document (`isHandlerEnterDocumentClickButton`)
|
|
21
|
+
- ✅ Angular Signals + `ChangeDetectionStrategy.OnPush`
|
|
22
|
+
- ✅ Standalone Component, không cần NgModule
|
|
20
23
|
|
|
21
24
|
## Khi nào sử dụng
|
|
22
25
|
|
|
23
|
-
- Khi cần
|
|
24
|
-
- Khi cần hiển thị trạng thái loading trong
|
|
26
|
+
- Khi cần nút bấm để kích hoạt hành động (submit form, mở dialog, điều hướng...)
|
|
27
|
+
- Khi cần hiển thị trạng thái loading trong lúc xử lý async operation
|
|
25
28
|
- Khi cần button với popover tooltip hoặc menu dropdown
|
|
26
|
-
- Khi cần button
|
|
27
|
-
-
|
|
29
|
+
- Khi cần button chỉ có icon, không có label
|
|
30
|
+
- Khi cần tùy chỉnh màu sắc button theo brand riêng của feature
|
|
28
31
|
|
|
29
32
|
## Cài đặt
|
|
30
33
|
|
|
31
34
|
```bash
|
|
32
|
-
# npm
|
|
33
35
|
npm install @libs-ui/components-buttons-button
|
|
34
|
-
|
|
35
|
-
# yarn
|
|
36
|
-
yarn add @libs-ui/components-buttons-button
|
|
37
36
|
```
|
|
38
37
|
|
|
39
38
|
## Import
|
|
40
39
|
|
|
41
40
|
```typescript
|
|
42
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
LibsUiComponentsButtonsButtonComponent,
|
|
43
|
+
IButton,
|
|
44
|
+
TYPE_BUTTON,
|
|
45
|
+
TYPE_SIZE_BUTTON,
|
|
46
|
+
} from '@libs-ui/components-buttons-button';
|
|
43
47
|
|
|
44
48
|
@Component({
|
|
45
49
|
standalone: true,
|
|
@@ -49,206 +53,405 @@ import { LibsUiComponentsButtonsButtonComponent, TYPE_BUTTON, TYPE_SIZE_BUTTON }
|
|
|
49
53
|
export class YourComponent {}
|
|
50
54
|
```
|
|
51
55
|
|
|
52
|
-
## Ví dụ
|
|
56
|
+
## Ví dụ sử dụng
|
|
53
57
|
|
|
54
|
-
### Basic
|
|
58
|
+
### Basic — Nút bấm đơn giản
|
|
55
59
|
|
|
56
60
|
```html
|
|
57
61
|
<libs_ui-components-buttons-button
|
|
58
|
-
label="
|
|
59
|
-
(outClick)="
|
|
62
|
+
label="Lưu"
|
|
63
|
+
(outClick)="handlerSave($event)" />
|
|
60
64
|
```
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
```typescript
|
|
67
|
+
import { Component } from '@angular/core';
|
|
68
|
+
import { LibsUiComponentsButtonsButtonComponent } from '@libs-ui/components-buttons-button';
|
|
69
|
+
|
|
70
|
+
@Component({
|
|
71
|
+
selector: 'app-example',
|
|
72
|
+
standalone: true,
|
|
73
|
+
imports: [LibsUiComponentsButtonsButtonComponent],
|
|
74
|
+
templateUrl: './example.component.html',
|
|
75
|
+
})
|
|
76
|
+
export class ExampleComponent {
|
|
77
|
+
handlerSave(event: Event): void {
|
|
78
|
+
event.stopPropagation();
|
|
79
|
+
// xử lý lưu dữ liệu
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Button Types — Các kiểu màu sắc
|
|
63
85
|
|
|
64
86
|
```html
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
label="
|
|
68
|
-
|
|
69
|
-
|
|
87
|
+
<!-- Solid buttons -->
|
|
88
|
+
<libs_ui-components-buttons-button type="button-primary" label="Primary" (outClick)="handlerClick($event)" />
|
|
89
|
+
<libs_ui-components-buttons-button type="button-secondary" label="Secondary" (outClick)="handlerClick($event)" />
|
|
90
|
+
<libs_ui-components-buttons-button type="button-third" label="Third" (outClick)="handlerClick($event)" />
|
|
91
|
+
<libs_ui-components-buttons-button type="button-outline" label="Outline" (outClick)="handlerClick($event)" />
|
|
92
|
+
<libs_ui-components-buttons-button type="button-danger-high" label="Danger" (outClick)="handlerDelete($event)" />
|
|
93
|
+
<libs_ui-components-buttons-button type="button-danger-low" label="Cảnh báo" (outClick)="handlerWarning($event)" />
|
|
94
|
+
<libs_ui-components-buttons-button type="button-green" label="Xác nhận" (outClick)="handlerConfirm($event)" />
|
|
95
|
+
<libs_ui-components-buttons-button type="button-violet" label="Violet" (outClick)="handlerClick($event)" />
|
|
96
|
+
|
|
97
|
+
<!-- Outline variants -->
|
|
98
|
+
<libs_ui-components-buttons-button type="button-outline-secondary" label="Outline Secondary" (outClick)="handlerClick($event)" />
|
|
99
|
+
<libs_ui-components-buttons-button type="button-outline-green" label="Outline Green" (outClick)="handlerClick($event)" />
|
|
100
|
+
<libs_ui-components-buttons-button type="button-outline-hover-danger" label="Hover Danger" (outClick)="handlerClick($event)" />
|
|
101
|
+
|
|
102
|
+
<!-- Link style buttons -->
|
|
103
|
+
<libs_ui-components-buttons-button type="button-link-primary" label="Xem thêm" (outClick)="handlerClick($event)" />
|
|
104
|
+
<libs_ui-components-buttons-button type="button-link-danger-high" label="Xóa" (outClick)="handlerDelete($event)" />
|
|
105
|
+
<libs_ui-components-buttons-button type="button-link-green" label="Xác nhận" (outClick)="handlerClick($event)" />
|
|
70
106
|
```
|
|
71
107
|
|
|
72
|
-
###
|
|
108
|
+
### Button Sizes — Các kích thước
|
|
73
109
|
|
|
74
110
|
```html
|
|
75
|
-
|
|
76
|
-
<libs_ui-components-buttons-button
|
|
77
|
-
|
|
78
|
-
|
|
111
|
+
<libs_ui-components-buttons-button [sizeButton]="'large'" label="Large Button" (outClick)="handlerClick($event)" />
|
|
112
|
+
<libs_ui-components-buttons-button [sizeButton]="'medium'" label="Medium Button" (outClick)="handlerClick($event)" />
|
|
113
|
+
<libs_ui-components-buttons-button [sizeButton]="'small'" label="Small Button" (outClick)="handlerClick($event)" />
|
|
114
|
+
<libs_ui-components-buttons-button [sizeButton]="'smaller'" label="Smaller Button" (outClick)="handlerClick($event)" />
|
|
115
|
+
```
|
|
79
116
|
|
|
80
|
-
|
|
81
|
-
<libs_ui-components-buttons-button
|
|
82
|
-
type="button-secondary"
|
|
83
|
-
label="Secondary" />
|
|
117
|
+
### Loading State — Trạng thái loading
|
|
84
118
|
|
|
85
|
-
|
|
119
|
+
```html
|
|
86
120
|
<libs_ui-components-buttons-button
|
|
87
|
-
|
|
88
|
-
|
|
121
|
+
label="Lưu thông tin"
|
|
122
|
+
[isPending]="isSaving()"
|
|
123
|
+
(outClick)="handlerSave($event)" />
|
|
124
|
+
```
|
|
89
125
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
126
|
+
```typescript
|
|
127
|
+
import { Component, signal } from '@angular/core';
|
|
128
|
+
import { LibsUiComponentsButtonsButtonComponent } from '@libs-ui/components-buttons-button';
|
|
129
|
+
|
|
130
|
+
@Component({
|
|
131
|
+
selector: 'app-form',
|
|
132
|
+
standalone: true,
|
|
133
|
+
imports: [LibsUiComponentsButtonsButtonComponent],
|
|
134
|
+
templateUrl: './form.component.html',
|
|
135
|
+
})
|
|
136
|
+
export class FormComponent {
|
|
137
|
+
protected isSaving = signal(false);
|
|
138
|
+
|
|
139
|
+
handlerSave(event: Event): void {
|
|
140
|
+
event.stopPropagation();
|
|
141
|
+
this.isSaving.set(true);
|
|
142
|
+
// gọi API...
|
|
143
|
+
// sau khi xong: this.isSaving.set(false);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
94
146
|
```
|
|
95
147
|
|
|
96
|
-
###
|
|
148
|
+
### With Icons — Button kèm icon
|
|
97
149
|
|
|
98
150
|
```html
|
|
151
|
+
<!-- Icon bên trái -->
|
|
99
152
|
<libs_ui-components-buttons-button
|
|
100
|
-
|
|
101
|
-
|
|
153
|
+
label="Thêm mới"
|
|
154
|
+
[classIconLeft]="'libs-ui-icon-add'"
|
|
155
|
+
(outClick)="handlerAdd($event)" />
|
|
102
156
|
|
|
157
|
+
<!-- Icon bên phải -->
|
|
103
158
|
<libs_ui-components-buttons-button
|
|
104
|
-
|
|
105
|
-
|
|
159
|
+
label="Tiếp theo"
|
|
160
|
+
[classIconRight]="'libs-ui-icon-arrow-right'"
|
|
161
|
+
(outClick)="handlerNext($event)" />
|
|
106
162
|
|
|
163
|
+
<!-- Cả hai icon -->
|
|
107
164
|
<libs_ui-components-buttons-button
|
|
108
|
-
|
|
109
|
-
|
|
165
|
+
label="Tải xuống"
|
|
166
|
+
[classIconLeft]="'libs-ui-icon-download-outline'"
|
|
167
|
+
[classIconRight]="'libs-ui-icon-arrow-down'"
|
|
168
|
+
(outClick)="handlerDownload($event)" />
|
|
110
169
|
|
|
170
|
+
<!-- Chỉ icon, không label -->
|
|
111
171
|
<libs_ui-components-buttons-button
|
|
112
|
-
[
|
|
113
|
-
|
|
172
|
+
[classIconLeft]="'libs-ui-icon-arrange'"
|
|
173
|
+
[iconOnlyType]="true"
|
|
174
|
+
(outClick)="handlerOpenSettings($event)" />
|
|
114
175
|
```
|
|
115
176
|
|
|
116
|
-
###
|
|
177
|
+
### Disabled State — Trạng thái vô hiệu hóa
|
|
117
178
|
|
|
118
179
|
```html
|
|
119
180
|
<libs_ui-components-buttons-button
|
|
120
|
-
label="
|
|
121
|
-
[
|
|
122
|
-
(outClick)="
|
|
181
|
+
label="Không thể bấm"
|
|
182
|
+
[disable]="true"
|
|
183
|
+
(outClick)="handlerClick($event)" />
|
|
184
|
+
|
|
185
|
+
<!-- Disable nhưng vẫn nhận pointer events (để show tooltip) -->
|
|
186
|
+
<libs_ui-components-buttons-button
|
|
187
|
+
label="Disabled nhưng có tooltip"
|
|
188
|
+
[disable]="true"
|
|
189
|
+
[ignorePointerEvent]="true"
|
|
190
|
+
(outClick)="handlerClick($event)" />
|
|
123
191
|
```
|
|
124
192
|
|
|
125
|
-
###
|
|
193
|
+
### Custom Color — Màu tùy chỉnh
|
|
126
194
|
|
|
127
195
|
```html
|
|
128
196
|
<libs_ui-components-buttons-button
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
197
|
+
type="button-custom"
|
|
198
|
+
label="Custom Brand Color"
|
|
199
|
+
[buttonCustom]="customButtonConfig"
|
|
200
|
+
(outClick)="handlerClick($event)" />
|
|
132
201
|
```
|
|
133
202
|
|
|
134
|
-
|
|
203
|
+
```typescript
|
|
204
|
+
import { Component } from '@angular/core';
|
|
205
|
+
import { LibsUiComponentsButtonsButtonComponent } from '@libs-ui/components-buttons-button';
|
|
206
|
+
import { IColorButton } from '@libs-ui/services-config-project';
|
|
207
|
+
|
|
208
|
+
@Component({
|
|
209
|
+
selector: 'app-example',
|
|
210
|
+
standalone: true,
|
|
211
|
+
imports: [LibsUiComponentsButtonsButtonComponent],
|
|
212
|
+
templateUrl: './example.component.html',
|
|
213
|
+
})
|
|
214
|
+
export class ExampleComponent {
|
|
215
|
+
protected readonly customButtonConfig: IColorButton = {
|
|
216
|
+
rootColor: '#6366f1',
|
|
217
|
+
configStepColor: {
|
|
218
|
+
background: 500,
|
|
219
|
+
color: -500,
|
|
220
|
+
background_hover: 600,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
handlerClick(event: Event): void {
|
|
225
|
+
event.stopPropagation();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### With Popover — Button kèm popover/tooltip
|
|
135
231
|
|
|
136
232
|
```html
|
|
137
233
|
<libs_ui-components-buttons-button
|
|
138
|
-
label="Hover
|
|
234
|
+
label="Hover để xem tooltip"
|
|
139
235
|
[popover]="{
|
|
140
236
|
type: 'text',
|
|
141
237
|
mode: 'hover',
|
|
142
238
|
config: {
|
|
143
|
-
content: '
|
|
144
|
-
width:
|
|
239
|
+
content: 'Đây là tooltip giải thích chức năng',
|
|
240
|
+
width: 220
|
|
145
241
|
}
|
|
146
|
-
}"
|
|
242
|
+
}"
|
|
243
|
+
(outClick)="handlerClick($event)"
|
|
244
|
+
(outPopoverEvent)="handlerPopoverEvent($event)" />
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { Component } from '@angular/core';
|
|
249
|
+
import { LibsUiComponentsButtonsButtonComponent } from '@libs-ui/components-buttons-button';
|
|
250
|
+
import { TYPE_POPOVER_EVENT } from '@libs-ui/components-popover';
|
|
251
|
+
|
|
252
|
+
@Component({
|
|
253
|
+
selector: 'app-example',
|
|
254
|
+
standalone: true,
|
|
255
|
+
imports: [LibsUiComponentsButtonsButtonComponent],
|
|
256
|
+
templateUrl: './example.component.html',
|
|
257
|
+
})
|
|
258
|
+
export class ExampleComponent {
|
|
259
|
+
handlerClick(event: Event): void {
|
|
260
|
+
event.stopPropagation();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
handlerPopoverEvent(event: TYPE_POPOVER_EVENT): void {
|
|
264
|
+
event.stopPropagation?.();
|
|
265
|
+
// event: 'show' | 'hide' | 'click' | 'remove'
|
|
266
|
+
}
|
|
267
|
+
}
|
|
147
268
|
```
|
|
148
269
|
|
|
149
|
-
###
|
|
270
|
+
### Image Left — Button kèm hình ảnh
|
|
150
271
|
|
|
151
272
|
```html
|
|
152
273
|
<libs_ui-components-buttons-button
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
rootColor: '#ff6b6b',
|
|
157
|
-
configStepColor: {
|
|
158
|
-
background: 500,
|
|
159
|
-
color: -500,
|
|
160
|
-
background_hover: 600
|
|
161
|
-
}
|
|
162
|
-
}" />
|
|
274
|
+
label="Đăng nhập với Google"
|
|
275
|
+
[imageLeft]="{ src: '/assets/icons/google.svg', classInclude: 'mr-[8px] w-[16px] h-[16px]' }"
|
|
276
|
+
(outClick)="handlerLoginWithGoogle($event)" />
|
|
163
277
|
```
|
|
164
278
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
279
|
+
### Active State — Trạng thái đang được chọn
|
|
280
|
+
|
|
281
|
+
```html
|
|
282
|
+
<libs_ui-components-buttons-button
|
|
283
|
+
type="button-outline"
|
|
284
|
+
label="Tab đang chọn"
|
|
285
|
+
[isActive]="true"
|
|
286
|
+
(outClick)="handlerSelect($event)" />
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Class Include — Thêm class tùy chỉnh
|
|
290
|
+
|
|
291
|
+
```html
|
|
292
|
+
<libs_ui-components-buttons-button
|
|
293
|
+
label="Button full-width"
|
|
294
|
+
classInclude="w-full justify-center"
|
|
295
|
+
(outClick)="handlerClick($event)" />
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Enter Key Handler — Kích hoạt bằng phím Enter
|
|
299
|
+
|
|
300
|
+
```html
|
|
301
|
+
<!-- Button sẽ được click khi user nhấn Enter trên document -->
|
|
302
|
+
<libs_ui-components-buttons-button
|
|
303
|
+
label="Xác nhận"
|
|
304
|
+
[isHandlerEnterDocumentClickButton]="true"
|
|
305
|
+
(outClick)="handlerConfirm($event)" />
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### ViewChild — Truy cập Popover Control
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { Component, viewChild } from '@angular/core';
|
|
312
|
+
import {
|
|
313
|
+
LibsUiComponentsButtonsButtonComponent,
|
|
314
|
+
IPopoverFunctionControlEvent,
|
|
315
|
+
} from '@libs-ui/components-buttons-button';
|
|
316
|
+
|
|
317
|
+
@Component({
|
|
318
|
+
selector: 'app-example',
|
|
319
|
+
standalone: true,
|
|
320
|
+
imports: [LibsUiComponentsButtonsButtonComponent],
|
|
321
|
+
template: `
|
|
322
|
+
<libs_ui-components-buttons-button
|
|
323
|
+
#myBtn
|
|
324
|
+
label="Click"
|
|
325
|
+
(outFunctionsControl)="handlerFunctionsControl($event)"
|
|
326
|
+
(outClick)="handlerClick($event)" />
|
|
327
|
+
`,
|
|
328
|
+
})
|
|
329
|
+
export class ExampleComponent {
|
|
330
|
+
private readonly myBtn = viewChild<LibsUiComponentsButtonsButtonComponent>('myBtn');
|
|
331
|
+
|
|
332
|
+
handlerClick(event: Event): void {
|
|
333
|
+
event.stopPropagation();
|
|
334
|
+
// truy cập popover control qua FunctionsControl getter
|
|
335
|
+
const control = this.myBtn()?.FunctionsControl;
|
|
336
|
+
control?.hide?.();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
handlerFunctionsControl(control: IPopoverFunctionControlEvent): void {
|
|
340
|
+
// lưu control để điều khiển popover từ bên ngoài
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## @Input()
|
|
346
|
+
|
|
347
|
+
| Input | Type | Default | Mô tả | Ví dụ |
|
|
348
|
+
|---|---|---|---|---|
|
|
349
|
+
| `[buttonCustom]` | `IColorButton` | `undefined` | Cấu hình màu custom — bắt buộc khi `type` là `'button-custom'` hoặc `'button-link-custom'` | `[buttonCustom]="{ rootColor: '#6366f1', configStepColor: { background: 500 } }"` |
|
|
350
|
+
| `[classIconLeft]` | `string` | `''` | Class CSS của icon hiển thị bên trái label | `[classIconLeft]="'libs-ui-icon-add'"` |
|
|
351
|
+
| `[classIconRight]` | `string` | `''` | Class CSS của icon hiển thị bên phải label | `[classIconRight]="'libs-ui-icon-arrow-right'"` |
|
|
352
|
+
| `[classInclude]` | `string` | `''` | Class CSS bổ sung gắn vào phần tử button | `classInclude="w-full"` |
|
|
353
|
+
| `[classLabel]` | `string` | `''` | Class CSS bổ sung cho phần tử label bên trong | `[classLabel]="'font-bold'"` |
|
|
354
|
+
| `[disable]` | `boolean` | `false` | Vô hiệu hóa button, ngăn click và áp dụng style disabled | `[disable]="isFormInvalid()"` |
|
|
355
|
+
| `[flagMouse]` | `IFlagMouse` | `{ isMouseEnter: false, isMouseEnterContent: false, isContainerHasScroll: false }` | Trạng thái con trỏ chuột từ container cha để popover hoạt động đúng khi scroll | `[flagMouse]="flagMouse()"` |
|
|
356
|
+
| `[iconOnlyType]` | `boolean` | `false` | Chỉ hiển thị icon, ẩn hoàn toàn label | `[iconOnlyType]="true"` |
|
|
357
|
+
| `[ignoreFocusWhenInputTab]` | `boolean` | `undefined` | Đặt `tabindex="-1"` để button không nhận focus khi nhấn phím Tab | `[ignoreFocusWhenInputTab]="true"` |
|
|
358
|
+
| `[ignorePointerEvent]` | `boolean` | `undefined` | Khi `disable=true`, vẫn cho phép pointer events (hữu ích khi cần show tooltip khi disabled) | `[ignorePointerEvent]="true"` |
|
|
359
|
+
| `[ignoreSetClickWhenShowPopover]` | `boolean` | `undefined` | Không set trạng thái `isClick=true` khi popover mở | `[ignoreSetClickWhenShowPopover]="true"` |
|
|
360
|
+
| `[ignoreStopPropagationEvent]` | `boolean` | `true` | Khi `false`, gọi `event.stopPropagation()` bên trong handler click của button | `[ignoreStopPropagationEvent]="false"` |
|
|
361
|
+
| `[imageLeft]` | `{ src: string; classInclude?: string }` | `undefined` | Hiển thị hình ảnh bên trái label | `[imageLeft]="{ src: '/assets/google.svg', classInclude: 'mr-[8px]' }"` |
|
|
362
|
+
| `[isActive]` | `boolean` | `undefined` | Áp dụng trạng thái active lên button (thêm attribute `active`) | `[isActive]="isSelected()"` |
|
|
363
|
+
| `[isHandlerEnterDocumentClickButton]` | `boolean` | `undefined` | Lắng nghe sự kiện `keyup Enter` trên `document` và trigger click button | `[isHandlerEnterDocumentClickButton]="true"` |
|
|
364
|
+
| `[isPending]` | `boolean` | `undefined` | Hiển thị spinner loading, tạm thời chặn click khi đang pending | `[isPending]="isLoading()"` |
|
|
365
|
+
| `[label]` | `string` | `' '` | Nội dung text hiển thị trên button, hỗ trợ i18n key | `label="Lưu"` hoặc `label="i18n_save"` |
|
|
366
|
+
| `[popover]` | `IPopover` | `{}` | Cấu hình popover tích hợp (type, mode, config...) | `[popover]="{ type: 'text', mode: 'hover', config: { content: 'Tooltip' } }"` |
|
|
367
|
+
| `[sizeButton]` | `TYPE_SIZE_BUTTON` | `'medium'` | Kích thước button: `'large'`, `'medium'`, `'small'`, `'smaller'` | `[sizeButton]="'small'"` |
|
|
368
|
+
| `[styleButton]` | `Record<string, any>` | `undefined` | Inline styles trực tiếp cho phần tử `<button>` | `[styleButton]="{ minWidth: '120px' }"` |
|
|
369
|
+
| `[styleIconLeft]` | `Record<string, any>` | `undefined` | Inline styles cho icon bên trái | `[styleIconLeft]="{ fontSize: '18px' }"` |
|
|
370
|
+
| `[type]` | `TYPE_BUTTON` | `'button-primary'` | Kiểu button xác định màu sắc và style tổng thể | `type="button-secondary"` |
|
|
371
|
+
| `[widthLabelPopover]` | `number` | `undefined` | Chiều rộng (px) của popover hiển thị khi label bị truncate | `[widthLabelPopover]="200"` |
|
|
372
|
+
| `[zIndex]` | `number` | `10` | Z-index của popover tích hợp | `[zIndex]="100"` |
|
|
373
|
+
|
|
374
|
+
## @Output()
|
|
375
|
+
|
|
376
|
+
| Output | Type | Mô tả | Handler TS | Binding HTML |
|
|
377
|
+
|---|---|---|---|---|
|
|
378
|
+
| `(outClick)` | `Event` | Phát ra khi button được click và không bị disabled/pending | `handlerClick(event: Event): void { event.stopPropagation(); /* xử lý */ }` | `(outClick)="handlerClick($event)"` |
|
|
379
|
+
| `(outFunctionsControl)` | `IPopoverFunctionControlEvent` | Phát ra object chứa các hàm điều khiển popover (show, hide...) sau khi popover khởi tạo xong | `handlerFunctionsControl(ctrl: IPopoverFunctionControlEvent): void { event.stopPropagation?.(); this.popoverControl = ctrl; }` | `(outFunctionsControl)="handlerFunctionsControl($event)"` |
|
|
380
|
+
| `(outPopoverEvent)` | `TYPE_POPOVER_EVENT` | Phát ra các sự kiện lifecycle của popover: `'show'`, `'hide'`, `'click'`, `'remove'` | `handlerPopoverEvent(event: TYPE_POPOVER_EVENT): void { /* xử lý */ }` | `(outPopoverEvent)="handlerPopoverEvent($event)"` |
|
|
381
|
+
|
|
382
|
+
## Public Getter (FunctionsControl)
|
|
383
|
+
|
|
384
|
+
Truy cập qua `viewChild` để điều khiển popover từ bên ngoài component:
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
private readonly buttonRef = viewChild<LibsUiComponentsButtonsButtonComponent>('buttonRef');
|
|
388
|
+
|
|
389
|
+
// Trong template:
|
|
390
|
+
// <libs_ui-components-buttons-button #buttonRef ... />
|
|
391
|
+
|
|
392
|
+
// Trong TS:
|
|
393
|
+
const control = this.buttonRef()?.FunctionsControl;
|
|
394
|
+
control?.hide?.(); // ẩn popover
|
|
395
|
+
control?.show?.(); // hiện popover
|
|
396
|
+
```
|
|
211
397
|
|
|
212
398
|
## Types & Interfaces
|
|
213
399
|
|
|
214
400
|
```typescript
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
401
|
+
import {
|
|
402
|
+
IButton,
|
|
403
|
+
TYPE_BUTTON,
|
|
404
|
+
TYPE_SIZE_BUTTON,
|
|
405
|
+
} from '@libs-ui/components-buttons-button';
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### TYPE_BUTTON
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
218
411
|
export type TYPE_BUTTON =
|
|
219
|
-
|
|
220
|
-
| 'button-primary
|
|
221
|
-
| 'button-
|
|
222
|
-
| 'button-secondary
|
|
223
|
-
| 'button-
|
|
224
|
-
| 'button-
|
|
225
|
-
| 'button-
|
|
226
|
-
| 'button-
|
|
227
|
-
| 'button-outline-
|
|
228
|
-
| 'button-
|
|
229
|
-
| 'button-danger
|
|
230
|
-
| 'button-
|
|
231
|
-
| 'button-
|
|
232
|
-
| 'button-
|
|
233
|
-
| 'button-
|
|
234
|
-
| 'button-
|
|
412
|
+
// Solid buttons
|
|
413
|
+
| 'button-primary' // Nút chính màu xanh lam
|
|
414
|
+
| 'button-primary-revert' // Biến thể revert của primary
|
|
415
|
+
| 'button-secondary' // Nút phụ
|
|
416
|
+
| 'button-secondary-red' // Nút phụ màu đỏ
|
|
417
|
+
| 'button-secondary-green' // Nút phụ màu xanh lá
|
|
418
|
+
| 'button-third' // Nút cấp 3 (ít nổi bật hơn)
|
|
419
|
+
| 'button-outline' // Nút viền
|
|
420
|
+
| 'button-outline-secondary' // Nút viền secondary
|
|
421
|
+
| 'button-outline-green' // Nút viền xanh lá
|
|
422
|
+
| 'button-outline-hover-danger' // Nút viền, hover chuyển đỏ
|
|
423
|
+
| 'button-third-hover-danger' // Nút third, hover chuyển đỏ
|
|
424
|
+
| 'button-danger-high' // Nút nguy hiểm cao (đỏ đậm)
|
|
425
|
+
| 'button-danger-low' // Nút nguy hiểm thấp (đỏ nhạt)
|
|
426
|
+
| 'button-green' // Nút xanh lá (thành công)
|
|
427
|
+
| 'button-violet' // Nút tím
|
|
428
|
+
| 'button-custom' // Màu tùy chỉnh — bắt buộc truyền [buttonCustom]
|
|
429
|
+
// Link-style buttons (không có nền, dạng text link)
|
|
235
430
|
| 'button-link-primary'
|
|
236
431
|
| 'button-link-third'
|
|
237
432
|
| 'button-link-danger-high'
|
|
238
433
|
| 'button-link-danger-low'
|
|
239
434
|
| 'button-link-green'
|
|
240
435
|
| 'button-link-violet'
|
|
241
|
-
| 'button-link-custom'
|
|
242
|
-
| string;
|
|
436
|
+
| 'button-link-custom' // Link màu tùy chỉnh — bắt buộc truyền [buttonCustom]
|
|
437
|
+
| string; // Hỗ trợ type tùy chỉnh mở rộng
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### TYPE_SIZE_BUTTON
|
|
243
441
|
|
|
244
|
-
|
|
245
|
-
* Kích thước button
|
|
246
|
-
*/
|
|
442
|
+
```typescript
|
|
247
443
|
export type TYPE_SIZE_BUTTON = 'large' | 'medium' | 'small' | 'smaller';
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### IButton
|
|
447
|
+
|
|
448
|
+
Interface dùng để cấu hình button trong danh sách (ví dụ: toolbar actions, button group):
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
import { IButton } from '@libs-ui/components-buttons-button';
|
|
452
|
+
import { IColorButton } from '@libs-ui/services-config-project';
|
|
453
|
+
import { IPopover } from '@libs-ui/components-popover';
|
|
248
454
|
|
|
249
|
-
/**
|
|
250
|
-
* Interface cho button config
|
|
251
|
-
*/
|
|
252
455
|
export interface IButton {
|
|
253
456
|
key?: string;
|
|
254
457
|
type?: TYPE_BUTTON;
|
|
@@ -269,25 +472,59 @@ export interface IButton {
|
|
|
269
472
|
styleButton?: Record<string, any>;
|
|
270
473
|
buttonCustom?: IColorButton;
|
|
271
474
|
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Ví dụ sử dụng `IButton` để cấu hình danh sách toolbar:
|
|
272
478
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
479
|
+
```typescript
|
|
480
|
+
import { Component } from '@angular/core';
|
|
481
|
+
import {
|
|
482
|
+
LibsUiComponentsButtonsButtonComponent,
|
|
483
|
+
IButton,
|
|
484
|
+
} from '@libs-ui/components-buttons-button';
|
|
485
|
+
|
|
486
|
+
@Component({
|
|
487
|
+
selector: 'app-toolbar',
|
|
488
|
+
standalone: true,
|
|
489
|
+
imports: [LibsUiComponentsButtonsButtonComponent],
|
|
490
|
+
template: `
|
|
491
|
+
@for (btn of toolbarButtons; track btn.key) {
|
|
492
|
+
<libs_ui-components-buttons-button
|
|
493
|
+
[type]="btn.type || 'button-primary'"
|
|
494
|
+
[label]="btn.label || ''"
|
|
495
|
+
[classIconLeft]="btn.classIconLeft || ''"
|
|
496
|
+
[disable]="btn.disable || false"
|
|
497
|
+
(outClick)="handlerToolbarAction($event, btn)" />
|
|
498
|
+
}
|
|
499
|
+
`,
|
|
500
|
+
})
|
|
501
|
+
export class ToolbarComponent {
|
|
502
|
+
protected readonly toolbarButtons: IButton[] = [
|
|
503
|
+
{ key: 'save', type: 'button-primary', label: 'Lưu', classIconLeft: 'libs-ui-icon-download-outline' },
|
|
504
|
+
{ key: 'cancel', type: 'button-secondary', label: 'Hủy' },
|
|
505
|
+
{ key: 'delete', type: 'button-danger-high', label: 'Xóa', classIconLeft: 'libs-ui-icon-remove' },
|
|
506
|
+
];
|
|
507
|
+
|
|
508
|
+
handlerToolbarAction(event: Event, btn: IButton): void {
|
|
509
|
+
event.stopPropagation();
|
|
510
|
+
btn.action?.();
|
|
511
|
+
}
|
|
280
512
|
}
|
|
281
513
|
```
|
|
282
514
|
|
|
283
|
-
##
|
|
515
|
+
## Lưu ý quan trọng
|
|
516
|
+
|
|
517
|
+
⚠️ **`buttonCustom` bắt buộc với custom type**: Khi `type="button-custom"` hoặc `type="button-link-custom"`, bắt buộc phải truyền `[buttonCustom]` với `rootColor` và `configStepColor`. Thiếu config này sẽ khiến button không hiển thị màu sắc.
|
|
518
|
+
|
|
519
|
+
⚠️ **`ignoreStopPropagationEvent` mặc định là `true`**: Mặc định button KHÔNG gọi `event.stopPropagation()`. Nếu muốn chặn event lan ra container cha, set `[ignoreStopPropagationEvent]="false"`. Trong handler `(outClick)` phía consumer, nên tự gọi `event.stopPropagation()` để chắc chắn.
|
|
520
|
+
|
|
521
|
+
⚠️ **`isHandlerEnterDocumentClickButton` listener global**: Input này đăng ký `keyup` listener trên toàn bộ `document`. Chỉ dùng cho button duy nhất trong context cần xác nhận bằng Enter (ví dụ: nút OK trong dialog confirm). Không bật đồng thời nhiều button với flag này.
|
|
284
522
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
| OnPush | - | Change Detection |
|
|
523
|
+
⚠️ **`isPending` chặn click**: Khi `isPending=true`, button hiển thị spinner và tự động bỏ qua mọi sự kiện click — không cần thêm `[disable]`.
|
|
524
|
+
|
|
525
|
+
⚠️ **`iconOnlyType` ẩn label hoàn toàn**: Khi `iconOnlyType=true`, label sẽ không được render dù có truyền `label`. Cần truyền ít nhất `classIconLeft` hoặc `classIconRight`.
|
|
526
|
+
|
|
527
|
+
⚠️ **Selector dùng dấu gạch dưới**: Selector chính xác là `libs_ui-components-buttons-button` (dấu `_` sau `libs`), không phải `libs-ui-components-buttons-button`.
|
|
291
528
|
|
|
292
529
|
## Demo
|
|
293
530
|
|
|
@@ -300,16 +537,9 @@ Truy cập: `http://localhost:4500/buttons/button`
|
|
|
300
537
|
## Unit Tests
|
|
301
538
|
|
|
302
539
|
```bash
|
|
303
|
-
# Chạy
|
|
304
|
-
npx nx test components-buttons-button
|
|
305
|
-
|
|
306
|
-
# Coverage
|
|
307
|
-
npx nx test components-buttons-button --coverage
|
|
540
|
+
# Chạy test cho lib này
|
|
541
|
+
npx nx test components-buttons-button --testFile=libs-ui/components/buttons/button/src/button.component.spec.ts
|
|
308
542
|
|
|
309
|
-
#
|
|
310
|
-
npx nx test components-buttons-button
|
|
543
|
+
# Toàn bộ test
|
|
544
|
+
npx nx test components-buttons-button
|
|
311
545
|
```
|
|
312
|
-
|
|
313
|
-
## License
|
|
314
|
-
|
|
315
|
-
MIT
|