Filter bar
ProBuild filters one at a time: pick a field, a condition and a value (text, a number, one of the field's options, or a time window with presets like "Last 7 days"), then Add. The rules show as chips, each removable, with Clear all. The bar gives you FilterRule[] to query with; useFilterChips gives the same chips anywhere else.
Nothing is added until the person presses Add, and Add says what is missing until the rule is whole. The bar never filters anything itself: it hands the rules to you.
Version 5.2.0· other versions
This page is for 5.2.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).
Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0, 2.5.1, 2.6.0, 2.6.1 and 2.6.2: the page came later, or had another name then.
Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.
Preview
Loading the preview
Install
First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/filter-bar
Brings in: button, data, date-range-picker, filter-chips, input, label, locale, locked, number-input, popover, select, tokens, utils · npm: lucide-react@^1.67.0
Usage
"use client"
import * as React from "react"
import { FilterBar, type FilterField, type FilterRule } from "@/components/candor/filter-bar"
const fields: FilterField[] = [
{
id: "status",
label: "Status",
kind: "option",
options: [
{ value: "running", label: "Running" },
{ value: "waiting", label: "Waiting on a person" },
{ value: "failed", label: "Failed" },
],
},
{ id: "agent", label: "Agent", kind: "text" },
{ id: "cost", label: "Cost (USD)", kind: "number" },
{ id: "started", label: "Started", kind: "date" },
]
// The bar hands you the rules; your query applies them. Here they're only printed.
export default function Example() {
const [rules, setRules] = React.useState<FilterRule[]>([{ id: "seed", field: "status", operator: "isNot", value: "failed" }])
return (
<div className="flex w-full flex-col gap-3">
<FilterBar fields={fields} value={rules} onValueChange={setRules} />
<pre className="overflow-x-auto rounded-md bg-muted p-3 text-xs">{JSON.stringify(rules.map(({ field, operator, value }) => ({ field, operator, value })), null, 2)}</pre>
</div>
)
}<FilterBar>
| Prop | Type | Default | Description |
|---|---|---|---|
| fieldsrequired | FilterField[] | — | What the person can filter on. |
| value | FilterRule[] | undefined | — | The rules, when you control them. |
| defaultValue | FilterRule[] | undefined | — | The rules at first, when you don't control them. |
| onValueChange | ((rules: FilterRule[]) => void) | undefined | — | Called with every rule each time one is added or removed. |
| className | string | undefined | — | Classes for the bar. |
Functions
useFilterChips(rules: FilterRule[], fields: FilterField[])The chips for a set of rules, in the locale's words: "Status: not Closed", "Cost: more than 5". Pass them to FilterChips wherever the active filters show. A rule whose field is gone shows its field id and raw value.
Values
filterOperatorsThe conditions each kind offers, the first being the one picked when the field is.
Record<FilterFieldKind, FilterOperator[]>Strings
The fixed words, in English by default. Change any of them under filterBar in <LocaleProvider strings={{ … }}>; the table is exported as filterBarStrings.
| Key | English |
|---|---|
| add | Add filter |
| field | Field |
| operator | Condition |
| value | Value |
| chooseField | Choose a field |
| chooseValue | Choose a value |
| apply | Add |
| needField | Choose a field first. |
| needValue | Fill in a value first. |
| is | is |
| isNot | is not |
| contains | contains |
| doesNotContain | doesn’t contain |
| equals | equals |
| notEquals | doesn’t equal |
| greaterThan | is more than |
| lessThan | is less than |
| within | is within |
| chipIs | {value} |
| chipIsNot | not {value} |
| chipContains | contains “{value}” |
| chipDoesNotContain | doesn’t contain “{value}” |
| chipEquals | {value} |
| chipNotEquals | not {value} |
| chipGreaterThan | more than {value} |
| chipLessThan | less than {value} |
| chipWithin | {value} |
Types
- FilterFieldKind
- What a field holds, which decides its conditions and how its value is entered.
"text" | "number" | "option" | "date"- FilterOperator
- How a rule compares the field with its value. Which ones a field offers follows its kind.
"is" | "isNot" | "contains" | "doesNotContain" | "equals" | "notEquals" | "greaterThan" | "lessThan" | "within"- FilterField
- Something a person can filter on.
{ /** Kept on each rule as `field`. */ id: string; /** The field's name in your app's words: "Status", "Owner", "Started". */ label: string; /** What it holds: free text, a number, one of `options`, or a time. */ kind: FilterFieldKind; /** For an `option` field: the values to choose from. */ options?: { value: string; label: string; }[] | undefined; /** The conditions to offer, when not all of the kind's. */ operators?: FilterOperator[] | undefined; /** For a `date` field: the windows to offer. Defaults to DateRangePicker's. */ presets?: DateRangePreset[] | undefined; }- FilterRule
- One filter the person added: a field, a condition and a value. Your query reads these.
{ /** The rule's own id, which is also its chip's. */ id: string; /** The `id` of the field it filters. */ field: string; /** How it compares. */ operator: FilterOperator; /** Text for a `text` field, an option's `value` for an `option` field, a number, or a time window. */ value: string | number | DateRangeValue; }