# useTableStickyColumns

## Usage

Hook that returns a TablePlugin which pins a contiguous run of columns to the start and/or end edge of the table. Pinned columns get cumulative offsets and a soft, scroll-aware shadow over the scrolling content. An empty config (`{}`) is a valid no-op that pins nothing.

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

## Examples

### useTableStickyColumns — Pinned Columns

Name stays on the left and Friday stays on the right while the days in between scroll. The shadow over the scrolling content tells you which side still has hidden columns.

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

interface Shift extends Record<string, unknown> {
  id: string
  name: string
  monday: string
  tuesday: string
  wednesday: string
  thursday: string
  friday: string
}

const shifts: Shift[] = [
  { id: '1', name: 'Alice', monday: 'Desk', tuesday: 'Desk', wednesday: 'Remote', thursday: 'Desk', friday: 'Off' },
  { id: '2', name: 'Bob', monday: 'Remote', tuesday: 'Desk', wednesday: 'Desk', thursday: 'Remote', friday: 'Desk' },
  { id: '3', name: 'Carol', monday: 'Off', tuesday: 'Remote', wednesday: 'Desk', thursday: 'Desk', friday: 'Remote' },
]

const columns: TableColumn<Shift>[] = [
  { key: 'name', header: 'Name', width: pixel(140) },
  { key: 'monday', header: 'Monday', width: pixel(140) },
  { key: 'tuesday', header: 'Tuesday', width: pixel(140) },
  { key: 'wednesday', header: 'Wednesday', width: pixel(160) },
  { key: 'thursday', header: 'Thursday', width: pixel(140) },
  { key: 'friday', header: 'Friday', width: pixel(140) },
]

const sticky = useTableStickyColumns<Shift>({
  startKeys: ['name'],
  endKeys: ['friday'],
})
</script>

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

## Properties

The config is a `MaybeRefOrGetter`, so pass a getter or a ref to change the pinned keys at runtime.

## Parameters

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `startKeys` | `readonly string[]` | — | Column keys pinned to the start (inline-start / left in LTR) edge: the contiguous run from the first column through the last listed key. |
| `endKeys` | `readonly string[]` | — | Column keys pinned to the end (inline-end / right in LTR) edge: the contiguous run from the first listed key through the last column. |

## Returns

| Field | Type | Description |
| --- | --- | --- |
| `cellPatch` | `(input: TableCellPatchInput) => TableCellPatch \| null` | Marks a header or body cell as sticky. The offset is cumulative over the pinned run: each pinned column adds the floor width of the one before it. Returns `null` for columns that stay in flow, and for every column while both key lists are empty. |
| `bindScrollRoot` | `(element: HTMLElement \| null) => void` | The table calls this with its scroll container, and again with `null` when it unmounts. The hook listens to scroll and resize there and writes `--table-sticky-shadow-start` / `--table-sticky-shadow-end` as `'1'` or `'0'`, which drive the soft shadow. The listener is removed when the scope is disposed. |

A pinned cell gets `position: sticky`, an inset on the start or end edge, `z-index` 3 for a header cell and 1 for a body cell, and the classes `astryx-table__sticky` plus `astryx-table__sticky--start` or `astryx-table__sticky--end`.
