@libs-ui/components-buttons-group 0.2.355-9 → 0.2.356-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.
Files changed (2) hide show
  1. package/README.md +438 -84
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -1,131 +1,485 @@
1
- # Button Group
1
+ # @libs-ui/components-buttons-group
2
+
3
+ > Component Button Group cho phép nhóm nhiều button thành một nhóm liền kề với trạng thái active.
2
4
 
3
5
  ## Giới thiệu
4
6
 
5
- `@libs-ui/components-buttons-group` là một component nhóm Button cho Angular, cho phép hiển thị nhiều nút theo hàng, hỗ trợ quản lý trạng thái active và disable cho toàn nhóm.
7
+ `LibsUiComponentsButtonsGroupComponent` là một standalone Angular component được thiết kế để hiển thị một nhóm các button liền kề nhau. Component hỗ trợ quản lý trạng thái active và cho phép chuyển đổi giữa các button trong nhóm.
8
+
9
+ ### Tính năng
10
+
11
+ - ✅ Nhóm nhiều button thành một group liền kề
12
+ - ✅ Quản lý trạng thái active với visual feedback rõ ràng
13
+ - ✅ Two-way binding cho index button đang active
14
+ - ✅ Hỗ trợ disable toàn bộ group hoặc từng button
15
+ - ✅ Tích hợp popover cho từng button
16
+ - ✅ Hỗ trợ icon cho button
17
+ - ✅ Tự động xử lý border radius cho button đầu/cuối/giữa
18
+ - ✅ Angular Signals cho tính phản hồi cao
19
+ - ✅ OnPush Change Detection tối ưu hiệu năng
6
20
 
7
- ## Tính năng
21
+ ## Khi nào sử dụng
8
22
 
9
- - Hiển thị danh sách nút theo hàng ngang
10
- - Hỗ trợ two-way binding cho chỉ số nút đang active (`indexActive`)
11
- - Emit sự kiện khi thay đổi nút active (`outChange`)
12
- - Hỗ trợ disable toàn bộ nhóm nút
13
- - Tích hợp popover control event (`outFunctionsControl`)
23
+ - Khi cần nhóm các button liên quan với nhau
24
+ - Khi cần toggle giữa các options (như tab buttons)
25
+ - Khi cần hiển thị một nhóm actions liền kề
26
+ - Phù hợp cho filter groups, view switchers, segmented controls
14
27
 
15
28
  ## Cài đặt
16
29
 
17
30
  ```bash
31
+ # npm
18
32
  npm install @libs-ui/components-buttons-group
19
- ```
20
-
21
- hoặc
22
33
 
23
- ```bash
34
+ # yarn
24
35
  yarn add @libs-ui/components-buttons-group
25
36
  ```
26
37
 
27
- ## Sử dụng
28
-
29
- ### Inline Template
38
+ ## Import
30
39
 
31
40
  ```typescript
32
- import { Component } from '@angular/core';
33
41
  import { LibsUiComponentsButtonsGroupComponent } from '@libs-ui/components-buttons-group';
42
+ import { IButton } from '@libs-ui/components-buttons-button';
34
43
 
35
44
  @Component({
36
- selector: 'app-example',
37
45
  standalone: true,
38
46
  imports: [LibsUiComponentsButtonsGroupComponent],
39
- template: `
40
- <libs_ui-components-buttons-group
41
- [buttons]="buttons"
42
- [(indexActive)]="activeIndex"
43
- (outChange)="onChange($event)"></libs_ui-components-buttons-group>
44
- `,
47
+ // ...
45
48
  })
46
- export class ExampleComponent {
47
- buttons = [
48
- { key: '1', label: 'Option 1' },
49
- { key: '2', label: 'Option 2' },
50
- { key: '3', label: 'Option 3' },
51
- ];
52
- activeIndex = 0;
53
- onChange(button: any) {
54
- console.log('Changed:', button);
55
- }
56
- }
49
+ export class YourComponent {}
57
50
  ```
58
51
 
59
- ### File HTML riêng
52
+ ## dụ
53
+
54
+ ### Basic Usage
55
+
56
+ ```html
57
+ <libs_ui-components-buttons-group
58
+ [buttons]="viewButtons"
59
+ [(indexActive)]="activeIndex"
60
+ (outChange)="handleChange($event)" />
61
+ ```
60
62
 
61
63
  ```typescript
62
- import { Component } from '@angular/core';
63
- import { LibsUiComponentsButtonsGroupComponent } from '@libs-ui/components-buttons-group';
64
+ viewButtons: IButton[] = [
65
+ { label: 'List View', key: 'list' },
66
+ { label: 'Grid View', key: 'grid' },
67
+ { label: 'Card View', key: 'card' }
68
+ ];
64
69
 
65
- @Component({
66
- selector: 'app-example',
67
- standalone: true,
68
- imports: [LibsUiComponentsButtonsGroupComponent],
69
- templateUrl: './example.component.html',
70
- })
71
- export class ExampleComponent {
72
- buttons = [
73
- /* ... */
74
- ];
75
- activeIndex = 0;
76
- onChange(button: any) {}
70
+ activeIndex = 0;
71
+
72
+ handleChange(button: IButton) {
73
+ console.log('Selected:', button);
77
74
  }
78
75
  ```
79
76
 
77
+ ### With Icons
78
+
79
+ ```html
80
+ <libs_ui-components-buttons-group
81
+ [buttons]="viewButtonsWithIcons"
82
+ [(indexActive)]="activeView" />
83
+ ```
84
+
85
+ ```typescript
86
+ viewButtonsWithIcons: IButton[] = [
87
+ {
88
+ label: 'List',
89
+ key: 'list',
90
+ classIconLeft: 'libs-ui-icon-list'
91
+ },
92
+ {
93
+ label: 'Grid',
94
+ key: 'grid',
95
+ classIconLeft: 'libs-ui-icon-grid'
96
+ },
97
+ {
98
+ label: 'Card',
99
+ key: 'card',
100
+ classIconLeft: 'libs-ui-icon-card'
101
+ }
102
+ ];
103
+
104
+ activeView = 0;
105
+ ```
106
+
107
+ ### Icon Only Buttons
108
+
109
+ ```html
110
+ <libs_ui-components-buttons-group
111
+ [buttons]="iconOnlyButtons"
112
+ [(indexActive)]="selectedIndex" />
113
+ ```
114
+
115
+ ```typescript
116
+ iconOnlyButtons: IButton[] = [
117
+ {
118
+ key: 'bold',
119
+ classIconLeft: 'libs-ui-icon-bold',
120
+ iconOnlyType: true
121
+ },
122
+ {
123
+ key: 'italic',
124
+ classIconLeft: 'libs-ui-icon-italic',
125
+ iconOnlyType: true
126
+ },
127
+ {
128
+ key: 'underline',
129
+ classIconLeft: 'libs-ui-icon-underline',
130
+ iconOnlyType: true
131
+ }
132
+ ];
133
+ ```
134
+
135
+ ### With Disabled State
136
+
80
137
  ```html
138
+ <!-- Disable entire group -->
81
139
  <libs_ui-components-buttons-group
82
140
  [buttons]="buttons"
141
+ [disable]="true"
142
+ [(indexActive)]="activeIndex" />
143
+
144
+ <!-- Disable specific buttons -->
145
+ <libs_ui-components-buttons-group
146
+ [buttons]="buttonsWithDisabled"
147
+ [(indexActive)]="activeIndex" />
148
+ ```
149
+
150
+ ```typescript
151
+ buttonsWithDisabled: IButton[] = [
152
+ { label: 'Option 1', key: '1' },
153
+ { label: 'Option 2', key: '2', disable: true },
154
+ { label: 'Option 3', key: '3' }
155
+ ];
156
+ ```
157
+
158
+ ### With Popover
159
+
160
+ ```html
161
+ <libs_ui-components-buttons-group
162
+ [buttons]="buttonsWithPopover"
83
163
  [(indexActive)]="activeIndex"
84
- (outChange)="onChange($event)"></libs_ui-components-buttons-group>
164
+ (outFunctionsControl)="handlePopoverControl($event)" />
165
+ ```
166
+
167
+ ```typescript
168
+ buttonsWithPopover: IButton[] = [
169
+ {
170
+ label: 'Info',
171
+ key: 'info',
172
+ popover: {
173
+ content: 'This is information button',
174
+ mode: 'hover'
175
+ }
176
+ },
177
+ {
178
+ label: 'Warning',
179
+ key: 'warning',
180
+ popover: {
181
+ content: 'This is warning button',
182
+ mode: 'hover'
183
+ }
184
+ }
185
+ ];
186
+
187
+ handlePopoverControl(control: IPopoverFunctionControlEvent) {
188
+ console.log('Popover control:', control);
189
+ }
190
+ ```
191
+
192
+ ### Filter Group Example
193
+
194
+ ```html
195
+ <libs_ui-components-buttons-group
196
+ [buttons]="filterButtons"
197
+ [(indexActive)]="activeFilter"
198
+ (outChange)="applyFilter($event)" />
199
+ ```
200
+
201
+ ```typescript
202
+ filterButtons: IButton[] = [
203
+ { label: 'All', key: 'all' },
204
+ { label: 'Active', key: 'active' },
205
+ { label: 'Completed', key: 'completed' },
206
+ { label: 'Archived', key: 'archived' }
207
+ ];
208
+
209
+ activeFilter = 0;
210
+
211
+ applyFilter(button: IButton) {
212
+ // Apply filter based on button.key
213
+ console.log('Filtering by:', button.key);
214
+ }
215
+ ```
216
+
217
+ ### Custom Styling
218
+
219
+ ```html
220
+ <libs_ui-components-buttons-group
221
+ [buttons]="styledButtons"
222
+ [(indexActive)]="activeIndex" />
223
+ ```
224
+
225
+ ```typescript
226
+ styledButtons: IButton[] = [
227
+ {
228
+ label: 'Primary',
229
+ key: '1',
230
+ classInclude: 'custom-button-class'
231
+ },
232
+ {
233
+ label: 'Secondary',
234
+ key: '2',
235
+ classInclude: 'custom-button-class'
236
+ }
237
+ ];
238
+ ```
239
+
240
+ ## API
241
+
242
+ ### libs_ui-components-buttons-group
243
+
244
+ #### Inputs
245
+
246
+ | Property | Type | Default | Description |
247
+ | ----------------- | ---------------- | -------- | ---------------------------------------------- |
248
+ | `[buttons]` | `Array<IButton>` | required | Danh sách các button trong group |
249
+ | `[(indexActive)]` | `number` | `0` | Index của button đang active (two-way binding) |
250
+ | `[disable]` | `boolean` | `false` | Disable toàn bộ button group |
251
+
252
+ #### Outputs
253
+
254
+ | Property | Type | Description |
255
+ | ----------------------- | ------------------------------ | --------------------------------- |
256
+ | `(outChange)` | `IButton` | Emit khi chuyển đổi button active |
257
+ | `(outFunctionsControl)` | `IPopoverFunctionControlEvent` | Emit functions điều khiển popover |
258
+
259
+ #### Public Methods
260
+
261
+ | Method | Description |
262
+ | ------------------ | --------------------------------------- |
263
+ | `FunctionsControl` | Getter để lấy popover control functions |
264
+
265
+ ## Types & Interfaces
266
+
267
+ ### IButton Interface
268
+
269
+ Button group sử dụng interface `IButton` từ `@libs-ui/components-buttons-button`:
270
+
271
+ ```typescript
272
+ export interface IButton {
273
+ /** Unique key cho button */
274
+ key?: string;
275
+
276
+ /** Kiểu button (sẽ được override bởi component) */
277
+ type?: TYPE_BUTTON;
278
+
279
+ /** Kích thước button */
280
+ sizeButton?: TYPE_SIZE_BUTTON;
281
+
282
+ /** Chỉ hiển thị icon, ẩn label */
283
+ iconOnlyType?: boolean;
284
+
285
+ /** Label hiển thị trên button */
286
+ label?: string;
287
+
288
+ /** Disable button này */
289
+ disable?: boolean;
290
+
291
+ /** Class CSS bổ sung */
292
+ classInclude?: string;
293
+
294
+ /** Class icon bên trái */
295
+ classIconLeft?: string;
296
+
297
+ /** Class icon bên phải */
298
+ classIconRight?: string;
299
+
300
+ /** Class cho label */
301
+ classLabel?: string;
302
+
303
+ /** Cấu hình popover */
304
+ popover?: IPopover;
305
+
306
+ /** Không stop propagation event */
307
+ ignoreStopPropagationEvent?: boolean;
308
+
309
+ /** Z-index cho button */
310
+ zIndex?: number;
311
+
312
+ /** Trạng thái pending */
313
+ isPending?: boolean;
314
+
315
+ /** Action callback */
316
+ action?: (data?: any) => Promise<void>;
317
+
318
+ /** Style cho icon trái */
319
+ styleIconLeft?: Record<string, any>;
320
+
321
+ /** Style cho button */
322
+ styleButton?: Record<string, any>;
323
+
324
+ /** Custom color (khi type là button-custom) */
325
+ buttonCustom?: IColorButton;
326
+ }
327
+ ```
328
+
329
+ ### TYPE_BUTTON
330
+
331
+ ```typescript
332
+ export type TYPE_BUTTON =
333
+ | 'button-primary'
334
+ | 'button-primary-revert'
335
+ | 'button-secondary'
336
+ | 'button-secondary-red'
337
+ | 'button-outline-secondary'
338
+ | 'button-third'
339
+ | 'button-outline'
340
+ | 'button-danger-high'
341
+ | 'button-outline-hover-danger'
342
+ | 'button-third-hover-danger'
343
+ | 'button-danger-low'
344
+ | 'button-green'
345
+ | 'button-violet'
346
+ | 'button-secondary-green'
347
+ | 'button-outline-green'
348
+ | 'button-custom'
349
+ | 'button-link-primary'
350
+ | 'button-link-third'
351
+ | 'button-link-danger-high'
352
+ | 'button-link-danger-low'
353
+ | 'button-link-green'
354
+ | 'button-link-violet'
355
+ | 'button-link-custom'
356
+ | string;
357
+ ```
358
+
359
+ ### TYPE_SIZE_BUTTON
360
+
361
+ ```typescript
362
+ export type TYPE_SIZE_BUTTON = 'large' | 'medium' | 'small' | 'smaller';
363
+ ```
364
+
365
+ ## Styling
366
+
367
+ Component tự động xử lý styling cho button group:
368
+
369
+ - **First button**: Border radius trái (top-left, bottom-left)
370
+ - **Middle buttons**: Không có border radius, border-left bị loại bỏ
371
+ - **Last button**: Border radius phải (top-right, bottom-right), border-left bị loại bỏ
372
+ - **Active button**: Sử dụng `button-primary` type
373
+ - **Inactive buttons**: Sử dụng `button-primary-revert` type
374
+
375
+ ### CSS Variables
376
+
377
+ Component sử dụng CSS variable:
378
+
379
+ ```scss
380
+ --libs-ui-button-other-color-border: #226ff5; // Border color cho disabled state
381
+ ```
382
+
383
+ ## Behavior
384
+
385
+ ### Active State Management
386
+
387
+ - Khi click vào một button, nó sẽ trở thành active
388
+ - Click vào button đang active sẽ không có effect (không emit event)
389
+ - Index active được quản lý qua two-way binding `[(indexActive)]`
390
+ - Active button hiển thị với style `button-primary`
391
+ - Inactive buttons hiển thị với style `button-primary-revert`
392
+
393
+ ### Disable Behavior
394
+
395
+ - Khi `[disable]="true"`: Toàn bộ group bị disable
396
+ - Khi button cụ thể có `disable: true`: Chỉ button đó bị disable
397
+ - Disabled buttons vẫn giữ border styling phù hợp với vị trí trong group
398
+
399
+ ## Công nghệ
400
+
401
+ | Technology | Version | Purpose |
402
+ | --------------- | ------- | ---------------- |
403
+ | Angular | 18+ | Framework |
404
+ | Angular Signals | - | State management |
405
+ | SCSS | - | Styling |
406
+ | OnPush | - | Change Detection |
407
+
408
+ ## Demo
409
+
410
+ ```bash
411
+ npx nx serve core-ui
412
+ ```
413
+
414
+ Truy cập: `http://localhost:4500/buttons/group`
415
+
416
+ ## Unit Tests
417
+
418
+ ```bash
419
+ # Chạy tests
420
+ npx nx test components-buttons-group
421
+
422
+ # Coverage
423
+ npx nx test components-buttons-group --coverage
424
+
425
+ # Watch mode
426
+ npx nx test components-buttons-group --watch
85
427
  ```
86
428
 
87
- ## Công nghệ sử dụng
429
+ ## Dependencies
430
+
431
+ - `@angular/core`: >=18.0.0
432
+ - `@libs-ui/components-buttons-button`: 0.2.355-14
433
+ - `@libs-ui/components-popover`: 0.2.355-14
434
+ - `@ngx-translate/core`: ^15.0.0
435
+
436
+ ## Important Notes
437
+
438
+ ### ⚠️ Type Override
439
+
440
+ Component tự động override `type` property của buttons:
441
+
442
+ - Active button → `button-primary`
443
+ - Inactive button → `button-primary-revert`
444
+
445
+ Nếu bạn muốn custom type, cần modify component source code.
446
+
447
+ ### ⚠️ Border Styling
448
+
449
+ Component sử dụng `::ng-deep` để style nested components. Điều này cần thiết để style các button components bên trong nhưng có thể ảnh hưởng đến global styles nếu không cẩn thận.
450
+
451
+ ### ⚠️ Index-based Active State
452
+
453
+ Component sử dụng index thay vì key để quản lý active state. Đảm bảo array `buttons` không thay đổi thứ tự nếu bạn muốn maintain active state.
88
454
 
89
- - **Angular 18** với standalone components và Signals
90
- - **Tailwind CSS** 3.x (phong cách demo)
455
+ ## Best Practices
91
456
 
92
- ## API Reference
457
+ 1. **Sử dụng key duy nhất**: Luôn cung cấp `key` unique cho mỗi button để dễ identify
458
+ 2. **Limit số lượng buttons**: Nên giới hạn 3-5 buttons trong một group để UX tốt
459
+ 3. **Consistent labels**: Sử dụng label ngắn gọn, rõ ràng và có độ dài tương đương
460
+ 4. **Icon consistency**: Nếu dùng icon, nên dùng cho tất cả buttons hoặc không dùng
461
+ 5. **Responsive design**: Cân nhắc sử dụng icon-only mode trên mobile
93
462
 
94
- ### Inputs
463
+ ## Troubleshooting
95
464
 
96
- | Tên | Kiểu | Mặc định | Mô tả |
97
- | ----------- | ---------------- | -------- | --------------------------------------- |
98
- | buttons | `Array<IButton>` | required | Mảng các nút sẽ hiển thị trong nhóm. |
99
- | indexActive | `number` | `0` | Chỉ số của nút đang được chọn (từ 0). |
100
- | disable | `boolean` | `false` | Nếu true: vô hiệu hóa toàn bộ nhóm nút. |
465
+ ### Button không chuyển active state
101
466
 
102
- ### Outputs
467
+ - Kiểm tra `[(indexActive)]` binding
468
+ - Đảm bảo không có logic nào block event handler
469
+ - Verify button không bị disable
103
470
 
104
- | Tên | Kiểu | tả |
105
- | ------------------- | ------------------------------ | ----------------------------------- |
106
- | outChange | `(button: IButton) => void` | Trả về nút vừa được chọn. |
107
- | outFunctionsControl | `IPopoverFunctionControlEvent` | Cung cấp API để điều khiển popover. |
471
+ ### Border styling không đúng
108
472
 
109
- ### Interfaces
473
+ - Kiểm tra ViewEncapsulation của parent component
474
+ - Verify CSS variables được define
475
+ - Check xem có CSS conflicts không
110
476
 
111
- #### `IButton`
477
+ ### Popover không hoạt động
112
478
 
113
- | Property | Type | Required | Description |
114
- | -------------- | ------------------ | -------- | ----------------------------------------------- |
115
- | key | `string` | no | Khóa định danh của nút (unique). |
116
- | type | `TYPE_BUTTON` | no | Kiểu giao diện của nút (primary, secondary...). |
117
- | sizeButton | `TYPE_SIZE_BUTTON` | no | Kích thước của nút (nhỏ, trung, lớn). |
118
- | iconOnlyType | `boolean` | no | Nếu true: chỉ hiện icon, không hiện nhãn. |
119
- | label | `string` | no | Văn bản hiển thị trên nút. |
120
- | disable | `boolean` | no | Vô hiệu hóa nút khi true. |
121
- | classInclude | `string` | no | Class CSS thêm cho container của nút. |
122
- | classIconLeft | `string` | no | Class CSS thêm cho icon bên trái. |
123
- | classIconRight | `string` | no | Class CSS thêm cho icon bên phải. |
124
- | classLabel | `string` | no | Class CSS thêm cho nhãn. |
125
- | popover | `IPopover` | no | Cấu hình popover cho nút (nếu cần). |
479
+ - Đảm bảo đã import `@libs-ui/components-popover`
480
+ - Verify popover config đúng format
481
+ - Check z-index conflicts
126
482
 
127
- #### `IPopoverFunctionControlEvent`
483
+ ## License
128
484
 
129
- | Property | Type | Required | Description |
130
- | -------- | ---- | -------- | --------------------------------------------------- |
131
- | ... | ... | ... | Xem `@libs-ui/components-popover` để biết chi tiết. |
485
+ MIT
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@libs-ui/components-buttons-group",
3
- "version": "0.2.355-9",
3
+ "version": "0.2.356-1",
4
4
  "peerDependencies": {
5
5
  "@angular/core": ">=18.0.0",
6
- "@libs-ui/components-buttons-button": "0.2.355-9",
7
- "@libs-ui/components-popover": "0.2.355-9",
6
+ "@libs-ui/components-buttons-button": "0.2.356-1",
7
+ "@libs-ui/components-popover": "0.2.356-1",
8
8
  "@ngx-translate/core": "^15.0.0"
9
9
  },
10
10
  "sideEffects": false,