# FilterConfig

> _Since v0.1.1_

Configuration options for the FilteringPlugin.

Pass this object to the `FilteringPlugin` constructor to customize filtering
behavior, panel rendering, server-side integration, and state persistence.

#### Example

```typescript
// Basic usage with defaults
new FilteringPlugin()

// Customized local filtering
new FilteringPlugin({
  debounceMs: 200,
  caseSensitive: true,
  trackColumnState: true,
})

// Server-side filtering with custom values
new FilteringPlugin<Employee>({
  valuesHandler: async (field) => {
    const res = await fetch(`/api/employees/distinct/${field}`);
    return res.json();
  },
  filterHandler: async (filters) => {
    const params = new URLSearchParams();
    filters.forEach(f => params.append(f.field, `${f.operator}:${f.value}`));
    const res = await fetch(`/api/employees?${params}`);
    return res.json();
  },
})
```

## Properties

| Property | Type | Description |
| -------- | ---- | ----------- |
| `debounceMs?` | <code>number</code> | Debounce delay in milliseconds for the search input inside the default filter panel. Controls how long the panel waits after the user stops typing before re-filtering the unique values list. |
| `caseSensitive?` | <code>boolean</code> | Whether text-based filtering comparisons are case-sensitive. |
| `filterPanelRenderer?` | <code><a href="/grid/plugins/filtering/types/filterpanelrenderer/">FilterPanelRenderer</a></code> | Custom filter panel renderer that replaces the built-in panel content for **all** columns (unless you return `undefined` for specific columns to fall through to the next level). |
| `trackColumnState?` | <code>boolean</code> | Whether filter state should be included in column state persistence. |
| `valuesHandler?` | <code><a href="/grid/plugins/filtering/types/filtervalueshandler/">FilterValuesHandler</a></code> | Async handler for fetching unique filter values from a server. |
| `filterHandler?` | <code><a href="/grid/plugins/filtering/types/filterhandler/">FilterHandler</a>&lt;TRow&gt;</code> | Async handler for delegating filtering to a server. |

### Property Details

#### debounceMs

Debounce delay in milliseconds for the search input inside the default
filter panel. Controls how long the panel waits after the user stops
typing before re-filtering the unique values list.

Lower values feel more responsive but cause more DOM updates.
Higher values reduce work for columns with many unique values.

**Default:** `300`

```typescript
// Faster response for small datasets
new FilteringPlugin({ debounceMs: 100 })

// Slower debounce for columns with thousands of unique values
new FilteringPlugin({ debounceMs: 500 })
```

---

#### caseSensitive

Whether text-based filtering comparisons are case-sensitive.

When `false` (default), `"alice"` matches `"Alice"`, `"ALICE"`, etc.
When `true`, only exact case matches pass the filter.

Affects both:
- The core `filterRows()` logic (text operators like `contains`, `equals`, etc.)
- The search input inside the default filter panel (value list filtering)

**Default:** `false`

```typescript
// Enable case-sensitive filtering
new FilteringPlugin({ caseSensitive: true })
```

---

#### filterPanelRenderer

Custom filter panel renderer that replaces the built-in panel content
for **all** columns (unless you return `undefined` for specific columns
to fall through to the next level).

**Resolution priority** (first non-empty result wins):
1. This plugin-level `filterPanelRenderer`
2. Type-level `filterPanelRenderer` (in `gridConfig.typeDefaults[type]`)
3. Built-in panel (checkbox set filter, number range slider, or date picker)

The renderer receives the panel container element and a FilterPanelParams
object with state and action callbacks. Append your UI to the container.

**Return `undefined`** (or leave the container empty) to fall through to
the next resolution level. This lets you override only specific fields.

```typescript
// Override only the "status" column, use defaults for everything else
new FilteringPlugin({
  filterPanelRenderer: (container, params) => {
    if (params.field !== 'status') return undefined; // fall through

    params.uniqueValues.forEach(val => {
      const btn = document.createElement('button');
      btn.textContent = String(val);
      btn.onclick = () => {
        const excluded = params.uniqueValues.filter(v => v !== val);
        params.applySetFilter(excluded as unknown[]);
      };
      container.appendChild(btn);
    });
  },
})
```

```typescript
// Replace ALL filter panels with a custom component
new FilteringPlugin({
  filterPanelRenderer: (container, params) => {
    const myFilter = new MyCustomFilterElement();
    myFilter.field = params.field;
    myFilter.values = params.uniqueValues;
    myFilter.onApply = (excluded) => params.applySetFilter(excluded);
    myFilter.onClear = () => params.clearFilter();
    container.appendChild(myFilter);
  },
})
```

**See also:** [`FilterPanelParams`](/grid/plugins/filtering/interfaces/filterpanelparams.md) for all available state and action callbacks. · [`FilterPanelRenderer`](/grid/plugins/filtering/types/filterpanelrenderer.md) for the function signature.

---

#### trackColumnState

Whether filter state should be included in column state persistence.

When `true`:
- `getColumnState()` includes filter data for each column
- Filter changes fire the `column-state-change` event (debounced)
- `applyColumnState()` restores filter state from a saved snapshot

When `false` (default):
- Filters are excluded from column state entirely
- Filter changes do **not** fire `column-state-change`

Enable this when you persist column state (e.g., to localStorage or a server)
and want filter selections to survive page reloads.

**Default:** `false`

```typescript
// Persist filters alongside column order, widths, and sort state
new FilteringPlugin({ trackColumnState: true })

// Save/restore cycle:
const state = grid.getColumnState();   // includes filter data
localStorage.setItem('grid-state', JSON.stringify(state));
// ... later ...
grid.applyColumnState(JSON.parse(localStorage.getItem('grid-state')!));
```

---

#### valuesHandler

Async handler for fetching unique filter values from a server.

When provided, this handler is called **each time a filter panel opens**
instead of extracting unique values from the locally-loaded rows. The panel
shows a loading indicator while the handler resolves.

Use this for server-side or paginated datasets where the client only holds
a subset of the data and the local unique values would be incomplete.

The returned values populate `FilterPanelParams.uniqueValues` and appear
in the checkbox list (or are passed to your custom `filterPanelRenderer`).

```typescript
new FilteringPlugin({
  valuesHandler: async (field, column) => {
    const res = await fetch(`/api/data/distinct/${field}`);
    return res.json(); // ['Engineering', 'Marketing', 'Sales', ...]
  },
})
```

```typescript
// Combine with filterHandler for full server-side filtering
new FilteringPlugin<Employee>({
  valuesHandler: async (field) => {
    const res = await fetch(`/api/employees/distinct/${field}`);
    return res.json();
  },
  filterHandler: async (filters) => {
    const body = JSON.stringify(filters);
    const res = await fetch('/api/employees/filter', { method: 'POST', body });
    return res.json();
  },
})
```

**See also:** [`FilterValuesHandler`](/grid/plugins/filtering/types/filtervalueshandler.md) for the full type signature.

---

#### filterHandler

Async handler for delegating filtering to a server.

When provided, the plugin's `processRows()` hook becomes a **passthrough**
(returns rows unfiltered) and instead calls this handler whenever the
active filters change. The handler should return the filtered rows, which
replace the grid's current data.

This enables full server-side filtering for large datasets that can't be
loaded into the browser. The handler receives the complete list of active
FilterModel objects and the current row array (useful for optimistic
updates or reference).

The handler may return rows synchronously (plain array) or asynchronously
(Promise). While an async handler is pending, the grid retains its current
rows until the new data arrives.

```typescript
// Server-side filtering with query params
new FilteringPlugin<Employee>({
  filterHandler: async (filters, currentRows) => {
    const params = new URLSearchParams();
    filters.forEach(f => params.append(f.field, `${f.operator}:${f.value}`));
    const res = await fetch(`/api/employees?${params}`);
    return res.json();
  },
})
```

```typescript
// POST-based filtering with full filter models
new FilteringPlugin<Product>({
  filterHandler: async (filters) => {
    const res = await fetch('/api/products/filter', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ filters }),
    });
    return res.json();
  },
})
```

**See also:** [`FilterHandler`](/grid/plugins/filtering/types/filterhandler.md) for the full type signature. · [`FilterChangeDetail`](/grid/plugins/filtering/interfaces/filterchangedetail.md) for the event emitted after filter changes.

---
