Filtering
useFilters()
Section titled “useFilters()”function
declare function useFilters(doc: FilterableDocument, id: string, columns: readonly FilterColumn[], options?: FiltersOptions): Filters;The filter inputs of one owner of a condition part: a grid’s filter row, a search form, a picker. One hook for all of an owner’s filters, because reading a condition back has to see all columns at once to tell whose term is whose.
const filters = useFilters(foos, "foo-grid", [ {field: "name", filter: operatorFilter("containsIgnoreCase")}, {field: "num", filter: operatorFilter("eq", "Int")}])
filters.columns.map(column => <FilterInput key={ column.label } column={ column }/>)The owner’s part of the condition is its component where the condition is a composition of components, all of it
otherwise (see FilterDSL’s ownedPart() and updateComponent()); other owners’ components are left as they are.
The part is one term per active column, combined with and(), plus the unclaimed terms.
When the part changes to something the owner didn’t write, the inputs show it: each column’s filter is asked to
recognize a term (fromCondition), and what nobody recognizes shows as unclaimed. A term the owner wrote
itself is not read back, so the input keeps what the user typed even where the filter normalizes it.
Changing a filter goes back to the first page.
Parameters
doc |
query document snapshot |
id |
component id of the owner |
columns |
the owner’s filters, in a fixed order |
options |
delay before an input change is sent |
FilterableDocument
Section titled “FilterableDocument”interface
interface FilterableDocumentWhat useFilters() needs of a query document: its config and update(). A snapshot from useInjection() or
useQueryDocument() is one as long as its query selects config.
FilterableDocument.type
Section titled “FilterableDocument.type”type: string;the row type, which the columns’ field paths start from
FilterableDocument.config
Section titled “FilterableDocument.config”config: QueryConfig;FilterableDocument.update
Section titled “FilterableDocument.update”update(delta: QueryConfigDelta): Promise<unknown>;FiltersOptions
Section titled “FiltersOptions”interface
interface FiltersOptionsOptions for useFilter()
FiltersOptions.delay
Section titled “FiltersOptions.delay”delay?: number;Milliseconds between the last change to an input and the update() it causes. Default 300.
Filters
Section titled “Filters”interface
interface FiltersThe filter state of one owner of a condition part.
Filters.columns
Section titled “Filters.columns”columns: ColumnFilterState[];per column, in the order given
Filters.unclaimed
Section titled “Filters.unclaimed”unclaimed: FilterExpression[];Terms of the owner’s part that no column recognizes: written by code that doesn’t know the columns’ filters, or recognized by more than one column. They stay in the part until reset().
Filters.active
Section titled “Filters.active”active: boolean;whether the owner’s part filters anything
Filters.reset
Section titled “Filters.reset”reset(): Promise<void>;Clears every input and the unclaimed terms, and updates the document right away.
ColumnFilterState
Section titled “ColumnFilterState”interface
interface ColumnFilterStateThe filter state of one column.
ColumnFilterState.field
Section titled “ColumnFilterState.field”field: FieldExpression;what the column filters: a field path or an expression
ColumnFilterState.label
Section titled “ColumnFilterState.label”label: string;what the inputs’ accessible names call it: the column’s label, or its field’s heading, "Foo.name"
ColumnFilterState.filter
Section titled “ColumnFilterState.filter”filter: ColumnFilter<any>;the column’s filter
ColumnFilterState.values
Section titled “ColumnFilterState.values”values: unknown[];current input values, null for an empty input
ColumnFilterState.setValues
Section titled “ColumnFilterState.setValues”setValues(values: unknown[]): void;Replaces the input values. The document is updated after the delay, once the inputs have stopped changing, and
only if every input is filled, or any for a partial filter; until then the column filters nothing.
ColumnFilterState.active
Section titled “ColumnFilterState.active”active: boolean;whether the column’s term is part of the document’s condition
FilterInput()
Section titled “FilterInput()”function
declare function FilterInput({ column, className }: FilterInputProps): JSX.Element;The inputs of one column’s filter: the filter’s own Input component where it has one, otherwise one text input
per value.
Built on useFilters() and nothing else, so an input that has to look different is written the same way.
FilterInputProps
Section titled “FilterInputProps”type
type FilterInputProps = { /** * The column's filter state, one of `useFilters().columns`. */ column: ColumnFilterState; /** * Added to the wrapper's own classes. */ className?: string;};ColumnFilter
Section titled “ColumnFilter”interface
interface ColumnFilter<V extends unknown[] = unknown[]>A column’s filter.
ColumnFilter.arity
Section titled “ColumnFilter.arity”arity: number;Number of inputs.
ColumnFilter.partial
Section titled “ColumnFilter.partial”partial?: boolean;Whether the filter takes effect with some of its inputs empty, like a date range open at one end. Then
toCondition is called once any input is filled, with null for the empty ones; otherwise only once every
input is.
ColumnFilter.key
Section titled “ColumnFilter.key”key?: boolean;Whether the filter picks the related row of a relation column, and so filters its foreign key ("ownerId")
rather than its first name field ("owner.login"). For a relation whose foreign key is one field.
ColumnFilter.toCondition
Section titled “ColumnFilter.toCondition”toCondition(target: FieldReference, values: V): FilterExpression | null;Builds the term from the input values, null for “no filter”. Only called once every input is filled, or with
partial once any is.
Parameters
target |
what the column filters: a field node, or the expression of a computed column, either with the builder methods, target.between(from, to) |
values |
one value per input |
ColumnFilter.fromCondition
Section titled “ColumnFilter.fromCondition”fromCondition?(target: FieldReference, term: FilterExpression): V | null;Recognizes a term as one this filter produced and returns the input values it came from, null for “not mine”.
A filter without it can’t take over a term set from outside the owner, which then shows as unclaimed. The
target is recognized by structure, conditionsEqual(operand, target), which holds for a field and an
expression alike.
Parameters
target |
what the column filters, as toCondition gets it |
term |
a term of the owner’s part of the condition |
ColumnFilter.Input
Section titled “ColumnFilter.Input”Input?: ComponentType<ColumnFilterInputProps<V>>;Input component, where the default text inputs won’t do.
ColumnFilterInputProps
Section titled “ColumnFilterInputProps”interface
interface ColumnFilterInputProps<V extends unknown[] = unknown[]>What a filter input component gets.
ColumnFilterInputProps.label
Section titled “ColumnFilterInputProps.label”label: string;what the inputs filter, for their accessible names: the column’s label
ColumnFilterInputProps.arity
Section titled “ColumnFilterInputProps.arity”arity: number;number of inputs
ColumnFilterInputProps.values
Section titled “ColumnFilterInputProps.values”values: FilterInputValues<V>;current values, null for an empty input
ColumnFilterInputProps.setValues
Section titled “ColumnFilterInputProps.setValues”setValues(values: FilterInputValues<V>): void;replaces the values
ColumnFilterInputProps.filter
Section titled “ColumnFilterInputProps.filter”filter: ColumnFilter<V>;the filter the input belongs to, for an input that takes what it offers from it
FilterInputValues
Section titled “FilterInputValues”type
type FilterInputValues<V extends unknown[] = unknown[]> = { [K in keyof V]: V[K] | null; };Values of a filter’s inputs as the inputs hold them: null where an input is empty.
FilterColumn
Section titled “FilterColumn”interface
interface FilterColumnA filter on one field or expression, as a filter row or a search form has one per input group.
FilterColumn.field
Section titled “FilterColumn.field”field: FieldExpression;what the column filters: a field path, or a FilterDSL expression like field("numA").add(field("numB"))
FilterColumn.filter
Section titled “FilterColumn.filter”filter: ColumnFilter<any>;the filter
FilterColumn.label
Section titled “FilterColumn.label”label?: string;what the inputs’ accessible names call it, in place of the field’s heading, the i18n() of Type.field as a
grid column has it, or the text form of the expression
filterTarget()
Section titled “filterTarget()”function
declare function filterTarget(expression: FieldExpression): FieldReference;The node a filter compares, with the builder methods: a field path becomes a field node, an expression built with the FilterDSL stays as it is, and a plain node like one parsed from JSON gets the methods on a copy.
An expression has the methods of a field, as the DSL’s builders give it, and its type says FieldReference for that.
Parameters
expression |
field path or expression |
filterLabel()
Section titled “filterLabel()”function
declare function filterLabel(column: FilterColumn): string;The label of a filter column: the one it states, or else its field path, or a text form of its expression,
add(numA, numB).
Parameters
column |
the filter column |
operatorFilter()
Section titled “operatorFilter()”function
declare function operatorFilter(name: FieldConditionName, scalarType?: string): ColumnFilter<string[]>;A filter applying one field condition: operatorFilter("containsIgnoreCase") filters the column’s field for the
text typed, operatorFilter("between", "Int") takes two numbers. It has one input per operand besides the field,
and it reads back exactly the terms it writes: that condition on that field, with values as operands. On a
computed column it applies the condition to the column’s expression instead, between on a sum.
The inputs are text. The filter converts it to the scalar type: a number for Int, Short, Byte and Float, a boolean for “true” and “false” with Boolean, the text as is for everything else. Text that isn’t a value of the type filters nothing, so a half-typed number doesn’t send a query.
Parameters
name |
field condition, with at least one operand |
scalarType |
scalar type of the field. Default “String”. |
booleanFilter()
Section titled “booleanFilter()”function
declare function booleanFilter(): ColumnFilter<string[]>;The filter of a Boolean column: eq on the field, chosen from a select of any, yes and no.
dateRangeFilter()
Section titled “dateRangeFilter()”function
declare function dateRangeFilter(scalarType: "Date" | "Timestamp", options?: DateRangeFilterOptions): ColumnFilter<(string | null)[]>;The filter of a date or time column: two dates, from and until, both included. Either can stay empty for a range
open at that end. The inputs hold ISO dates, "2026-09-28", which is what a date input gives.
A Date column is compared with the dates as they are: between, or ge or le alone. A Timestamp
column is filtered by the days in a time zone, the user’s unless the options name one: from the start of the
first day, ge, to before the start of the day after the last, lt, combined with and(). It recognizes only
terms on day starts in that zone, so a term written with other instants shows as unclaimed rather than moved to
the nearest day.
It is the default filter of Date and Timestamp columns.
Parameters
scalarType |
“Date” or “Timestamp” |
options |
time zone of a Timestamp column’s days |
DateRangeFilterOptions
Section titled “DateRangeFilterOptions”interface
interface DateRangeFilterOptionsOptions for the dateRangeFilter.
DateRangeFilterOptions.timeZone
Section titled “DateRangeFilterOptions.timeZone”timeZone?: string;Time zone whose days a Timestamp column is filtered by. Default: the user’s, Temporal.Now.timeZoneId().
numberContainsFilter()
Section titled “numberContainsFilter()”function
declare function numberContainsFilter(): ColumnFilter<string[]>;A filter for the digits of a number: numberContainsFilter() on an order number finds 12345 for “234”. The term
is contains on the field as text, so it works for any scalar with a text form.
patternFilter()
Section titled “patternFilter()”function
declare function patternFilter(scalarType?: string): ColumnFilter<string[]>;A filter for a search pattern:
*stands for any text, and a word with it matches the whole value:Foo*starts with “Foo”,*#1ends with “#1”. A word without it matches anywhere in the value.&between words: all of them match.|between groups of those: one of the groups matches.&binds tighter, and there are no brackets.!before a word: the word does not match.
Case doesn’t matter. Empty words are left out, so a pattern being typed filters by the words already there:
foo & filters for “foo”.
A plain word becomes containsIgnoreCase, a word with wildcards a regular expression (likeRegex) on the
lowercased value, and the words of a group are combined with and(), the groups with or(). A field that isn’t
a String is matched as text.
Parameters
scalarType |
scalar type of the field. Default “String”. |
pick()
Section titled “pick()”function
declare function pick<R = any>(doc: CatalogDocument<R>, options?: PickOptions<R>): ColumnFilter<string[]>;A filter choosing one row of a catalog: a select of the rows of a query document the view injected, filtering the foreign key that points at them.
const owners = useInjection(Q_OwnerCatalog, {config: {pageSize: 1000}});
<DataGrid doc={ foos } columns={ ["name", {field: "owner", filter: pick(owners)}] }/>On a relation column it filters the foreign key ("ownerId", see ColumnFilter.key); in a search form, give it
the foreign key as the field. The term is eq on that field with the key of the chosen row, which is its primary
key from the uniqueKeys meta, a single field. A term set from outside shows as the row it names, or as the key
itself where the catalog doesn’t hold that row.
The catalog is the document as it is: all of it for a small catalog, as much as its page holds otherwise. A table too large to load whole needs a picker with its own search.
Parameters
doc |
the catalog’s query document |
options |
how an option is labeled |
CatalogDocument
Section titled “CatalogDocument”interface
interface CatalogDocument<R = any>What pick() needs of a query document: its type and its rows. Any snapshot is one.
CatalogDocument.type
Section titled “CatalogDocument.type”type: string;CatalogDocument.rows
Section titled “CatalogDocument.rows”rows: readonly R[];PickOptions
Section titled “PickOptions”interface
interface PickOptions<R = any>Options for the pick() filter helper.
PickOptions.label
Section titled “PickOptions.label”label?(row: R): string;The text an option shows for a row. Default: the row’s name fields, most significant first, from the
nameFields meta of its type.
flagSetFilter()
Section titled “flagSetFilter()”function
declare function flagSetFilter(flags: readonly Flag[]): ColumnFilter<string[][]>;A filter of flags to check: each flag stands for a term, and the rows shown are those matching every flag checked. The input is a checkbox per flag, and its value the names of the checked flags.
const STATE = flagSetFilter([ {name: "open", label: "Open", term: target => target.isNull()}, {name: "mine", label: "Mine", term: () => field("ownerId").eq(value(me))}]);A flag’s term is up to it: it can test what the column filters, or any other field. The terms of the checked flags are
combined with and(), in the order of the flags, and a term set from outside is recognized when it is such a
combination.
Parameters
flags |
the flags, in the order they show |
interface
interface FlagOne flag of a flag set filter.
Flag.name
Section titled “Flag.name”name: string;identifies the flag among the set’s, and is what the input values hold
Flag.label
Section titled “Flag.label”label: ReactNode;what the checkbox says
Flag.term
Section titled “Flag.term”term(target: FieldReference): FilterExpression;The term the flag stands for.
Parameters
target |
what the column filters: a field node, or the expression of a computed column, either with the builder methods |
filled()
Section titled “filled()”function
declare function filled(values: readonly unknown[]): boolean;True if every input of a filter has a value. An empty string counts as no value.
ready()
Section titled “ready()”function
declare function ready(filter: ColumnFilter<any>, values: readonly unknown[]): boolean;True if a filter’s inputs hold enough for it to take effect: all of them, or with partial any of them.
Parameters
filter |
the filter |
values |
its input values |
claimTerms()
Section titled “claimTerms()”function
declare function claimTerms(part: FilterExpression | null, columns: readonly FilterColumn[], written?: readonly (readonly (FilterExpression | null)[])[]): ClaimedTerms;Hands the terms of an owner’s part of a condition to the columns that produced them, by asking each column’s
filter to recognize them (fromCondition).
The part is one term per active column, combined with and(), which unwraps a single operand. So a column term
that is itself an and looks like several terms when it is the only one active, and the part is read in two
steps: the whole part is offered to every column first, and if exactly one claims it, that column is the only one
active. Otherwise the operands of an and are offered one by one.
A term no column claims is unclaimed, and so is a term two columns claim, which is a configuration error warned about in development: two columns filtering one field with one operator. A column claims one term at most; a second one it recognizes is unclaimed.
A term a column wrote itself is that column’s without asking its filter, which is how a filter without
fromCondition keeps its own terms. Its values are null then: the owner still has what it wrote them from.
Parameters
part |
the owner’s part of the condition, as from ownedPart() |
columns |
the owner’s filter columns |
written |
per column, terms it wrote itself |
ClaimedTerms
Section titled “ClaimedTerms”interface
interface ClaimedTermsHow the terms of an owner’s part of a condition are distributed over its filter columns.
ClaimedTerms.terms
Section titled “ClaimedTerms.terms”terms: (FilterExpression | null)[];per column, the term it claimed, or null
ClaimedTerms.values
Section titled “ClaimedTerms.values”values: (unknown[] | null)[];per column, the input values its term came from, or null, also for a term the column wrote itself
ClaimedTerms.unclaimed
Section titled “ClaimedTerms.unclaimed”unclaimed: FilterExpression[];terms no column claimed, or more than one did