# useTableColumnResize

## Usage

Hook that returns a `TablePlugin` adding draggable resize handles to column header borders. Every resizable column gets a handle on its inline end, except the last proportional column: that one keeps flexing to fill whatever space is left. Dragging previews the new widths, and releasing the pointer commits them: `onColumnResizeEnd` receives a map of every column key whose width changed, and you merge it into `columnWidths`.

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

A proportional column hands width to the next resizable column, so the table stays full width while you drag. A pixel column has no neighbour to lean on, so only its own width changes and the last column takes up the difference.

```ts
const columnWidths = ref<Record<string, number>>({})
const resizePlugin = useTableColumnResize(() => ({
  columnWidths: columnWidths.value,
  minWidth: 80,
  maxWidth: 400,
  onColumnResizeEnd(updates) {
    columnWidths.value = { ...columnWidths.value, ...updates }
  },
}))
```

The config is read again on every access, so pass a getter when the widths live in a `ref`. A plain object works too, but it keeps whatever widths it had the first time the table asked.

### How narrow and how wide

Each column's floor comes from the column itself: `proportional(n, { minWidth })` uses that `minWidth`, a `pixel(n)` column cannot get narrower than `n`, and a column without a `width` stops at 60px. Setting `minWidth` on the config overrides all of them, and `maxWidth` caps every column during a resize.

### Keyboard and RTL

The handle is a WAI-ARIA window splitter: focus it and use ArrowLeft / ArrowRight to move 10px, hold Shift for 50px, Home to jump to the narrowest allowed width, and End to jump to `maxWidth`. Right and left follow the writing direction, so in RTL the arrows resize the other way.

## Examples

### useTableColumnResize — Draggable Columns

A Table using useTableColumnResize. Drag the right edge of any column header to resize; the width is committed when you release the pointer. The remaining proportional columns share what is left.

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

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

const users: User[] = [
  { id: '1', name: 'Alice Johnson', email: 'alice@example.com', role: 'Engineer' },
  { id: '2', name: 'Bob Smith', email: 'bob@example.com', role: 'Designer' },
  { id: '3', name: 'Charlie Brown', email: 'charlie@example.com', role: 'PM' },
  { id: '4', name: 'Diana Prince', email: 'diana@example.com', role: 'Engineer' },
  { id: '5', name: 'Eve Davis', email: 'eve@example.com', role: 'Analyst' },
]

const columns: TableColumn<User>[] = [
  { key: 'name', header: 'Name' },
  { key: 'email', header: 'Email', width: proportional(2) },
  { key: 'role', header: 'Role', width: pixel(120) },
]

const columnWidths = ref<Record<string, number>>({})
const resizePlugin = useTableColumnResize<User>(() => ({
  columnWidths: columnWidths.value,
  onColumnResizeEnd: (updates) => {
    columnWidths.value = { ...columnWidths.value, ...updates }
  },
}))
</script>

<template>
  <Table
    :data="users"
    :columns="columns"
    id-key="id"
    has-hover
    :plugins="{ columnResize: resizePlugin }"
  />
</template>
```

## Parameters

`useTableColumnResize()` takes one config object. Wrap it in a getter or a `ref` so the table reads the current widths: `useTableColumnResize(() => ({ ... }))`.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `columnWidths` | `Record<string, number>` | `{}` | Controlled pixel-width overrides keyed by column key. When a key is present it overrides the column's declared width. |
| `onColumnResizeEnd` | `(updates: Record<string, number>) => void` | — | Called when a resize completes (pointer release or a key press). Receives a map of every column key whose width changed; merge it into your `columnWidths` state. The last column is left out because its width follows the space that is left. |
| `minWidth` | `number` | — | Global minimum column width in pixels during a resize. Overrides the per-column floors. |
| `maxWidth` | `number` | `Infinity` | Global maximum column width in pixels during a resize. |
| `columns` | `readonly TableColumn[]` | — | Column definitions, used to derive per-column minimums and to tell proportional columns from pixel ones when picking the neighbour and the last column. `Table` passes its own columns, so you only need this when you call the helper functions yourself. |

## Returns

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

| Name | Type | Description |
| --- | --- | --- |
| `widthFor` | `(column: TableColumn) => number \| undefined` | Returns the committed override for one column, or `undefined` when the column has none. |
| `resizeFor` | `(column: TableColumn, columns: readonly TableColumn[]) => TableColumnResizeContribution \| null` | Describes how one column takes part in a resize: its current width, its minimum and maximum, the neighbour that gives up width, and whether the column gets a handle. |
| `onColumnResizeEnd` | `(updates: Record<string, number>) => void` | Called by `Table` with every changed column. It rounds each width to whole pixels and passes the map on to your `onColumnResizeEnd`. |

## Helpers

The same module exports the pieces the plugin is built from, so you can reuse the arithmetic in your own UI.

| Name | Type | Description |
| --- | --- | --- |
| `columnResizeMinWidth` | `(width: ColumnWidth \| undefined, globalMinWidth?: number) => number` | The narrowest a column may get: the global minimum when set, otherwise the column's own floor. |
| `isProportionalColumnWidth` | `(width: ColumnWidth \| undefined) => boolean` | True for a proportional column and for a column with no declared width. |
| `resizeColumnWidths` | `(plan: ColumnResizePlan, delta: number) => number[]` | Applies a pointer movement in pixels to a set of column snapshots and returns every resulting width. |
| `columnResizeStep` | `(isShiftKey: boolean) => number` | How far one arrow key press moves a handle: 10px, or 50px with Shift. |
| `columnResizeDirectionSign` | `(direction: string) => 1 \| -1` | The sign a horizontal movement gets in the current writing direction. RTL returns `-1`. |
