# useTableFilterState

## Usage

Managed state hook for table filtering. Returns the current filter map, an onChange handler, and a way to clear everything. Pass the map and the handler to `useTableFiltering`, and the map to `applyTableFilters()` to get the matching rows.

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

A filter is "off" when its key is missing. Setting `null`, an empty string, or an empty array removes the key instead of storing an empty value, so a truthy key in the map always means a live filter.

Give it an initial map when the table should open with a filter already applied. Pass the map directly, or wrap it as `initialState`. The initial object is shallow copied, so later writes never touch it.

```ts
const direct = useTableFilterState({ role: 'Engineer' })
const wrapped = useTableFilterState({ initialState: { role: 'Engineer' } })
```

## Examples

### Table — Inline Filters

Inline filter controls with the filter map held by useTableFilterState.

```vue
<script setup lang="ts">
import type { TableColumn, TableFilterField } from '@astryx-vue/core/Table'
import { applyTableFilters, proportional, Table, useTableFiltering, useTableFilterState } from '@astryx-vue/core/Table'
import { computed } from 'vue'

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

const employees: Employee[] = [
  { id: '1', name: 'Alice Johnson', email: 'alice@example.com', role: 'Engineer', department: 'Platform' },
  { id: '2', name: 'Bob Smith', email: 'bob@example.com', role: 'Designer', department: 'Product' },
  { id: '3', name: 'Charlie Brown', email: 'charlie@example.com', role: 'Manager', department: 'Platform' },
  { id: '4', name: 'Diana Prince', email: 'diana@example.com', role: 'Engineer', department: 'Infra' },
  { id: '5', name: 'Eve Davis', email: 'eve@example.com', role: 'Admin', department: 'Operations' },
]

const fields: TableFilterField[] = [
  { key: 'name', type: 'string', label: 'Name' },
  { key: 'email', type: 'string', label: 'Email' },
  {
    key: 'role',
    type: 'enum',
    label: 'Role',
    options: [
      { value: 'Engineer', label: 'Engineer' },
      { value: 'Designer', label: 'Designer' },
      { value: 'Manager', label: 'Manager' },
      { value: 'Admin', label: 'Admin' },
    ],
  },
]

const columns: TableColumn<Employee>[] = [
  { key: 'name', header: 'Name', width: proportional(1), filter: 'name' },
  { key: 'email', header: 'Email', width: proportional(2), filter: 'email' },
  { key: 'role', header: 'Role', width: proportional(1), filter: 'role' },
  { key: 'department', header: 'Department', width: proportional(1) },
]

const { filters, onFilterChange } = useTableFilterState()
const filterPlugin = useTableFiltering<Employee>(() => ({
  filters: filters.value,
  onFilterChange,
  fields,
  variant: 'inline',
}))
const rows = computed(() => applyTableFilters(employees, filters.value, fields))
</script>

<template>
  <Table
    :data="rows"
    :columns="columns"
    id-key="id"
    :plugins="{ filter: filterPlugin }"
  />
</template>
```

### Table — Popover Filters

Popover filter controls with the same managed state.

```vue
<script setup lang="ts">
import type { TableColumn, TableFilterField } from '@astryx-vue/core/Table'
import { applyTableFilters, proportional, Table, useTableFiltering, useTableFilterState } from '@astryx-vue/core/Table'
import { computed } from 'vue'

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

const employees: Employee[] = [
  { id: '1', name: 'Alice', email: 'alice@example.com', role: 'Engineer', department: 'Platform' },
  { id: '2', name: 'Bob', email: 'bob@example.com', role: 'Designer', department: 'Product' },
  { id: '3', name: 'Charlie', email: 'charlie@example.com', role: 'Manager', department: 'Platform' },
  { id: '4', name: 'Diana', email: 'diana@example.com', role: 'Engineer', department: 'Infrastructure' },
  { id: '5', name: 'Eve', email: 'eve@example.com', role: 'Admin', department: 'Operations' },
]

const fields: TableFilterField[] = [
  { key: 'name', type: 'string', label: 'Name' },
  { key: 'email', type: 'string', label: 'Email' },
  {
    key: 'role',
    type: 'enum',
    label: 'Role',
    options: [
      { value: 'Engineer', label: 'Engineer' },
      { value: 'Designer', label: 'Designer' },
      { value: 'Manager', label: 'Manager' },
      { value: 'Admin', label: 'Admin' },
    ],
  },
]

const columns: TableColumn<Employee>[] = [
  { key: 'name', header: 'Name', width: proportional(1), filter: 'name' },
  { key: 'email', header: 'Email', width: proportional(2), filter: 'email' },
  { key: 'role', header: 'Role', width: proportional(1), filter: 'role' },
  { key: 'department', header: 'Department', width: proportional(1) },
]

const { filters, onFilterChange } = useTableFilterState()
const filterPlugin = useTableFiltering<Employee>(() => ({
  filters: filters.value,
  onFilterChange,
  fields,
}))
const rows = computed(() => applyTableFilters(employees, filters.value, fields))
</script>

<template>
  <Table
    :data="rows"
    :columns="columns"
    id-key="id"
    :plugins="{ filter: filterPlugin }"
  />
</template>
```

## Parameters

`useTableFilterState()` takes one optional argument, in either of two shapes.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `initial` | `TableFilterState` | `{}` | The initial filter map itself. It is shallow copied, so the object you pass is never written to. |
| `initial.initialState` | `TableFilterState` | `{}` | The same map behind an `initialState` key: `useTableFilterState({ initialState })`. A filter value is never an object, so a key named `initialState` with an object value can only be this form. |

## Returns

| Name | Type | Description |
| --- | --- | --- |
| `filters` | `Ref` | The live filter map. Read `filters.value` for the current state. |
| `onFilterChange` | `(key: string, value: TableFilterValue \| null) => void` | Sets or clears one field. The map is replaced with a new object, so a getter over it stays in sync. |
| `clearAll` | `() => void` | Empties the map. Every filter turns off at once. |

### TableFilterState

| Name | Type | Description |
| --- | --- | --- |
| `[fieldKey]` | `TableFilterValue` | Field key to filter value. `TableFilterValue` is `string \| number \| string[]`. |
