# useTableGroupedRows

## 使用方法

把一个扁平的数组收成可折叠的分组行。每个不同的 groupBy 值变成一整行的分组表头，带折叠按钮、分组名和成员数；折叠后这一组的数据行隐藏，表头还在。展开折叠的状态在按钮上，不在原生的表格行上。和 useTableTreeState 一样：被折叠的集合由你保存，hook 返回 {data, plugin, idKey}，分别传给 Table 的 data、plugins 和 idKey。分组发生在你交进来的行上，所以分页时顺序是过滤、排序、切片、再分组：先把分组字段排好，再排用户选的字段，这样同一组的行是连续的，新的一页接在表格下面，而不是插到读者上方。按行数切页仍会切在一组中间，这时表头数的是已经加载的行，而不是总数：「6」会悄悄变成「10」。整份结果都在手上时，把切点推到它落在的那一组末尾：这样每个渲染出来的分组都是完整的，数量也是总数。

```ts
import { useTableGroupedRows } from '@astryx-vue/core/Table'
```

`data` 是 computed，配置变了两者才重算。用 getter 传配置：折叠集合要换成一个新对象，不要原地改；原地改还是同一个对象，computed 不会重跑。

```ts
const { data, plugin, idKey } = useTableGroupedRows(() => ({
  data: employees,
  groupBy: employee => employee.team,
  collapsedGroups: collapsed.value,
  onToggleGroup: toggleGroup,
  getRowKey: employee => employee.id,
}))
```

分组表头就是数组里一个普通对象。导出的 `isTableGroupHeader(row)` 能把表头和数据行分开，比如给行编号前先判一下。

## 示例

### useTableGroupedRows — 可折叠分组

用 useTableGroupedRows 分成可折叠分组的表格。每组一整行表头，带折叠按钮、组名和成员数；点一下就折叠或展开。

```vue
<script setup lang="ts">
import type { TableColumn } from '@astryx-vue/core/Table'
import { pixel, proportional, Table, useTableGroupedRows, useTableRowIndex } from '@astryx-vue/core/Table'
import { ref } from 'vue'

interface Employee extends Record<string, unknown> {
  id: string
  name: string
  role: string
}

const employees: Employee[] = [
  { id: '1', name: 'Alice', role: 'Engineer' },
  { id: '2', name: 'Bob', role: 'Designer' },
  { id: '3', name: 'Carol', role: 'Engineer' },
  { id: '4', name: 'Diego', role: 'Designer' },
]

const columns: TableColumn<Employee>[] = [
  { key: 'name', header: 'Name', width: proportional(1) },
  { key: 'role', header: 'Role', width: pixel(140) },
]

const collapsed = ref(new Set<string>())

function toggleGroup(key: string) {
  const next = new Set(collapsed.value)
  if (next.has(key))
    next.delete(key)
  else
    next.add(key)
  collapsed.value = next
}

const { data: groupedRows, plugin: groupedPlugin, idKey } = useTableGroupedRows<Employee>(() => ({
  data: employees,
  groupBy: employee => employee.role,
  collapsedGroups: collapsed.value,
  onToggleGroup: toggleGroup,
  getRowKey: employee => employee.id,
  groupOrder: ['Designer', 'Engineer'],
}))

const numbers = useTableRowIndex<Employee>(() => ({
  data: groupedRows.value,
  getRowKey: employee => employee.id,
}))
</script>

<template>
  <Table
    :data="groupedRows"
    :columns="columns"
    :id-key="idKey"
    :plugins="{ grouped: groupedPlugin, numbers }"
  />
</template>
```

## 参数

`useTableGroupedRows()` 只有一个配置对象。用 getter 或 `ref` 包一层，分组才能跟着当前的行和当前的折叠集合走：`useTableGroupedRows(() => ({ ... }))`。

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `data` | `readonly T[]` | — | 必填。要分组的扁平数据。 |
| `groupBy` | `(item: T) => string` | — | 必填。算出这一行的分组 key。key 相同的行在同一组。 |
| `collapsedGroups` | `ReadonlySet<string>` | — | 必填。当前折叠了的分组 key 集合。 |
| `onToggleGroup` | `(groupKey: string) => void` | — | 必填。点分组表头时调用，传入这组的 key。 |
| `renderGroupHeader` | `(groupKey: string, count: number, collapsed: boolean) => VNodeChild` | — | 自定义分组表头的内容，画在折叠按钮右边。默认是 `groupKey (count)`。 |
| `getRowKey` | `(item: T) => string` | — | 真实行的稳定 key。不传时按位置生成。 |
| `groupOrder` | `readonly string[]` | — | 指定分组顺序。没列出来的组按第一次出现的顺序排在这些后面。 |

## 返回值

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `data` | `ComputedRef<T[]>` | 你的行加上每组一个表头，按显示顺序排好。折叠的组留下表头，藏起自己的行。 |
| `plugin` | `TablePlugin` | 让分组表头占满整行，选择列和行编号跳过它，点一下就切换折叠。 |
| `idKey` | `(item: T) => string` | 两种行都能算出的行身份：分组表头给分组 key，数据行给 `getRowKey` 的结果或它的位置。 |

### isTableGroupHeader

`isTableGroupHeader(item)` 把 `data` 里的一项收窄成分组表头，可以直接读 `groupKey` 和 `count`，不用强转。插件用的就是这个判断。

## 分组表头的字段

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `groupKey` | `string` | 这一组的分组 key。 |
| `count` | `number` | 这一组有多少行。折叠着也是这个数。 |
| `id` | `string` | 分组表头的稳定 id，由分组 key 拼出来。 |
