@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 standalone Angular component được thiết kế để hiển thị các nút bấm với nhiều kiểu dáng (primary, secondary, outline, danger...), kích thước (large, medium, small, smaller) hỗ trợ tích hợp popover, spinner loading, icon trái/phải.
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`, tích hợp Popover. Component sử dụng Angular Signals + `ChangeDetectionStrategy.OnPush` để tối ưu hiệu năng.
8
8
 
9
- ### Tính năng
9
+ ## Tính năng
10
10
 
11
- - ✅ Hỗ trợ nhiều kiểu button (primary, secondary, outline, danger, link...)
12
- - ✅ 4 kích thước: large, medium, small, smaller
13
- - ✅ Tích hợp popover (hover/click)
14
- - ✅ Loading state với spinner
15
- - ✅ Icon trái/phải hoặc icon-only mode
16
- - ✅ Custom colors với buttonCustom
17
- - ✅ Disable state
18
- - ✅ Angular Signals cho tính phản hồi cao
19
- - ✅ OnPush Change Detection tối ưu hiệu năng
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 một nút bấm để kích hoạt hành động (submit form, mở dialog, navigate...)
24
- - Khi cần hiển thị trạng thái loading trong khi xử lý async operation
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 với icon hoặc chỉ hiển thị icon
27
- - Phù hợp cho các action buttons, form buttons, navigation buttons
29
+ - Khi cần button chỉ icon, không 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 { LibsUiComponentsButtonsButtonComponent, TYPE_BUTTON, TYPE_SIZE_BUTTON } from '@libs-ui/components-buttons-button';
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="Click me"
59
- (outClick)="handleClick($event)" />
62
+ label="Lưu"
63
+ (outClick)="handlerSave($event)" />
60
64
  ```
61
65
 
62
- ### With Icon
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
- <libs_ui-components-buttons-button
66
- type="button-primary"
67
- label="Add Item"
68
- [classIconLeft]="'libs-ui-icon-add'"
69
- (outClick)="handleClick($event)" />
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
- ### Different Types
108
+ ### Button Sizes — Các kích thước
73
109
 
74
110
  ```html
75
- <!-- Primary -->
76
- <libs_ui-components-buttons-button
77
- type="button-primary"
78
- label="Primary" />
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
- <!-- Secondary -->
81
- <libs_ui-components-buttons-button
82
- type="button-secondary"
83
- label="Secondary" />
117
+ ### Loading State — Trạng thái loading
84
118
 
85
- <!-- Danger -->
119
+ ```html
86
120
  <libs_ui-components-buttons-button
87
- type="button-danger-high"
88
- label="Delete" />
121
+ label="Lưu thông tin"
122
+ [isPending]="isSaving()"
123
+ (outClick)="handlerSave($event)" />
124
+ ```
89
125
 
90
- <!-- Link style -->
91
- <libs_ui-components-buttons-button
92
- type="button-link-primary"
93
- label="Learn more" />
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
- ### Different Sizes
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
- [sizeButton]="'large'"
101
- label="Large Button" />
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
- [sizeButton]="'medium'"
105
- label="Medium Button" />
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
- [sizeButton]="'small'"
109
- label="Small Button" />
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
- [sizeButton]="'smaller'"
113
- label="Smaller Button" />
172
+ [classIconLeft]="'libs-ui-icon-arrange'"
173
+ [iconOnlyType]="true"
174
+ (outClick)="handlerOpenSettings($event)" />
114
175
  ```
115
176
 
116
- ### Loading State
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="Save"
121
- [isPending]="isLoading"
122
- (outClick)="handleSave($event)" />
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
- ### Icon Only
193
+ ### Custom Color — Màu tùy chỉnh
126
194
 
127
195
  ```html
128
196
  <libs_ui-components-buttons-button
129
- [classIconLeft]="'libs-ui-icon-settings'"
130
- [iconOnlyType]="true"
131
- (outClick)="openSettings($event)" />
197
+ type="button-custom"
198
+ label="Custom Brand Color"
199
+ [buttonCustom]="customButtonConfig"
200
+ (outClick)="handlerClick($event)" />
132
201
  ```
133
202
 
134
- ### With Popover
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 me"
234
+ label="Hover để xem tooltip"
139
235
  [popover]="{
140
236
  type: 'text',
141
237
  mode: 'hover',
142
238
  config: {
143
- content: 'This is a tooltip',
144
- width: 200
239
+ content: 'Đây 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
- ### Custom Color
270
+ ### Image Left — Button kèm hình ảnh
150
271
 
151
272
  ```html
152
273
  <libs_ui-components-buttons-button
153
- type="button-custom"
154
- label="Custom"
155
- [buttonCustom]="{
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
- ## API
166
-
167
- ### libs_ui-components-buttons-button
168
-
169
- #### Inputs
170
-
171
- | Property | Type | Default | Description |
172
- | ------------------------------------- | -------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------- |
173
- | `[buttonCustom]` | `IColorButton` | `undefined` | Cấu hình màu custom (bắt buộc khi type là button-custom) |
174
- | `[classIconLeft]` | `string` | `''` | Class của icon bên trái |
175
- | `[classIconRight]` | `string` | `''` | Class của icon bên phải |
176
- | `[classInclude]` | `string` | `''` | Class CSS bổ sung cho button |
177
- | `[classLabel]` | `string` | `''` | Class CSS cho label |
178
- | `[disable]` | `boolean` | `false` | Disable button |
179
- | `[flagMouse]` | `IFlagMouse` | `{isMouseEnter: false, isMouseEnterContent: false, isContainerHasScroll: false}` | Trạng thái con trỏ chuột cho popover |
180
- | `[iconOnlyType]` | `boolean` | `false` | Chỉ hiển thị icon, ẩn label |
181
- | `[ignoreFocusWhenInputTab]` | `boolean` | `undefined` | Bỏ qua focus khi bấm TAB |
182
- | `[ignorePointerEvent]` | `boolean` | `undefined` | Bỏ qua pointer events |
183
- | `[ignoreSetClickWhenShowPopover]` | `boolean` | `undefined` | Không set click state khi show popover |
184
- | `[ignoreStopPropagationEvent]` | `boolean` | `true` | Cho phép event propagation |
185
- | `[imageLeft]` | `{src: string, classInclude?: string}` | `undefined` | Hình ảnh bên trái button |
186
- | `[isActive]` | `boolean` | `undefined` | Trạng thái active của button |
187
- | `[isHandlerEnterDocumentClickButton]` | `boolean` | `undefined` | Xử Enter key để click button |
188
- | `[isPending]` | `boolean` | `undefined` | Hiển thị spinner loading |
189
- | `[label]` | `string` | `' '` | Label của button |
190
- | `[popover]` | `IPopover` | `{}` | Cấu hình popover |
191
- | `[sizeButton]` | `TYPE_SIZE_BUTTON` | `'medium'` | Kích thước button: large, medium, small, smaller |
192
- | `[styleButton]` | `Record<string, any>` | `undefined` | Inline styles cho button |
193
- | `[styleIconLeft]` | `Record<string, any>` | `undefined` | Inline styles cho icon trái |
194
- | `[type]` | `TYPE_BUTTON` | `'button-primary'` | Kiểu button (màu sắc, style) |
195
- | `[widthLabelPopover]` | `number` | `undefined` | Chiều rộng popover của label |
196
- | `[zIndex]` | `number` | `10` | Z-index của popover |
197
-
198
- #### Outputs
199
-
200
- | Property | Type | Description |
201
- | ----------------------- | ------------------------------ | ------------------------------------ |
202
- | `(outClick)` | `Event` | Emit khi button được click |
203
- | `(outFunctionsControl)` | `IPopoverFunctionControlEvent` | Emit functions để điều khiển popover |
204
- | `(outPopoverEvent)` | `TYPE_POPOVER_EVENT` | Emit events từ popover |
205
-
206
- #### Public Methods
207
-
208
- | Method | Description |
209
- | ------------------ | --------------------------------------- |
210
- | `FunctionsControl` | Getter để lấy popover control functions |
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
- * Các kiểu button có sẵn
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
- | 'button-primary'
220
- | 'button-primary-revert'
221
- | 'button-secondary'
222
- | 'button-secondary-red'
223
- | 'button-outline-secondary'
224
- | 'button-third'
225
- | 'button-outline'
226
- | 'button-danger-high'
227
- | 'button-outline-hover-danger'
228
- | 'button-third-hover-danger'
229
- | 'button-danger-low'
230
- | 'button-green'
231
- | 'button-violet'
232
- | 'button-secondary-green'
233
- | 'button-outline-green'
234
- | 'button-custom'
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
- * Trạng thái chuột cho popover
275
- */
276
- export interface IFlagMouse {
277
- isMouseEnter: boolean;
278
- isMouseEnterContent: boolean;
279
- isContainerHasScroll?: boolean;
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
- ## Công nghệ
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
- | Technology | Version | Purpose |
286
- | --------------- | ------- | ---------------- |
287
- | Angular | 18+ | Framework |
288
- | Angular Signals | - | State management |
289
- | TailwindCSS | 3.x | Styling |
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 tests
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
- # Watch mode
310
- npx nx test components-buttons-button --watch
543
+ # Toàn bộ test
544
+ npx nx test components-buttons-button
311
545
  ```
312
-
313
- ## License
314
-
315
- MIT