# ButtonGroup 按钮组

把若干个 `MButton` 拼成一个视觉整体，适合「左中右」这类互斥或连续的操作用途。组本身不渲染任何按钮，只通过 `provide/inject` 向内部按钮统一下发 `variant` / `color` / `size` / `disabled`，因此组内按钮的外观天然一致。

## 代码演示

### 基础用法

`MButtonGroup` 必须搭配 `MButton` 使用，组只负责统一外观与圆角拼接。

**演示说明：** 组内按钮通过 `provide/inject` 拿到组下发的上下文，点击时各自抛出 `click`；组本身不代表某次选中状态，需要互斥选中请自行记录当前项。

```vue
<script setup lang="ts">
import { toast } from 'miao-design'

function handleClick(label: string) {
  toast.success(`选中：${label}`)
}
</script>

<template>
  <div class="m-row-demo">
    <MButtonGroup variant="outlined" color="primary">
      <MButton v-for="label in ['左', '中', '右']" :key="label" @click="handleClick(label)">
        {{ label }}
      </MButton>
    </MButtonGroup>
  </div>
</template>
```

### 外观变体

组上的 `variant` 会覆盖内部每个按钮自己的 `variant`，即使子按钮显式传了 `variant` 也会被组改写。

**演示说明：** 非 `outlined` 变体下，组内按钮之间的分隔边框会转为透明，只保留拼接后的整体轮廓。

```vue
<script setup lang="ts">
const variants = ['outlined', 'contained', 'tonal', 'text'] as const
</script>

<template>
  <div class="m-row-demo--col">
    <MButtonGroup v-for="variant in variants" :key="variant" :variant="variant" color="primary">
      <MButton>{{ variant }}</MButton>
      <MButton>中间</MButton>
      <MButton>右侧</MButton>
    </MButtonGroup>
  </div>
</template>
```

### 尺寸

`small` / `default` / `large` 三档由组统一决定，子按钮传 `size` 无效。

**演示说明：** `size` 未设置时组会在上下文里写入 `'default'`，所以「不传」并不等于「子按钮自己决定」，而是统一按 `default` 渲染。

```vue
<script setup lang="ts">
const sizes = ['small', 'default', 'large'] as const
</script>

<template>
  <div class="m-row-demo--col">
    <MButtonGroup v-for="size in sizes" :key="size" variant="outlined" color="primary" :size="size">
      <MButton>{{ size }}</MButton>
      <MButton>中间</MButton>
      <MButton>右侧</MButton>
    </MButtonGroup>
  </div>
</template>
```

### 语义色与子级覆盖

`color` 是唯一「子优先」的属性：子按钮自己设置了 `color` 就用自己的，没设置才继承组的。

**演示说明：** 每组的最后一个按钮都显式写了 `color="info"`，用于对比组色被覆盖的效果。

```vue
<script setup lang="ts">
const groups = ['primary', 'secondary', 'success', 'error'] as const
</script>

<template>
  <div class="m-row-demo">
    <MButtonGroup
      v-for="color in groups"
      :key="color"
      variant="contained"
      :color="color"
      size="small"
    >
      <MButton>{{ color }}</MButton>
      <MButton>中间</MButton>
      <!-- 子按钮自带 color，组色不再生效 -->
      <MButton color="info">info</MButton>
    </MButtonGroup>
  </div>
</template>
```

### 禁用

`disabled` 取父子并集：组禁用则整组禁用，组不禁用时单个子按钮仍可自行禁用。

**演示说明：** 点击下方按钮可实时切换整组的禁用状态。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const groupDisabled = ref(false)
</script>

<template>
  <div class="m-row-demo--col">
    <MButtonGroup variant="outlined" color="primary" :disabled="groupDisabled">
      <MButton>左</MButton>
      <MButton>中</MButton>
      <MButton>右</MButton>
    </MButtonGroup>

    <MButtonGroup variant="outlined" color="primary">
      <MButton>可用</MButton>
      <!-- 组不禁用时，单个子按钮仍可自行禁用 -->
      <MButton disabled>子级禁用</MButton>
      <MButton>可用</MButton>
    </MButtonGroup>

    <MButton variant="contained" color="primary" @click="groupDisabled = !groupDisabled">
      {{ groupDisabled ? '恢复整组' : '禁用整组' }}
    </MButton>
  </div>
</template>
```

## API

### Props

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `variant` | `'text' \| 'outlined' \| 'contained' \| 'tonal' \| 'circle'` | `'outlined'` | 组内按钮统一变体；**始终覆盖**子按钮自身的 `variant` |
| `color` | `'primary' \| 'secondary' \| 'error' \| 'success' \| 'warning' \| 'info'` | `undefined` | 组内按钮统一语义色；子按钮自己设置了 `color` 时以子为准 |
| `size` | `'small' \| 'default' \| 'large'` | `undefined`（上下文回落 `'default'`） | 组内按钮统一尺寸；**始终覆盖**子按钮自身的 `size` |
| `disabled` | `boolean` | `false` | 是否整体禁用组内所有按钮（与子按钮的 `disabled` 取并集） |

### Events

无。

### Slots

| 插槽 | 参数 | 说明 |
| --- | --- | --- |
| `default` | 无 | 组内的 `MButton` 列表 |

### 组内上下文

组通过 `provide/inject` 把下面的对象下发给内部按钮（`ButtonGroupContext`），子按钮读取后按各自的优先级合并：

```ts
export interface ButtonGroupContext {
  color?: ButtonColor
  variant: ButtonVariant
  size: 'small' | 'default' | 'large'
  disabled: boolean
}
```

合并规则：

| 属性 | 合并方式 | 结果 |
| --- | --- | --- |
| `variant` | 组优先 | 子按钮的 `variant` 被忽略 |
| `size` | 组优先 | 子按钮的 `size` 被忽略（组未传时按 `default` 覆盖） |
| `color` | 子优先 | 子按钮未设置时才取组的颜色 |
| `disabled` | 取并集 | 组或子按钮任一为 `true` 即禁用 |

## 使用建议

::: tip 组只统一外观，不管理选中状态
`MButtonGroup` 是纯样式与上下文容器，没有 `modelValue` 也不抛 `change`。需要「单选按钮组」这类互斥语义时，用业务里的 `ref` 记录当前项，再在按钮的 `click` 里更新即可。
:::

::: warning 子按钮的 variant 与 size 会被覆盖
这是刻意的设计：组内按钮外观必须一致，所以组的 `variant` / `size` 永远赢。只有 `color` 允许子按钮覆盖。如果你的按钮需要各自不同的变体，就不要把它们放进同一个组。

```vue
<MButtonGroup variant="contained" size="small">
  <MButton variant="text" size="large">仍然按 contained + small 渲染</MButton>
</MButtonGroup>
```
:::

::: warning 组内按钮的圆角与边框被改写
组会给首尾按钮加外圆角、给中间按钮去掉圆角，并把相邻按钮的 `margin-left` 设为 `-1px` 让边框重叠。如果按钮原本带了 `shape="round"` 或 `shape="circle"`，拼接效果会和预期不同。
:::

## 相关组件

- [Button 按钮](/components/button)：组内唯一可用的子组件，`variant` / `color` / `size` 都会被组接管。
- [Fab 悬浮按钮](/components/fab)：另一种操作入口，通常单独出现而不成组。
