Docs
Filter Bar
Filter Bar
An advanced filtering component with support for multiple conditions and operators.
Loading...
import { format } from 'date-fns'
import { useState } from 'react'
import { DateRange } from 'react-day-picker'
import { Button, Calendar } from 'ui'
import { CustomOptionProps, FilterBar, FilterGroup } from 'ui-patterns'
function CustomDatePicker({ onChange, onCancel, search }: CustomOptionProps) {
const [date, setDate] = useState<DateRange | undefined>(
search
? {
from: new Date(search),
to: undefined,
}
: undefined
)
return (
<div className="w-[300px] space-y-4">
<Calendar
initialFocus
mode="range"
defaultMonth={date?.from}
selected={date}
onSelect={setDate}
className="w-full"
/>
<div className="flex justify-end gap-2 py-3 px-4 border-t">
<Button type="default" onClick={onCancel}>
Cancel
</Button>
<Button
type="primary"
onClick={() =>
onChange(
date?.from
? date.to
? `${format(date.from, 'yyyy-MM-dd')} - ${format(date.to, 'yyyy-MM-dd')}`
: format(date.from, 'yyyy-MM-dd')
: ''
)
}
>
Apply
</Button>
</div>
</div>
)
}
const filterProperties = [
{
label: 'Name',
name: 'name',
type: 'string' as const,
operators: [
{ value: '=', label: 'Equals' },
{ value: '!=', label: 'Not equals' },
{ value: 'CONTAINS', label: 'Contains' },
{ value: 'STARTS WITH', label: 'Starts with' },
{ value: 'ENDS WITH', label: 'Ends with' },
],
},
{
label: 'Status',
name: 'status',
type: 'string' as const,
options: [
{ label: 'Active', value: 'active' },
{ label: 'Inactive', value: 'inactive' },
{ label: 'Pending', value: 'pending' },
],
operators: ['=', '!='],
},
{
label: 'Type',
name: 'type',
type: 'string' as const,
options: async (search?: string) => {
await new Promise((resolve) => setTimeout(resolve, 500))
const allOptions = ['user', 'admin', 'guest']
return search
? allOptions.filter((option) => option.toLowerCase().includes(search.toLowerCase()))
: allOptions
},
operators: ['=', '!='],
},
{
label: 'Time period',
name: 'created_at',
type: 'date' as const,
options: [
{ label: 'Today', value: format(new Date(), 'yyyy-MM-dd') },
{ label: 'Yesterday', value: format(new Date(Date.now() - 86400000), 'yyyy-MM-dd') },
{ label: 'Last 7 days', value: format(new Date(Date.now() - 7 * 86400000), 'yyyy-MM-dd') },
{ label: 'Last 30 days', value: format(new Date(Date.now() - 30 * 86400000), 'yyyy-MM-dd') },
{
label: 'Pick a date...',
component: (props: CustomOptionProps) => <CustomDatePicker {...props} />,
},
],
operators: ['=', '!=', '>', '<', '>=', '<='],
},
{
label: 'Priority',
name: 'priority',
type: 'number' as const,
options: {
component: (props: CustomOptionProps) => (
<div className="p-6">
<Button onClick={() => props.onChange('1')}>Custom value</Button>
</div>
),
},
operators: ['=', '!=', '>', '<', '>=', '<='],
},
]
const initialFilters: FilterGroup = {
logicalOperator: 'AND',
conditions: [],
}
export function FilterBarDemo() {
const [filters, setFilters] = useState<FilterGroup>(initialFilters)
const [freeformText, setFreeformText] = useState('')
return (
<div className="w-full">
<FilterBar
filterProperties={filterProperties}
freeformText={freeformText}
onFreeformTextChange={setFreeformText}
filters={filters}
onFilterChange={setFilters}
/>
</div>
)
}Usage
The Filter Bar component provides advanced filtering capabilities with support for multiple conditions, operators, and different field types. It can be used with both static and async options.
const filterProperties = [
{
label: 'Name',
name: 'name',
type: 'string',
operators: ['=', '!=', 'CONTAINS', 'STARTS WITH', 'ENDS WITH'],
},
{
label: 'Status',
name: 'status',
type: 'string',
options: ['active', 'inactive', 'pending'],
operators: ['=', '!='],
},
]
export function FilterDemo() {
const [filters, setFilters] = useState<FilterGroup>(initialFilters)
const [freeformText, setFreeformText] = useState('')
return (
<FilterBar
filterProperties={filterProperties}
freeformText={freeformText}
onFreeformTextChange={setFreeformText}
filters={filters}
onFilterChange={setFilters}
/>
)
}API Reference
FilterProperty
interface FilterProperty {
label: string
name: string
type: 'string' | 'number' | 'date' | 'boolean'
operators: string[]
options?: string[] | ((search?: string) => Promise<string[]> | string[])
}FilterGroup
interface FilterGroup {
logicalOperator: 'AND' | 'OR'
conditions: (FilterCondition | FilterGroup)[]
}FilterCondition
interface FilterCondition {
propertyName: string
value: string | number | boolean | Date
operator: string
}Component Props
| Prop | Type | Description |
|---|---|---|
| filterProperties | FilterProperty[] | Array of properties that can be filtered |
| filters | FilterGroup | Current filter state |
| onFilterChange | (filters: FilterGroup) => void | Callback when filters change |
| freeformText | string | Current free-form search text |
| onFreeformTextChange | (text: string) => void | Callback when free-form text changes |
| actions | FilterBarAction[]? | Optional custom actions to show in the menu |
| isLoading | boolean? | If true, dims the bar while work is in progress |
Custom actions (e.g. AI)
You can append custom actions to the property menu. Each action receives the current free-form input value and the active group's path so you can plug in AI, saved queries, etc.
const actions = [
{
value: 'ai-filter',
label: 'Filter by AI',
onSelect: async (inputValue, { path }) => {
const response = await fetch('/api/filter-ai', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt: inputValue, path }),
})
const group = (await response.json()) as FilterGroup
// Replace your filter state at the provided path with the returned group
setFilters((prev) => updateGroupAtPath(prev, path, group))
},
},
]
<FilterBar
filterProperties={filterProperties}
filters={filters}
onFilterChange={setFilters}
freeformText={freeformText}
onFreeformTextChange={setFreeformText}
actions={actions}
/>