# useTableRowExpansion

## Usage

Hook that returns a TablePlugin which expands a full-width detail panel below a row, rendered by the consumer via renderExpanded(item). Adds a leading chevron column and a right-click "Expand/Collapse row" action; the consumer owns the expandedKeys set. The panel spans the row but starts its content at the first column, past the chevron, so detail lines up with the labels above it; the inset follows the table density. Use it for master-detail rows (order details, forms, charts, nested tables). For hierarchical data where child rows reuse the parent columns, use useTableTreeData + useTableTreeState instead.

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

`renderExpanded` returns a `VNodeChild`, so build the panel with `h()` or render a component you imported. The config is read again on every access: pass a getter, and replace the expanded set with a new one instead of mutating it.

```ts
const expansion = useTableRowExpansion(() => ({
  expandedKeys: expanded.value,
  onToggle: toggle,
  getRowKey: order => order.id,
  renderExpanded: order => h('p', order.note),
}))
```

The chevron column is prepended to your columns under the key `__expansion`. It is 40px wide, its header is hidden from sight but kept for screen readers, and it cannot be resized.

## Examples

### useTableRowExpansion — Detail Panel

A table where each row opens a full-width detail panel. Click the chevron, or right-click a row for the expand action.

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

interface Order extends Record<string, unknown> {
  id: string
  customer: string
  total: string
  note: string
}

const orders: Order[] = [
  { id: '1042', customer: 'Northwind', total: '$240', note: 'Two seats, ships Friday.' },
  { id: '1043', customer: 'Contoso', total: '$80', note: 'Gift wrap requested.' },
  { id: '1044', customer: 'Fabrikam', total: '$560', note: 'Hold for pickup.' },
]

const columns: TableColumn<Order>[] = [
  { key: 'id', header: 'Order', width: pixel(100) },
  { key: 'customer', header: 'Customer', width: proportional(1) },
  { key: 'total', header: 'Total', width: pixel(100) },
]

const expanded = ref(new Set<string>(['1042']))

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

const expansion = useTableRowExpansion<Order>(() => ({
  expandedKeys: expanded.value,
  onToggle: toggle,
  getRowKey: order => order.id,
  panelVariant: 'muted',
  hasRowClickExpansion: true,
  renderExpanded: order => h('p', order.note),
}))
</script>

<template>
  <Table
    :data="orders"
    :columns="columns"
    id-key="id"
    :plugins="{ expansion }"
  />
</template>
```

## Parameters

`useTableRowExpansion()` takes one config object. Wrap it in a getter or a `ref` so the panels follow the current expanded set: `useTableRowExpansion(() => ({ ... }))`.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `expandedKeys` | `ReadonlySet<string>` | — | Required. Set of currently-expanded row keys. Consumer-owned. |
| `onToggle` | `(key: string) => void` | — | Required. Called with a row key when its expansion is toggled, by the chevron or by the context-menu action. |
| `getRowKey` | `(item: T) => string` | — | Required. Derive a stable unique key from a row item. |
| `renderExpanded` | `(item: T) => VNodeChild` | — | Required. Render the detail content shown in a full-width panel below the row when it is expanded. Receives the row item. |
| `getIsItemExpandable` | `(item: T) => boolean` | — | Control which rows are expandable. Non-expandable rows show no chevron, no context-menu action, and never render a panel. Defaults to all rows expandable. |
| `panelVariant` | `'muted' \| 'transparent'` | `'transparent'` | Background behind the detail panel. `'transparent'` paints nothing: the panel is the row's continuation and takes whatever surface the table sits on, so a Card shows through and a striped table's zebra reads on unchanged. `'muted'` washes it, marking it as commentary rather than another row of data — reach for it in a bare table with no card, dividers or striping, where nothing else makes that distinction. The wash is a low-alpha near-black, so it is close to invisible over a dark card: `'muted'` is largely a light-theme effect. |
| `hasRowClickExpansion` | `boolean` | `false` | Toggle a row by clicking anywhere on it, not only on its chevron. A pointer-only convenience layered over the chevron, which stays the accessible control for keyboard and assistive tech. Clicks on interactive cell content (buttons, links, form controls) or on a text selection do not toggle, and non-expandable rows stay inert. |

## Returns

`useTableRowExpansion()` returns one `TablePlugin&lt;T&gt;`. Pass it to `Table` in `plugins`. The plugin fills three fields of the plugin contract:

| Name | Type | Description |
| --- | --- | --- |
| `transformColumns` | `(columns: TableColumn[]) => TableColumn[]` | Prepends the `__expansion` column with the chevron. |
| `rowPatch` | `(row: T, index: number, columnCount: number) => TableRowPatch \| null` | Adds the expand action to the row's context menu, the optional whole-row click, and the panel when the row is open. Returns `null` for a row that is not expandable. |
| `cellPatch` | `(input: TableCellPatchInput) => TableCellPatch \| null` | Marks the chevron cells with the `astryx-table__expansion` class so they keep their 40px width. |

### TableRowPatch

| Name | Type | Description |
| --- | --- | --- |
| `attrs` | `Record<string, string \| undefined>` | `data-expanded` is set on an open row. |
| `class` | `string` | `astryx-table__clickable` when `hasRowClickExpansion` is on. |
| `onClick` | `(event: MouseEvent) => void` | Toggles the row, unless the click landed on an interactive element or on a text selection. |
| `menu` | `TableRowMenuItem[]` | One item that expands or collapses the row. Its label is localized. |
| `panel` | `TablePanelPatch` | The detail panel below the row. Carries `variant` and `render`, and is only present while the row is open. |
