Skip to content

Commit b9a45fc

Browse files
committed
docs: add best practices guide for using Filter Sphere effectively
1 parent 37ae13f commit b9a45fc

File tree

2 files changed

+223
-0
lines changed

2 files changed

+223
-0
lines changed

packages/docs/src/content/docs/customization/localization.mdx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,78 @@ implementation for these functions, make sure to update the `getLocaleText`
168168
function accordingly.
169169

170170
</Aside>
171+
172+
## Integration with i18n Libraries
173+
174+
If your project already uses an i18n library (e.g. `next-intl`, `react-intl`, `react-i18next`), you can bridge it with Filter Sphere's `getLocaleText` instead of maintaining separate locale files.
175+
176+
The key idea is to create a custom `getLocaleText` that delegates to your i18n library's translation function.
177+
178+
### Create a hook that bridges your i18n library
179+
180+
Wrap your i18n library's `useTranslations` (or equivalent) in a custom hook. This example uses `next-intl`:
181+
182+
```tsx
183+
import { defaultGetLocaleText } from "@fn-sphere/filter";
184+
import { useTranslations } from "next-intl";
185+
import { useCallback } from "react";
186+
187+
const FIELD_PREFIX = "filter"; // Optional prefix for filter keys
188+
189+
export function useGetLocaleText() {
190+
const t = useTranslations();
191+
192+
const getLocaleText = useCallback(
193+
(key: string) => t(`${FIELD_PREFIX}.${key}`) ?? defaultGetLocaleText(key),
194+
[t],
195+
);
196+
197+
return getLocaleText;
198+
}
199+
```
200+
201+
### Use the hook with Filter Sphere
202+
203+
Pass the bridged `getLocaleText` to `useFilterSphere`. All Filter Sphere UI labels (operator names, button text, field names, filter names) will be resolved through your i18n pipeline.
204+
205+
```tsx
206+
import {
207+
FilterSphereProvider,
208+
FilterBuilder,
209+
useFilterSphere,
210+
type FilterSphereInput,
211+
} from "@fn-sphere/filter";
212+
import { z } from "zod";
213+
import { useMemo } from "react";
214+
215+
const schema = z.object({
216+
name: z.string().describe("name"),
217+
price: z.number().describe("price"),
218+
});
219+
220+
function MyFilter() {
221+
const getLocaleText = useGetLocaleText();
222+
const { context } = useFilterSphere({
223+
schema,
224+
getLocaleText,
225+
});
226+
227+
return (
228+
<FilterSphereProvider context={context}>
229+
<FilterBuilder />
230+
</FilterSphereProvider>
231+
);
232+
}
233+
```
234+
235+
Then add the corresponding keys to your translation files:
236+
237+
```json
238+
// en.json
239+
{
240+
"filter.name": "Name",
241+
"filter.price": "Price"
242+
}
243+
```
244+
245+
With this approach, all Filter Sphere labels are resolved through your existing i18n pipeline, so you only need to maintain a single set of translation files.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
---
2+
title: Best Practices
3+
description: Best practices for using Filter Sphere effectively
4+
sidebar:
5+
order: 2
6+
---
7+
8+
import SourceCodeCard from "~/components/source-code-card.astro";
9+
10+
This guide covers best practices and recommended patterns when building with Filter Sphere.
11+
12+
## Project Structure
13+
14+
Organize your filter code into separate files by concern. This makes each piece easier to reuse, test, and maintain independently.
15+
16+
```
17+
filter-sphere/
18+
├── index.tsx # Main component, wires everything together
19+
├── schema.ts # Zod data schema, default rules, filter functions
20+
├── theme.tsx # Theme configuration via createFilterTheme
21+
└── locale.ts # i18n setup with getLocaleText
22+
```
23+
24+
## Define Descriptive Schemas
25+
26+
Define your data with zod. Use `.describe()` on your Zod fields to provide human-readable labels or i18n keys. These labels are displayed in the field selector dropdown and can be translated via `getLocaleText`.
27+
28+
```ts
29+
// schema.ts
30+
import { presetFilter } from "@fn-sphere/filter";
31+
import { z } from "zod";
32+
33+
const schema = z.object({
34+
name: z.string().describe("Name"),
35+
email: z.string().describe("Email"),
36+
age: z.number().describe("Age"),
37+
active: z.boolean().describe("Active"),
38+
createdAt: z.date().describe("Created At"),
39+
});
40+
41+
// (Optional) Define a default filter rule
42+
export const defaultRule = createFilterGroup({
43+
op: "and",
44+
conditions: [
45+
createSingleFilter({
46+
path: ["name"],
47+
name: "contains",
48+
args: ["default value"],
49+
}),
50+
],
51+
});
52+
53+
// (Optional) Customize the list of filter functions
54+
export const filterFnList: FnSchema[] = presetFilter
55+
// Exclude "endsWith" as an example
56+
.filter((fn) => fn.name !== "endsWith");
57+
```
58+
59+
## Create a Custom Theme
60+
61+
Use `createFilterTheme` to build your theme in a dedicated file. It merges your customizations with the preset defaults, so you only need to override what you want to change.
62+
63+
```tsx
64+
// theme.tsx
65+
import { createFilterTheme } from "@fn-sphere/filter";
66+
67+
export const theme = createFilterTheme({
68+
components: {
69+
Button: (props) => <button {...props} className="my-button" />,
70+
Input: ({ onChange, ...props }) => (
71+
<input onChange={(e) => onChange?.(e.target.value)} {...props} />
72+
),
73+
},
74+
templates: {
75+
// Customize FilterGroupContainer, SingleFilter, etc.
76+
},
77+
});
78+
```
79+
80+
## Handle Localization
81+
82+
Keep i18n logic in a separate locale file. Use built-in locales from `@fn-sphere/filter/locales` as a base, and extend them with your own field name translations.
83+
84+
```ts
85+
// locale.ts
86+
import { enUS, zhCN, jaJP } from "@fn-sphere/filter/locales";
87+
88+
export const locales = [
89+
{ key: "en", label: "English", value: enUS },
90+
{
91+
key: "cn",
92+
label: "中文",
93+
value: { ...zhCN, Name: "名称", Email: "邮箱" },
94+
},
95+
{
96+
key: "jp",
97+
label: "日本語",
98+
value: { ...jaJP, Name: "名前", Email: "メール" },
99+
},
100+
];
101+
102+
export const createGetLocaleText = (localeKey: string) => {
103+
const locale = locales.find((l) => l.key === localeKey)?.value;
104+
return (key: string): string => {
105+
if (!locale || !(key in locale)) return key;
106+
return locale[key as keyof typeof locale];
107+
};
108+
};
109+
```
110+
111+
The field labels from `.describe()` are passed through `getLocaleText`, so you can translate them by adding the label as a key in your locale objects.
112+
113+
## Wire Everything Together
114+
115+
The main file imports from all other modules and composes them into the final component.
116+
117+
```tsx
118+
// index.tsx
119+
import {
120+
FilterBuilder,
121+
FilterSphereProvider,
122+
useFilterSphere,
123+
} from "@fn-sphere/filter";
124+
import { defaultRule, filterFnList, filterSchema } from "./schema";
125+
import { theme } from "./theme";
126+
import { createGetLocaleText } from "./locale";
127+
128+
export function MyFilter() {
129+
const { context, predicate, filterRule } = useFilterSphere({
130+
schema: filterSchema,
131+
defaultRule,
132+
filterFnList,
133+
getLocaleText: createGetLocaleText("en"),
134+
});
135+
136+
const data = [
137+
/* your data here */
138+
];
139+
const filteredData = data.filter(predicate);
140+
console.log("Filtered Data:", filteredData);
141+
142+
return (
143+
<FilterSphereProvider context={context} theme={theme}>
144+
<FilterBuilder />
145+
</FilterSphereProvider>
146+
);
147+
}
148+
```

0 commit comments

Comments
 (0)