# 复选列表

## 使用方法

`CheckboxList` 是包着若干 `CheckboxListItem` 的字段。绑定 `v-model` 时，值是已勾选项的字符串数组。

```vue
<script setup lang="ts">
import { CheckboxList, CheckboxListItem } from '@astryx-vue/core/CheckboxList'
import { shallowRef } from 'vue'

const selected = shallowRef(['email'])
</script>

<template>
  <CheckboxList v-model="selected" label="通知">
    <CheckboxListItem label="邮件" value="email" />
    <CheckboxListItem label="短信" value="sms" />
  </CheckboxList>
</template>
```

全选这类行自己管勾选状态时，不要给列表绑值。那一行用自己的勾选状态。紧凑密度使用小号复选框。勾选的行使用强调色的浅底。只读和禁用时这层浅底会拿掉。

整组只读时，勾选状态还在，也不会变暗，但不能改。和禁用同时设置时按禁用处理。禁用并写上原因时，每一行仍可聚焦，悬停或用键盘停在上面能看到原因。某一行自己在加载，或者一次变更还没结束时，复选框里显示旋转指示，这一行不能再点。

### 结构

- **组**: 带标签的复选框组的容器。
- **组标签**: 说明这些选项代表什么。
- **说明**: 组标签下面的辅助文字。
- **选项列表**: 装着可选复选框的列表。
- **选项行**: 装着一个选项的可选择行。
- **复选框**: 选项的选中指示。
- **选项标签**: 标识一个选项的主要内容。
- **选项说明**: 选项标签下面的次要文字。
- **尾部内容**: 使用方放在选项行末尾的内容。
- **转圈**: 还在等待结果的复选框里显示的加载指示。
- **状态消息**: 组下面的错误、警告或成功提示。

## 最佳实践

| 指引 | 实践 |
| --- | --- |
| 推荐 | 全选行不要给 `value`，它不属于集合里的某一项。 |
| 避免 | 用复选列表表示互斥选项。那种情况用 RadioList。 |

## 示例

### 全选

没有全部选中时，全选行是半选。

```vue
<script setup lang="ts">
import { CheckboxList, CheckboxListItem } from '@astryx-vue/core/CheckboxList'
import { computed, shallowRef } from 'vue'

const documents = [
  { id: 'transactions', label: 'Transaction history' },
  { id: 'statements', label: 'Account statements' },
  { id: 'tax', label: 'Tax documents' },
  { id: 'invoices', label: 'Invoices' },
]
const selected = shallowRef(['transactions'])
const allIds = documents.map(document => document.id)
const allChecked = computed(() => allIds.every(id => selected.value.includes(id)))
const selectAllState = computed(() => allChecked.value ? true : selected.value.length === 0 ? false : 'indeterminate' as const)

function toggleAll(checked: boolean): void {
  selected.value = checked ? [...allIds] : []
}

function toggle(id: string, checked: boolean): void {
  selected.value = checked ? [...selected.value, id] : selected.value.filter(value => value !== id)
}
</script>

<template>
  <CheckboxList label="Include in export" has-dividers>
    <CheckboxListItem label="Select all" :is-checked="selectAllState" @check="toggleAll" />
    <CheckboxListItem
      v-for="document in documents"
      :key="document.id"
      :label="document.label"
      :is-checked="selected.includes(document.id)"
      @check="checked => toggle(document.id, checked)"
    />
  </CheckboxList>
</template>
```
### 尾部内容

每一行末尾可以放价格徽标。

```vue
<script setup lang="ts">
import { Badge } from '@astryx-vue/core/Badge'
import { CheckboxList, CheckboxListItem } from '@astryx-vue/core/CheckboxList'
import { shallowRef } from 'vue'

const value = shallowRef(['free'])
</script>

<template>
  <CheckboxList v-model="value" label="Add-on packages" has-dividers>
    <CheckboxListItem label="Free tier" value="free" description="Basic features included">
      <template #end>
        <Badge variant="success">
          $0/mo
        </Badge>
      </template>
    </CheckboxListItem>
    <CheckboxListItem label="Pro tier" value="pro" description="Advanced analytics and integrations">
      <template #end>
        <Badge variant="info">
          $9/mo
        </Badge>
      </template>
    </CheckboxListItem>
    <CheckboxListItem label="Enterprise" value="enterprise" description="Custom solutions and dedicated support">
      <template #end>
        <Badge variant="purple">
          Custom
        </Badge>
      </template>
    </CheckboxListItem>
  </CheckboxList>
</template>
```
### 只读、原因和加载

只读保留勾选且不变暗。禁用时写上原因，行仍然可以聚焦。正在加载的行不能再点。

```vue
<script setup lang="ts">
import { CheckboxList, CheckboxListItem } from '@astryx-vue/core/CheckboxList'
import { VStack } from '@astryx-vue/core/Stack'
import { ref } from 'vue'

const saved = ref(['csv'])
const locked = ref<string[]>([])
const pending = ref<string[]>([])
</script>

<template>
  <div style="width: 320px; max-width: 100%">
    <VStack :gap="6">
      <CheckboxList v-model="saved" label="Saved export" is-read-only>
        <CheckboxListItem label="CSV" value="csv" />
        <CheckboxListItem label="JSON" value="json" />
      </CheckboxList>
      <CheckboxList v-model="locked" label="Managed export" is-disabled disabled-reason="Ask an administrator">
        <CheckboxListItem label="CSV" value="csv" />
        <CheckboxListItem label="JSON" value="json" />
      </CheckboxList>
      <CheckboxList v-model="pending" label="Live export">
        <CheckboxListItem label="CSV" value="csv" is-loading />
        <CheckboxListItem label="JSON" value="json" />
      </CheckboxList>
    </VStack>
  </div>
</template>
```

## CheckboxList 属性

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `v-model` | `string[]` | — | 已勾选的值。独立行不要传。 |
| `label` | `string` | — | 必填。这一组的无障碍名称。 |
| `description` | `string` | — | 标签下的说明。 |
| `density` | `'compact' \| 'balanced' \| 'spacious'` | `'balanced'` | 行距。紧凑时用小号复选框。 |
| `hasDividers` | `boolean` | `false` | 行与行之间画分隔线。 |
| `status` | `FieldStatusInput` | — | 错误、警告或成功。 |
| `statusVariant` | `'attached' \| 'detached' \| 'tooltip'` | `'detached'` | 状态消息的位置。 |
| `isDisabled` | `boolean` | `false` | 禁用每一行。 |
| `disabledMessage` | `string` | — | 整组被禁用的原因。和 `isDisabled` 一起用时，悬停或键盘聚焦会显示这段提示，每一行仍可聚焦（带 `aria-disabled`）。旧名 `disabledReason` 同样可用。 |
| `isReadOnly` | `boolean` | `false` | 整组只读。勾选还在，不变暗，但不能改。和禁用同时设置时按禁用处理。 |
| `changeAction` | `(values) => void \| Promise<void>` | — | 勾选变化之后执行。进行中的那一行显示加载，并且不能再点。 |
| `containerClass` | `string` | — | 加到字段外壳上的 class。横向标签的表单里没有外壳，这时不生效。 |
| `containerStyle` | `StyleValue` | — | 加到字段外壳上的行内样式。落点同 `containerClass`。 |
| `controlClass` | `string` | — | 加到选项组（`role="group"`）上的 class，不会落到每一行的复选框上。 |
| `controlStyle` | `StyleValue` | — | 加到选项组上的行内样式。 |

## CheckboxListItem 属性

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `label` | `string` | — | 必填。行标题，也是复选框的名称。需要链接等富内容时用 `label` 插槽，复选框的名字取可见文字。 |
| `ariaLabel` | `string` | — | 覆盖复选框的无障碍名称。富文本标题没有可读文字时使用。 |
| `value` | `string` | — | 集合模式必填。独立行不要传。 |
| `description` | `string` | — | 次要文字，同时作为复选框的说明。富内容用 `description` 插槽，仍然通过 `aria-describedby` 关联。 |
| `isChecked` | `boolean \| 'indeterminate'` | — | 独立行的勾选状态。集合模式忽略它。 |
| `isDisabled` | `boolean` | `false` | 禁用这一行。 |
| `isLoading` | `boolean` | `false` | 这一行自己的加载。复选框里显示旋转指示，并且不能切换。 |

## CheckboxListItem 事件和插槽

独立行通过 `check` 发出下一个布尔值。`label` 和 `description` 插槽放富文本。`end` 插槽画在标题后面。
