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> </>