Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
101 changes: 69 additions & 32 deletions docs/framework/alpine/guide/column-filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ import {
columnFilteringFeature,
createFilteredRowModel,
createTable,
filterFns,
filterFn_includesString,
filterFn_inNumberRange,
tableFeatures,
} from '@tanstack/alpine-table'

const features = tableFeatures({
columnFilteringFeature,
filteredRowModel: createFilteredRowModel(), // if using client-side filtering
filterFns,
filterFns: {
includesString: filterFn_includesString,
inNumberRange: filterFn_inNumberRange,
},
})

const table = createTable({
Expand All @@ -39,6 +43,8 @@ const table = createTable({
})
```

> **Note:** The `filterFns` registry above lists only the built-in filter functions this table uses. Spreading the entire built-in `filterFns` registry (`filterFns: { ...filterFns }`) still works, but it puts every built-in filter function in your bundle. Register just the functions you use, or pass a function directly to the `filterFn` column option with no registration at all.

## Column Filtering (Alpine) Guide

Filtering comes in 2 flavors: Column Filtering and Global Filtering.
Expand Down Expand Up @@ -86,21 +92,25 @@ const table = createTable({

### Client-Side Filtering

If you are using the built-in client-side filtering features, add the `columnFilteringFeature` and the `filteredRowModel` factory to your features. Import `createFilteredRowModel` and `filterFns` from TanStack Table:
If you are using the built-in client-side filtering features, add the `columnFilteringFeature` and the `filteredRowModel` factory to your features. Import `createFilteredRowModel` and the filter functions you need from TanStack Table:

```ts
import {
columnFilteringFeature,
createFilteredRowModel,
createTable,
filterFns,
filterFn_includesString,
filterFn_inNumberRange,
tableFeatures,
} from '@tanstack/alpine-table'

const features = tableFeatures({
columnFilteringFeature,
filteredRowModel: createFilteredRowModel(),
filterFns,
filterFns: {
includesString: filterFn_includesString,
inNumberRange: filterFn_inNumberRange,
},
})

const table = createTable({
Expand Down Expand Up @@ -227,28 +237,34 @@ const table = createTable({

Each column can have its own unique filtering logic. Choose from any of the filter functions that are provided by TanStack Table, or create your own.

By default there are 12 built-in filter functions to choose from:
By default there are 18 built-in filter functions to choose from:

- `includesString` - Case-insensitive string inclusion
- `includesStringSensitive` - Case-sensitive string inclusion
- `startsWith` - Case-insensitive string prefix match
- `endsWith` - Case-insensitive string suffix match
- `equalsString` - Case-insensitive string equality
- `equalsStringSensitive` - Case-sensitive string equality
- `equals` - Strict equality `===`
- `weakEquals` - Weak equality `==`
- `empty` - The row's value is nullish or whitespace-only (the filter value is an on/off flag)
- `notEmpty` - The row's value is not nullish or whitespace-only (the filter value is an on/off flag)
- `arrIncludes` - The row's array (or string) value includes at least one of the filter values
- `arrIncludesAll` - The row's array value includes every filter value
- `arrIncludesSome` - The row's array value includes at least one of the filter values
- `arrHas` - The row's scalar value equals at least one of the filter values
- `inNumberRange` - Inclusive `[min, max]` number range (endpoints normalized and swapped if reversed)
- `inDateRange` - Inclusive `[min, max]` date range accepting `Date` objects, timestamps, or date strings (blank endpoints are open-ended)
- `between` - Exclusive min/max range (blank endpoints are open-ended)
- `betweenInclusive` - Inclusive min/max range (blank endpoints are open-ended)

You can also define your own custom filter functions, either inline as the `filterFn` column option, or by name in the filter function registry that you pass to `createFilteredRowModel`.
You can also define your own custom filter functions, either inline as the `filterFn` column option, or by name in the `filterFns` registry slot on `tableFeatures`.

#### Custom Filter Functions

> **Note:** These filter functions only run during client-side filtering.

Whether you register a custom filter function in the registry passed to `createFilteredRowModel` or pass it directly as a `filterFn` column option, it should have the following signature:
Whether you register a custom filter function in the `filterFns` slot on `tableFeatures` or pass it directly as a `filterFn` column option, it should have the following signature:

```ts
const myCustomFilterFn: FilterFn<typeof features, MyData> = (
Expand Down Expand Up @@ -282,7 +298,7 @@ const columns = [
{
header: () => 'Birthday',
accessorKey: 'birthday',
filterFn: 'myCustomFilterFn', // reference a custom filter function registered with createFilteredRowModel
filterFn: 'myCustomFilterFn', // reference a custom filter function registered in features
},
{
header: () => 'Profile',
Expand All @@ -298,7 +314,8 @@ const features = tableFeatures({
columnFilteringFeature,
filteredRowModel: createFilteredRowModel(),
filterFns: {
...filterFns,
includesString: filterFn_includesString,
inNumberRange: filterFn_inNumberRange,
myCustomFilterFn: (row, columnId, filterValue) => {
return // true or false based on your custom logic
},
Expand All @@ -321,34 +338,48 @@ const table = createTable({

You can attach a few other properties to filter functions to customize their behavior:

- `filterFn.resolveFilterValue` - This optional "hanging" method on any given `filterFn` allows the filter function to transform/sanitize/format the filter value before it is passed to the filter function.
- `filterFn.resolveFilterValue` - This optional "hanging" method on any given `filterFn` allows the filter function to transform/sanitize/format the filter value before it is passed to the filter function. The table applies it once per filter (not once per row), so it is also the right place for expensive preparation work.

- `filterFn.resolveDataValue` - This optional "hanging" method normalizes each row's value before it is compared against the filter value. It is honored by every filter function built with the `constructFilterFn` helper, which includes all built-in filter functions.

- `filterFn.autoRemove` - This optional "hanging" method on any given `filterFn` is passed a filter value and expected to return `true` if the filter value should be removed from the filter state. eg. Some boolean-style filters may want to remove the filter value from the table state if the filter value is set to `false`.

The `constructFilterFn` helper builds a filter function from a value-level comparator plus those optional resolvers:

```ts
const startsWithFilterFn = <
TFeatures extends TableFeatures,
TData extends RowData,
>(
row: Row<TFeatures, TData>,
columnId: string,
filterValue: string, // resolveFilterValue below transforms the raw value to a string
) =>
row
.getValue<number | string>(columnId)
.toString()
.toLowerCase()
.trim()
.startsWith(filterValue) // toString, toLowerCase, and trim the filter value in `resolveFilterValue`
const startsWithFilterFn = constructFilterFn({
// compare the (resolved) row value against the (resolved) filter value
filter: (dataValue, filterValue) =>
Boolean(dataValue?.startsWith(filterValue)),
// normalize the filter value once, before any rows are tested
resolveFilterValue: (value) => String(value).toLowerCase().trim(),
// normalize each row's value before it reaches the comparator
resolveDataValue: (value) => String(value ?? '').toLowerCase(),
// remove the filter value from filter state if it is falsy (empty string in this case)
autoRemove: (value) => !value,
})
```

// remove the filter value from filter state if it is falsy (empty string in this case)
startsWithFilterFn.autoRemove = (val: any) => !val
Keeping the comparison in `filter` and the normalization in the resolvers pays off when you need a variant of an existing filter function: the definition is attached to the returned function, so you can spread any filter function built with `constructFilterFn` and override only what differs. For example, a version of `includesString` that also ignores diacritics (so a search for "eric" matches "Éric"):

// transform/sanitize/format the filter value before it is passed to the filter function
startsWithFilterFn.resolveFilterValue = (val: any) =>
val.toString().toLowerCase().trim()
```ts
const normalize = (value: unknown) =>
String(value ?? '')
.toLowerCase()
.normalize('NFD')
.replace(/\p{Diacritic}/gu, '')

const includesStringIgnoreDiacritics = constructFilterFn({
...filterFn_includesString, // reuse the comparator and autoRemove behavior
resolveFilterValue: normalize,
resolveDataValue: normalize,
})
```

Register the variant by name in the `filterFns` registry or pass it directly to the `filterFn` column option, just like any other custom filter function.

> **Note:** The table applies `resolveFilterValue` once per filter before any rows are tested. If you ever call a filter function directly (outside of a table), resolve the filter value yourself: `myFilterFn(row, columnId, myFilterFn.resolveFilterValue?.(rawValue) ?? rawValue)`.

### Wiring up the filter UI

TanStack Table will not add filter inputs to your table. Add them yourself on real elements (Alpine does not initialize directives inside content set with `x-html`). Read the current value with `column.getFilterValue()` and write it with `column.setFilterValue()`. Use `column.getCanFilter()` to decide whether to render an input.
Expand Down Expand Up @@ -461,7 +492,10 @@ const features = tableFeatures({
rowExpandingFeature,
filteredRowModel: createFilteredRowModel(),
expandedRowModel: createExpandedRowModel(),
filterFns,
filterFns: {
includesString: filterFn_includesString,
inNumberRange: filterFn_inNumberRange,
},
})

const table = createTable({
Expand All @@ -486,7 +520,10 @@ const features = tableFeatures({
rowExpandingFeature,
filteredRowModel: createFilteredRowModel(),
expandedRowModel: createExpandedRowModel(),
filterFns,
filterFns: {
includesString: filterFn_includesString,
inNumberRange: filterFn_inNumberRange,
},
})

const table = createTable({
Expand Down
26 changes: 12 additions & 14 deletions docs/framework/alpine/guide/fuzzy-filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ import {
createFilteredRowModel,
createSortedRowModel,
createTable,
filterFns,
globalFilteringFeature,
rowSortingFeature,
sortFns,
tableFeatures,
} from '@tanstack/alpine-table'

Expand All @@ -33,8 +31,8 @@ const features = tableFeatures({
rowSortingFeature,
filteredRowModel: createFilteredRowModel(), // if using client-side filtering
sortedRowModel: createSortedRowModel(), // if using client-side sorting
filterFns,
sortFns,
filterFns: { fuzzy: fuzzyFilter },
sortFns: { fuzzy: fuzzySort },
})

const table = createTable({
Expand All @@ -46,6 +44,8 @@ const table = createTable({
})
```

> **Note:** The `filterFns` and `sortFns` registries above list only the custom `fuzzy` functions this guide uses. Spreading the entire built-in registries (`filterFns: { ...filterFns, fuzzy: fuzzyFilter }`) still works, but it puts every built-in function in your bundle. Register just the functions you use, or pass functions directly to the `filterFn` and `sortFn` column options with no registration.
## Fuzzy Filtering (Alpine) Guide

Fuzzy filtering is a technique that allows you to filter data based on approximate matches. This can be useful when you want to search for data that is similar to a given value, rather than an exact match.
Expand Down Expand Up @@ -124,8 +124,8 @@ const features = tableFeatures({
rowSortingFeature,
filteredRowModel: createFilteredRowModel(),
sortedRowModel: createSortedRowModel(),
filterFns: { ...filterFns, fuzzy: fuzzyFilter },
sortFns,
filterFns: { fuzzy: fuzzyFilter },
sortFns: { fuzzy: fuzzySort },
filterMeta: metaHelper<FuzzyFilterMeta>(),
})
```
Expand All @@ -140,11 +140,9 @@ import {
createFilteredRowModel,
createSortedRowModel,
createTable,
filterFns,
globalFilteringFeature,
metaHelper,
rowSortingFeature,
sortFns,
tableFeatures,
} from '@tanstack/alpine-table'

Expand All @@ -154,8 +152,8 @@ const features = tableFeatures({
rowSortingFeature,
filteredRowModel: createFilteredRowModel(),
sortedRowModel: createSortedRowModel(), // needed if you want sorting with fuzzy rank
filterFns: { ...filterFns, fuzzy: fuzzyFilter },
sortFns,
filterFns: { fuzzy: fuzzyFilter },
sortFns: { fuzzy: fuzzySort },
filterMeta: metaHelper<FuzzyFilterMeta>(),
})

Expand All @@ -171,7 +169,7 @@ const table = createTable({

### Using Fuzzy Filtering with Column Filtering

To use fuzzy filtering with column filtering, pass your fuzzy filter function to `createFilteredRowModel` (merging it with the built-in `filterFns`). You can then specify the fuzzy filter by name in the `filterFn` option of the column definition:
To use fuzzy filtering with column filtering, register your fuzzy filter function in the `filterFns` slot on `tableFeatures` (as shown above). You can then specify the fuzzy filter by name in the `filterFn` option of the column definition:

```ts
const column = [
Expand All @@ -194,7 +192,7 @@ When using fuzzy filtering with column filtering, you might also want to sort th

```ts
import { compareItems } from '@tanstack/match-sorter-utils'
import { sortFns } from '@tanstack/alpine-table'
import { sortFn_alphanumeric } from '@tanstack/alpine-table'
import type { SortFn } from '@tanstack/alpine-table'

const fuzzySort: SortFn<FuzzyFeatures, Person> = (rowA, rowB, columnId) => {
Expand All @@ -209,7 +207,7 @@ const fuzzySort: SortFn<FuzzyFeatures, Person> = (rowA, rowB, columnId) => {
}

// Provide an alphanumeric fallback for when the item ranks are equal
return dir === 0 ? sortFns.alphanumeric(rowA, rowB, columnId) : dir
return dir === 0 ? sortFn_alphanumeric(rowA, rowB, columnId) : dir
}
```

Expand All @@ -228,4 +226,4 @@ You can then pass this sorting function directly to the `sortFn` option of the c
}
```

> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would only work if you also registered the function in the `sortFns` slot on `tableFeatures` (e.g. `sortFns: { ...sortFns, fuzzySort }`). Passing the function directly skips that step.
> **Note:** Unlike `filterFn: 'fuzzy'` above, `fuzzySort` is passed as a function rather than a string. A string reference like `sortFn: 'fuzzySort'` would only work if you also registered the function in the `sortFns` slot on `tableFeatures` (e.g. `sortFns: { fuzzySort }`). Passing the function directly skips that step.
12 changes: 7 additions & 5 deletions docs/framework/alpine/guide/global-filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
columnFilteringFeature,
createFilteredRowModel,
createTable,
filterFns,
filterFn_includesString,
globalFilteringFeature,
tableFeatures,
} from '@tanstack/alpine-table'
Expand All @@ -29,7 +29,7 @@ const features = tableFeatures({
columnFilteringFeature,
globalFilteringFeature,
filteredRowModel: createFilteredRowModel(), // if using client-side filtering
filterFns,
filterFns: { includesString: filterFn_includesString },
})

const table = createTable({
Expand All @@ -41,6 +41,8 @@ const table = createTable({
})
```

> **Note:** The `filterFns` registry above lists only the built-in filter function this table uses. Spreading the entire built-in `filterFns` registry (`filterFns: { ...filterFns }`) still works, but it puts every built-in filter function in your bundle. Register just the functions you use, or pass a function directly to the `globalFilterFn` option with no registration at all.

## Global Filtering (Alpine) Guide

Filtering comes in 2 flavors: Column Filtering and Global Filtering.
Expand Down Expand Up @@ -103,7 +105,7 @@ import {
columnFilteringFeature,
createFilteredRowModel,
createTable,
filterFns,
filterFn_includesString,
globalFilteringFeature,
tableFeatures,
} from '@tanstack/alpine-table'
Expand All @@ -112,7 +114,7 @@ const features = tableFeatures({
columnFilteringFeature,
globalFilteringFeature,
filteredRowModel: createFilteredRowModel(),
filterFns,
filterFns: { includesString: filterFn_includesString },
})

const table = createTable({
Expand All @@ -123,7 +125,7 @@ const table = createTable({

### Global Filter Function

The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a built-in filter function, a string that references a custom filter function registered in the `filterFns` slot on `tableFeatures`, or a custom filter function passed directly.
The `globalFilterFn` option allows you to specify the filter function that will be used for global filtering. The filter function can be a string that references a filter function (built-in or custom) registered in the `filterFns` slot on `tableFeatures`, or a filter function passed directly.

```ts
const table = createTable({
Expand Down
Loading
Loading