Providing a Custom Filter Editor
Almost certainly, our current
string
and number
filters are not enough for you. You will definitely need to write your custom filter editor.Fortunately, doing this is straightforward - it involves using the
useInfiniteColumnFilterEditor
hook.The next snippet shows our implementation of the
number
filter editor:export function NumberFilterEditor<T>() {
const { ariaLabel, value, setValue, className, disabled } =
useInfiniteColumnFilterEditor<T>();
return (
<input
aria-label={ariaLabel}
type="number"
disabled={disabled}
value={value as any as number}
onChange={(event) => {
let value = isNaN(event.target.valueAsNumber)
? event.target.value
: event.target.valueAsNumber;
setValue(value as any as T);
}}
className={className}
/>
);
}
COPY
This
NumberFilterEditor
is configured in the components.FilterEditor
property for the number
filter type.If you want to import the
NumberFilterEditor
, you can do so with the following code:import { components } from '@infinite-table/infinite-react';
const { NumberFilterEditor, StringFilterEditor } = components;
COPY
As an exercise, let's write a custom filter editor that shows a checkbox and uses that to filter the values.
First step is to define the
bool
filter type: Defining the bool filter type with one emptyValue
filterTypes={{ bool: { label: 'Boolean', defaultOperator: 'eq', // when the filter checkbox is indeterminate state, that's mapped to `null` emptyValues: [null], operators: [ // operators will come here ], } }}
COPY
Note in the code above, we have
emptyValues: [null]
- so when the filter checkbox is in indeterminate state, it should show all the rows.Now it's time to define the operators - more exactly, just one operator,
eq
: Defining the eq operator
filterTypes={{ bool: { defaultOperator: 'eq', emptyValues: [null], operators: [ { name: 'eq', label: 'Equals', fn: ({ currentValue, filterValue }) => currentValue === filterValue, }, ], }, }}
COPY
The last part of the
bool
filter type will be to specify the FilterEditor
component - this can be either specified as part of the filter type or as part of the operator definition (each operator can override the components.FilterEditor
). Specifying the FilterEditor component
filterTypes={{ bool: { defaultOperator: 'eq', emptyValues: [null], components: { FilterEditor: BoolFilterEditor, FilterOperatorSwitch: () => null, }, operators: [ { name: 'eq', label: 'Equals', fn: ({ currentValue, filterValue }) => currentValue === filterValue, }, ], }, }}
COPY
Now it's time to write the actual
BoolFilterEditor
that the bool
filter type is using: BoolFilterEditor
import { components, useInfiniteColumnFilterEditor, } from '@infinite-table/infinite-react'; const { CheckBox } = components; function BoolFilterEditor() { const { value, setValue, className } = useInfiniteColumnFilterEditor<Developer>(); return ( <div className={className} style={{ textAlign: 'center' }}> <CheckBox checked={value} onChange={(newValue) => { if (value === true) { // after the value was true, make it go to indeterminate state newValue = null; } if (value === null) { // from indeterminate, goto false newValue = false; } setValue(newValue); }} /> </div> ); }
COPY
In the snippet above, note how we're using the
useInfiniteColumnFilterEditor
hook to get the current value
of the filter and also to retrieve the setValue
function that we need to call when we want to update filtering.The
canDesign
column is using a custom bool
filter type with a custom filter editor.View Mode
Fork Forkimport * as React from 'react'; import { InfiniteTable, InfiniteTablePropColumns, DataSource, components, useInfiniteColumnFilterEditor, } from '@infinite-table/infinite-react'; const { CheckBox } = components; type Developer = { id: number; firstName: string; canDesign: boolean; stack: string; hobby: string; }; const dataSource: Developer[] = [ { id: 1, firstName: 'John', canDesign: true, stack: 'frontend', hobby: 'gaming', }, { id: 2, firstName: 'Jane', canDesign: false, stack: 'backend', hobby: 'reading', }, { id: 3, firstName: 'Jack', canDesign: true, stack: 'frontend', hobby: 'gaming', }, { id: 4, firstName: 'Jill', canDesign: false, stack: 'backend', hobby: 'reading', }, { id: 5, firstName: 'Seb', canDesign: false, stack: 'backend', hobby: 'reading', }, ]; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', type: 'number', defaultWidth: 100, }, canDesign: { field: 'canDesign', filterType: 'bool', renderValue: ({ value }) => (value ? 'Yes' : 'No'), }, firstName: { field: 'firstName', }, stack: { field: 'stack' }, }; const domProps = { style: { height: '100%', }, }; function BoolFilterEditor() { const { value, setValue, className } = useInfiniteColumnFilterEditor<Developer>(); return ( <div className={className} style={{ textAlign: 'center' }}> <CheckBox checked={value} onChange={(newValue) => { if (value === true) { // after the value was true, make it go to indeterminate state newValue = null; } if (value === null) { // from indeterminate, goto false newValue = false; } setValue(newValue); }} /> </div> ); } export default () => { return ( <> <React.StrictMode> <DataSource<Developer> data={dataSource} primaryKey="id" defaultFilterValue={[]} filterDelay={0} filterTypes={{ bool: { defaultOperator: 'eq', emptyValues: [null], components: { FilterEditor: BoolFilterEditor, FilterOperatorSwitch: () => null, }, operators: [ { name: 'eq', label: 'Equals', fn: ({ currentValue, filterValue }) => currentValue === filterValue, }, ], }, }} > <InfiniteTable<Developer> domProps={domProps} columnDefaultWidth={150} columnMinWidth={50} columns={columns} /> </DataSource> </React.StrictMode> </>