Skip to content

Customizing Data Input

This guide demonstrates how to customize the data input view in your Filter Sphere components using a custom theme.

In this example, we will create a custom boolean input view and override the default boolean input view.

import {
type DataInputViewSpec,
useFilterSphere,
FilterSphereProvider,
FilterBuilder,
createFilterTheme,
} from "@fn-sphere/filter";
import { z } from "zod";
const schema = z.object({
active: z.boolean(),
id: z.number(),
});
// Define a custom input component
const booleanInput: DataInputViewSpec = {
name: "boolean",
// The data type that this input component can handle
match: [z.boolean()],
view: ({ rule, updateInput }) => {
return (
<input
type="checkbox"
value={!!rule.args?.[0]}
onChange={(e) => {
const value = e.target.checked;
updateInput(value);
}}
/>
);
},
};
const customTheme = createFilterTheme({
// Add the custom input component to the list of data input views
dataInputViews: [booleanInput],
});
export default function CustomDataInput() {
const { filterRule, predicate, context } = useFilterSphere({ schema });
return (
<FilterSphereProvider context={context} theme={customTheme}>
<FilterBuilder />
</FilterSphereProvider>
);
}

Sometimes you may want to create a custom input view for a specific data type. You can create a custom input view by defining a custom input component and adding it to the list of data input views in the theme.

X:Y:
import {
type DataInputViewSpec,
useFilterSphere,
FilterSphereProvider,
FilterBuilder,
createFilterTheme,
presetFilter,
defineTypedFn,
} from "@fn-sphere/filter";
import { z } from "zod";
const pointSchema = z.object({
x: z.number(),
y: z.number(),
});
const schema = z.object({
point: pointSchema,
});
// Define a custom input component
const pointInput: DataInputViewSpec = {
name: "point",
// The data type that this input component can handle
match: [pointSchema],
view: ({ rule, updateInput }) => {
const point = rule.args[0] ?? { x: 0, y: 0 };
return (
<>
<span>X:</span>
<input
onChange={(e) => {
const value = e.target.value;
updateInput({
x: value,
y: point.y,
});
}}
style={{ width: "50px" }}
/>
<span>Y:</span>
<input
onChange={(e) => {
const value = e.target.value;
updateInput({
x: point.x,
y: value,
});
}}
style={{ width: "50px" }}
/>
</>
);
},
};
// Define a custom filter function for the point data type
const pointFilter = defineTypedFn({
name: "equals",
define: z.function({
input: [pointSchema, pointSchema],
output: z.boolean(),
}),
implement: (value, userInput) => {
return value.x === userInput.x && value.y === userInput.y;
},
});
const customTheme = createFilterTheme({
// Add the custom input component to the list of data input views
dataInputViews: [pointInput],
});
export default function CustomDataInput() {
const { filterRule, predicate, context } = useFilterSphere({
schema,
filterFnList: [pointFilter, ...presetFilter],
});
return (
<FilterSphereProvider context={context} theme={customTheme}>
<FilterBuilder />
</FilterSphereProvider>
);
}
export type DataInputViewProps = {
rule: SingleFilter;
requiredDataSchema: $ZodTuple;
updateInput: (...input: unknown[]) => void;
};
export type DataInputViewSpec = {
name: string;
match:
| []
| [$ZodType, ...$ZodType[]]
| $ZodTuple
| ((
// The first parameter is the fn schema required parameters except the first one
parameterSchemas: $ZodTuple,
// The second parameter is the field schema. In most cases, you don't need to use it.
fieldSchema?: $ZodType,
) => boolean);
view: ComponentType<DataInputViewProps>;
meta?: Record<string, unknown>;
};