---
id: "packages/active-grid/filters-api"
title: "Filters API"
description: "Filter types, operator factories and filter components."
url: "/packages/active-grid/filters-api"
locale: "en"
revision: "961d388586fc639a"
---

# Filters API

> [!NOTE]
> **The package's own reference**
>
> The text below is the package's own API reference, included unchanged. The
> complete list of exports, generated from the source, is on [All
> exports](/packages/active-grid/api).

`FilterValue` discriminated union, operator arrays + factories,
`evaluateFilter`, `advancedFilterFn`, `DateFilterPreset`, and the built-in
filter components. For tutorial-style how-to see
`../guides/filters.md`.

## FilterValue Types

```typescript
import {type FilterValue, type FilterOperator} from '@workspace/active-grid';
```

`FilterValue` is a discriminated union based on the operator:

```typescript
type FilterValue = NoValueFilter | SingleValueFilter | RangeFilter;

type NoValueFilter = {
  operator: 'isEmpty' | 'isNotEmpty';
};

type SingleValueFilter = {
  operator: TextOperator | NumberOperator | DateOperator;
  value: string | number | boolean;
};

type RangeFilter = {
  operator: 'between' | 'inRange';
  value: string | number;
  valueTo: string | number;
};
```

### Operator Types

| Type | Operators |
| --- | --- |
| `TextOperator` | `contains`, `notContains`, `startsWith`, `endsWith`, `equals`, `notEquals` |
| `NumberOperator` | `equals`, `notEquals`, `greaterThan`, `greaterThanOrEqual`, `lessThan`, `lessThanOrEqual` |
| `DateOperator` | `onDate`, `before`, `after` |
| `RangeOperator` | `between`, `inRange` |
| `NoValueOperator` | `isEmpty`, `isNotEmpty` |

## Operator Factories

```typescript
import {
  getTextOperators,
  getNumberOperators,
  getDateOperators,
  getBooleanOperators,
  getOperatorsForType,
} from '@workspace/active-grid';
```

Each factory takes a required `GridTranslateFunction` for the labels; there
is no English default (pass `englishGridTranslator` for English).

```typescript
type FilterOperatorOption = {
  value: FilterOperator;
  label: string;
  requiresValue: boolean;
  requiresSecondValue?: boolean;
};

const operators: FilterOperatorOption[] = getNumberOperators(t);
// → [{value: 'equals', label: t('grid.operators.equals'), requiresValue: true}, …]
```

## DateFilterPreset

```typescript
import {
  type DateFilterPreset,
  DEFAULT_DATE_PRESETS,
} from '@workspace/active-grid';
```

```typescript
interface DateFilterPreset {
  key: string; // Translation key (e.g., 'grid.filter.datePreset.today')
  label?: string; // Optional display label — used instead of t(key) when provided
  type: 'single' | 'range';
  getValue: (presentation?: GridPresentation) => {from: Date; to?: Date};
}
```

`DEFAULT_DATE_PRESETS` provides 8 built-in presets: today, yesterday, last
7 days, last 30 days, this month, last month, this quarter, this year.
Relative presets require an explicit presentation time zone. Their dates carry
calendar days, which the date filter serializes as `yyyy-MM-dd`.
Pass a custom array via `meta.dateFilterPresets` or `false` to disable. How to
write a custom preset without reading the browser's time zone:
`../guides/filters.md` § Date Filter Configuration.

## evaluateFilter / advancedFilterFn / evaluateFilterExpression

```typescript
import {
  evaluateFilter,
  evaluateFilterExpression,
  advancedFilterFn,
} from '@workspace/active-grid';
```

| Function | Description |
| --- | --- |
| `evaluateFilter(value, filter)` | Evaluate a `FilterValue` against a cell value. |
| `evaluateFilterExpression(row, fe)` | Evaluate a `FilterExpression` tree (AND/OR groups) against a full row. |
| `advancedFilterFn` | Filter function for the engine that supports operator-based filtering. |

## createLocaleSortFn

```typescript
import {createLocaleSortFn} from '@workspace/active-grid';
```

Factory that produces a sort function using `Intl.Collator` for
locale-aware string sorting. Default sort uses ASCII codepoints; for
locale-correct ordering pass `sortFn: createLocaleSortFn(locale)` on
relevant columns.

```typescript
createLocaleSortFn(locale: string, options?: Intl.CollatorOptions)
```

- `locale` is required and must be a non-empty string; there is no host
  default. An empty or missing locale throws a TypeError.
- The collator defaults to `{numeric: true, sensitivity: 'base'}`; `options`
  are merged over those defaults.
- The returned function compares `row.getValue(columnId)` of two rows and
  returns a negative number, zero or a positive number. A string is compared as written, a finite number
  as its string form, and every other value (absent, `NaN`, `±Infinity`,
  boolean, date, object) as the empty string.

## Filter Components

For custom filter UIs, the package exports the five built-in filter
components plus a set of popover primitives.

```typescript
import {
  TextFilter,
  NumberFilter,
  DateFilter,
  SelectFilter,
  BooleanFilter,
  FilterPopover,
  FilterPopoverTrigger,
  FilterPopoverContent,
  FilterPopoverHeader,
  FilterPopoverBody,
  FilterIcon,
  FilterClearButton,
  FilterCheckboxItem,
  FilterDateInput,
  FilterNumberInput,
  FilterOperatorSelect,
  FilterSearchInput,
  FilterTextInput,
  FilterWrapper,
} from '@workspace/active-grid';
```

In most cases the built-in filter row handles everything automatically
based on `meta.filterType` — these exports are for custom filter-builder
UIs and advanced consumers.

## The layout contract of the filter row

All six built-in filters share one internal anatomy, with one rule: **the
value gives way, the controls never do.** That anatomy is an internal component
and not a public export -- consumers do not compose it and do not need to know
it.

```text
[ value / input            ][ clear 24px ][ menu 24px ]
  min-w-0, shrinks to zero     shrink-0      shrink-0
```

| What | Behaviour |
| --- | --- |
| Value area | shrinks first, down to zero; text truncates with an ellipsis |
| Clear button (X) | only with an active filter; 24x24 target; does not shrink; never disappears on width |
| Filter menu button (funnel) | always present; 24x24 target; does not shrink |
| Right-aligned column | `meta.headerAlign: 'right'` mirrors the controls to the left; the slots stay the same |
| Full value | is in the rendered content, so screen readers get it untruncated |

**A filterable column automatically gets an effective minimum width of
80px.** That floor is derived from the cell padding, the two targets and the
gaps between them, and protects only the CONTROLS. It deliberately does not
keep translated values readable: the same state needs 122px in English and
161px in Spanish, and the count in it is dynamic, so a floor that guarantees
text does not exist.

The floor only RAISES. A column with a larger `minSize` of its own, or a grid
with a larger `defaultMinSize`, is left alone; with
`settings.showFilterRow: false` it does not apply.

**A consumer does not need to declare anything for this.** There is no prop,
no opt-in and no migration, and there is no reason to add an own `minWidth` or
a local CSS workaround for the clear button. A `meta.customFilter` deliberately
falls outside the shell and owns its own anatomy.

Grounds and trade-off: `DECISIONS.md` ADR-027.

## Filter Expression Tree

For the AND/OR composite-filter UI, the package exports a small typed tree:

```typescript
import {
  type FilterExpression,
  type FilterCondition,
  type FilterGroup,
  type FilterExpressionNode,
  isFilterGroup,
  evaluateFilterExpression,
} from '@workspace/active-grid';
```

| Symbol | Type | Description |
| --- | --- | --- |
| `FilterCondition` | `{ id: string; field: string; operator: FilterOperator; value?: unknown; valueTo?: unknown }` | A single filter condition on a column. |
| `FilterGroup` | `{ id: string; combinator: 'and' \| 'or'; children: FilterExpressionNode[] }` | A group with AND/OR logic combining children. |
| `FilterExpressionNode` | `FilterCondition \| FilterGroup` | A node in the tree — either a condition leaf or a group. |
| `FilterExpression` | `FilterGroup` | Root of the tree — always a group. |
| `isFilterGroup(node)` | `(node: FilterExpressionNode) => node is FilterGroup` | Type guard. Returns `true` when the node has a `combinator` field. |
| `evaluateFilterExpression` | `<TData>(row: TData, expr: FilterExpression) => boolean` | Client-side row evaluator. Returns `true` if the row matches the expression. |

`evaluateFilterExpression` ignores an incomplete condition: one without an
operator, or without a value its operator reads (`undefined`, `null`, `''` or a
non-finite number; `0` and `false` are values; a range needs both ends).
`isEmpty` and `isNotEmpty` need no value. A group judges only its complete
children, and a group without any matches every row.

Tutorial: `../guides/filter-builder.md`.

## Server-mode filtering

`active-grid` does not build database queries. In `mode="server"` the filter
and global-search state is forwarded verbatim in `ServerFetchParams`, and the
resource view in `@workspace/active-resources` translates it into a query
server-side (see `./resource-mode.md`). The
`evaluateFilter` / `advancedFilterFn` helpers above apply **only** to
`mode="client"` in-memory filtering.
