npm package: @libs-ui/components-buttons-group
Description: > 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.
Latest version: 0.2.356-23

# @libs-ui/components-buttons-group

> 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.

## Giới thiệu

`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.

### Tính năng

- ✅ Nhóm nhiều button thành một group liền kề
- ✅ Quản lý trạng thái active với visual feedback rõ ràng
- ✅ Two-way binding cho index button đang active
- ✅ Hỗ trợ disable toàn bộ group hoặc từng button
- ✅ Tích hợp popover cho từng button
- ✅ Hỗ trợ icon cho button
- ✅ Tự động xử lý border radius cho button đầu/cuối/giữa
- ✅ Angular Signals cho tính phản hồi cao
- ✅ OnPush Change Detection tối ưu hiệu năng

## Khi nào sử dụng

- Khi cần nhóm các button có liên quan với nhau
- Khi cần toggle giữa các options (như tab buttons)
- Khi cần hiển thị một nhóm actions liền kề
- Phù hợp cho filter groups, view switchers, segmented controls

## Cài đặt

```bash
# npm
npm install @libs-ui/components-buttons-group

# yarn
yarn add @libs-ui/components-buttons-group
```

## Import

```typescript
import { LibsUiComponentsButtonsGroupComponent } from '@libs-ui/components-buttons-group';
import { IButton } from '@libs-ui/components-buttons-button';

@Component({
  standalone: true,
  imports: [LibsUiComponentsButtonsGroupComponent],
  // ...
})
export class YourComponent {}
```

## Ví dụ

### Basic Usage

```html
<libs_ui-components-buttons-group
  [buttons]="viewButtons"
  [(indexActive)]="activeIndex"
  (outChange)="handleChange($event)" />
```

```typescript
viewButtons: IButton[] = [
  { label: 'List View', key: 'list' },
  { label: 'Grid View', key: 'grid' },
  { label: 'Card View', key: 'card' }
];

activeIndex = 0;

handleChange(button: IButton) {
  console.log('Selected:', button);
}
```

### With Icons

```html
<libs_ui-components-buttons-group
  [buttons]="viewButtonsWithIcons"
  [(indexActive)]="activeView" />
```

```typescript
viewButtonsWithIcons: IButton[] = [
  {
    label: 'List',
    key: 'list',
    classIconLeft: 'libs-ui-icon-list'
  },
  {
    label: 'Grid',
    key: 'grid',
    classIconLeft: 'libs-ui-icon-grid'
  },
  {
    label: 'Card',
    key: 'card',
    classIconLeft: 'libs-ui-icon-card'
  }
];

activeView = 0;
```

### Icon Only Buttons

```html
<libs_ui-components-buttons-group
  [buttons]="iconOnlyButtons"
  [(indexActive)]="selectedIndex" />
```

```typescript
iconOnlyButtons: IButton[] = [
  {
    key: 'bold',
    classIconLeft: 'libs-ui-icon-bold',
    iconOnlyType: true
  },
  {
    key: 'italic',
    classIconLeft: 'libs-ui-icon-italic',
    iconOnlyType: true
  },
  {
    key: 'underline',
    classIconLeft: 'libs-ui-icon-underline',
    iconOnlyType: true
  }
];
```

### With Disabled State

```html
<!-- Disable entire group -->
<libs_ui-components-buttons-group
  [buttons]="buttons"
  [disable]="true"
  [(indexActive)]="activeIndex" />

<!-- Disable specific buttons -->
<libs_ui-components-buttons-group
  [buttons]="buttonsWithDisabled"
  [(indexActive)]="activeIndex" />
```

```typescript
buttonsWithDisabled: IButton[] = [
  { label: 'Option 1', key: '1' },
  { label: 'Option 2', key: '2', disable: true },
  { label: 'Option 3', key: '3' }
];
```

### With Popover

```html
<libs_ui-components-buttons-group
  [buttons]="buttonsWithPopover"
  [(indexActive)]="activeIndex"
  (outFunctionsControl)="handlePopoverControl($event)" />
```

```typescript
buttonsWithPopover: IButton[] = [
  {
    label: 'Info',
    key: 'info',
    popover: {
      content: 'This is information button',
      mode: 'hover'
    }
  },
  {
    label: 'Warning',
    key: 'warning',
    popover: {
      content: 'This is warning button',
      mode: 'hover'
    }
  }
];

handlePopoverControl(control: IPopoverFunctionControlEvent) {
  console.log('Popover control:', control);
}
```

### Filter Group Example

```html
<libs_ui-components-buttons-group
  [buttons]="filterButtons"
  [(indexActive)]="activeFilter"
  (outChange)="applyFilter($event)" />
```

```typescript
filterButtons: IButton[] = [
  { label: 'All', key: 'all' },
  { label: 'Active', key: 'active' },
  { label: 'Completed', key: 'completed' },
  { label: 'Archived', key: 'archived' }
];

activeFilter = 0;

applyFilter(button: IButton) {
  // Apply filter based on button.key
  console.log('Filtering by:', button.key);
}
```

### Custom Styling

```html
<libs_ui-components-buttons-group
  [buttons]="styledButtons"
  [(indexActive)]="activeIndex" />
```

```typescript
styledButtons: IButton[] = [
  {
    label: 'Primary',
    key: '1',
    classInclude: 'custom-button-class'
  },
  {
    label: 'Secondary',
    key: '2',
    classInclude: 'custom-button-class'
  }
];
```

## API

### libs_ui-components-buttons-group

#### Inputs

| Property          | Type             | Default  | Description                                    |
| ----------------- | ---------------- | -------- | ---------------------------------------------- |
| `[buttons]`       | `Array<IButton>` | required | Danh sách các button trong group               |
| `[(indexActive)]` | `number`         | `0`      | Index của button đang active (two-way binding) |
| `[disable]`       | `boolean`        | `false`  | Disable toàn bộ button group                   |

#### Outputs

| Property                | Type                           | Description                       |
| ----------------------- | ------------------------------ | --------------------------------- |
| `(outChange)`           | `IButton`                      | Emit khi chuyển đổi button active |
| `(outFunctionsControl)` | `IPopoverFunctionControlEvent` | Emit functions điều khiển popover |

#### Public Methods

| Method             | Description                             |
| ------------------ | --------------------------------------- |
| `FunctionsControl` | Getter để lấy popover control functions |

## Types & Interfaces

### IButton Interface

Button group sử dụng interface `IButton` từ `@libs-ui/components-buttons-button`:

```typescript
export interface IButton {
  /** Unique key cho button */
  key?: string;

  /** Kiểu button (sẽ được override bởi component) */
  type?: TYPE_BUTTON;

  /** Kích thước button */
  sizeButton?: TYPE_SIZE_BUTTON;

  /** Chỉ hiển thị icon, ẩn label */
  iconOnlyType?: boolean;

  /** Label hiển thị trên button */
  label?: string;

  /** Disable button này */
  disable?: boolean;

  /** Class CSS bổ sung */
  classInclude?: string;

  /** Class icon bên trái */
  classIconLeft?: string;

  /** Class icon bên phải */
  classIconRight?: string;

  /** Class cho label */
  classLabel?: string;

  /** Cấu hình popover */
  popover?: IPopover;

  /** Không stop propagation event */
  ignoreStopPropagationEvent?: boolean;

  /** Z-index cho button */
  zIndex?: number;

  /** Trạng thái pending */
  isPending?: boolean;

  /** Action callback */
  action?: (data?: any) => Promise<void>;

  /** Style cho icon trái */
  styleIconLeft?: Record<string, any>;

  /** Style cho button */
  styleButton?: Record<string, any>;

  /** Custom color (khi type là button-custom) */
  buttonCustom?: IColorButton;
}
```

### TYPE_BUTTON

```typescript
export type TYPE_BUTTON =
  | 'button-primary'
  | 'button-primary-revert'
  | 'button-secondary'
  | 'button-secondary-red'
  | 'button-outline-secondary'
  | 'button-third'
  | 'button-outline'
  | 'button-danger-high'
  | 'button-outline-hover-danger'
  | 'button-third-hover-danger'
  | 'button-danger-low'
  | 'button-green'
  | 'button-violet'
  | 'button-secondary-green'
  | 'button-outline-green'
  | 'button-custom'
  | 'button-link-primary'
  | 'button-link-third'
  | 'button-link-danger-high'
  | 'button-link-danger-low'
  | 'button-link-green'
  | 'button-link-violet'
  | 'button-link-custom'
  | string;
```

### TYPE_SIZE_BUTTON

```typescript
export type TYPE_SIZE_BUTTON = 'large' | 'medium' | 'small' | 'smaller';
```

## Styling

Component tự động xử lý styling cho button group:

- **First button**: Border radius trái (top-left, bottom-left)
- **Middle buttons**: Không có border radius, border-left bị loại bỏ
- **Last button**: Border radius phải (top-right, bottom-right), border-left bị loại bỏ
- **Active button**: Sử dụng `button-primary` type
- **Inactive buttons**: Sử dụng `button-primary-revert` type

### CSS Variables

Component sử dụng CSS variable:

```scss
--libs-ui-button-other-color-border: #226ff5; // Border color cho disabled state
```

## Behavior

### Active State Management

- Khi click vào một button, nó sẽ trở thành active
- Click vào button đang active sẽ không có effect (không emit event)
- Index active được quản lý qua two-way binding `[(indexActive)]`
- Active button hiển thị với style `button-primary`
- Inactive buttons hiển thị với style `button-primary-revert`

### Disable Behavior

- Khi `[disable]="true"`: Toàn bộ group bị disable
- Khi button cụ thể có `disable: true`: Chỉ button đó bị disable
- Disabled buttons vẫn giữ border styling phù hợp với vị trí trong group

## Công nghệ

| Technology      | Version | Purpose          |
| --------------- | ------- | ---------------- |
| Angular         | 18+     | Framework        |
| Angular Signals | -       | State management |
| SCSS            | -       | Styling          |
| OnPush          | -       | Change Detection |

## Demo

```bash
npx nx serve core-ui
```

Truy cập: `http://localhost:4500/buttons/group`

## Unit Tests

```bash
# Chạy tests
npx nx test components-buttons-group

# Coverage
npx nx test components-buttons-group --coverage

# Watch mode
npx nx test components-buttons-group --watch
```

## Dependencies

- `@angular/core`: >=18.0.0
- `@libs-ui/components-buttons-button`: 0.2.355-14
- `@libs-ui/components-popover`: 0.2.355-14
- `@ngx-translate/core`: ^15.0.0

## Important Notes

### ⚠️ Type Override

Component tự động override `type` property của buttons:

- Active button → `button-primary`
- Inactive button → `button-primary-revert`

Nếu bạn muốn custom type, cần modify component source code.

### ⚠️ Border Styling

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.

### ⚠️ Index-based Active State

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.

## Best Practices

1. **Sử dụng key duy nhất**: Luôn cung cấp `key` unique cho mỗi button để dễ identify
2. **Limit số lượng buttons**: Nên giới hạn 3-5 buttons trong một group để UX tốt
3. **Consistent labels**: Sử dụng label ngắn gọn, rõ ràng và có độ dài tương đương
4. **Icon consistency**: Nếu dùng icon, nên dùng cho tất cả buttons hoặc không dùng
5. **Responsive design**: Cân nhắc sử dụng icon-only mode trên mobile

## Troubleshooting

### Button không chuyển active state

- Kiểm tra `[(indexActive)]` binding
- Đảm bảo không có logic nào block event handler
- Verify button không bị disable

### Border styling không đúng

- Kiểm tra ViewEncapsulation của parent component
- Verify CSS variables được define
- Check xem có CSS conflicts không

### Popover không hoạt động

- Đảm bảo đã import `@libs-ui/components-popover`
- Verify popover config đúng format
- Check z-index conflicts

## License

MIT