Skip to content

Commit 2575614

Browse files
Copilothuangyiirene
andcommitted
Add filter builder demo and example usage
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
1 parent 5500938 commit 2575614

2 files changed

Lines changed: 291 additions & 1 deletion

File tree

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
import { SchemaRenderer } from '@object-ui/react';
2+
import '@object-ui/components';
3+
4+
const filterBuilderSchema = {
5+
type: 'div',
6+
className: 'min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 p-8',
7+
body: [
8+
{
9+
type: 'div',
10+
className: 'max-w-5xl mx-auto space-y-8',
11+
body: [
12+
// Header
13+
{
14+
type: 'div',
15+
className: 'space-y-2',
16+
body: [
17+
{
18+
type: 'div',
19+
className: 'text-3xl font-bold tracking-tight',
20+
body: { type: 'text', content: 'Filter Builder Demo' }
21+
},
22+
{
23+
type: 'div',
24+
className: 'text-muted-foreground',
25+
body: {
26+
type: 'text',
27+
content: 'Airtable-like filter component for building complex query conditions'
28+
}
29+
}
30+
]
31+
},
32+
33+
// Example 1: User Data Filtering
34+
{
35+
type: 'card',
36+
className: 'shadow-lg',
37+
body: [
38+
{
39+
type: 'div',
40+
className: 'p-6 border-b',
41+
body: [
42+
{
43+
type: 'div',
44+
className: 'text-xl font-semibold',
45+
body: { type: 'text', content: 'User Data Filters' }
46+
},
47+
{
48+
type: 'div',
49+
className: 'text-sm text-muted-foreground mt-1',
50+
body: {
51+
type: 'text',
52+
content: 'Filter users by name, email, age, and status'
53+
}
54+
}
55+
]
56+
},
57+
{
58+
type: 'div',
59+
className: 'p-6',
60+
body: {
61+
type: 'filter-builder',
62+
name: 'userFilters',
63+
label: 'User Filters',
64+
fields: [
65+
{ value: 'name', label: 'Name', type: 'text' },
66+
{ value: 'email', label: 'Email', type: 'text' },
67+
{ value: 'age', label: 'Age', type: 'number' },
68+
{ value: 'status', label: 'Status', type: 'text' },
69+
{ value: 'department', label: 'Department', type: 'text' },
70+
{ value: 'joinDate', label: 'Join Date', type: 'text' }
71+
],
72+
value: {
73+
id: 'root',
74+
logic: 'and',
75+
conditions: [
76+
{
77+
id: 'cond-1',
78+
field: 'status',
79+
operator: 'equals',
80+
value: 'active'
81+
}
82+
]
83+
}
84+
}
85+
}
86+
]
87+
},
88+
89+
// Example 2: Product Filtering
90+
{
91+
type: 'card',
92+
className: 'shadow-lg',
93+
body: [
94+
{
95+
type: 'div',
96+
className: 'p-6 border-b',
97+
body: [
98+
{
99+
type: 'div',
100+
className: 'text-xl font-semibold',
101+
body: { type: 'text', content: 'Product Filters' }
102+
},
103+
{
104+
type: 'div',
105+
className: 'text-sm text-muted-foreground mt-1',
106+
body: {
107+
type: 'text',
108+
content: 'Filter products by name, price, category, and stock'
109+
}
110+
}
111+
]
112+
},
113+
{
114+
type: 'div',
115+
className: 'p-6',
116+
body: {
117+
type: 'filter-builder',
118+
name: 'productFilters',
119+
label: 'Product Filters',
120+
fields: [
121+
{ value: 'name', label: 'Product Name', type: 'text' },
122+
{ value: 'price', label: 'Price', type: 'number' },
123+
{ value: 'category', label: 'Category', type: 'text' },
124+
{ value: 'stock', label: 'Stock Quantity', type: 'number' },
125+
{ value: 'brand', label: 'Brand', type: 'text' },
126+
{ value: 'rating', label: 'Rating', type: 'number' }
127+
],
128+
value: {
129+
id: 'root',
130+
logic: 'or',
131+
conditions: [
132+
{
133+
id: 'cond-1',
134+
field: 'price',
135+
operator: 'lessThan',
136+
value: '100'
137+
},
138+
{
139+
id: 'cond-2',
140+
field: 'category',
141+
operator: 'equals',
142+
value: 'Electronics'
143+
}
144+
]
145+
}
146+
}
147+
}
148+
]
149+
},
150+
151+
// Example 3: Empty Filter Builder
152+
{
153+
type: 'card',
154+
className: 'shadow-lg',
155+
body: [
156+
{
157+
type: 'div',
158+
className: 'p-6 border-b',
159+
body: [
160+
{
161+
type: 'div',
162+
className: 'text-xl font-semibold',
163+
body: { type: 'text', content: 'Order Filters' }
164+
},
165+
{
166+
type: 'div',
167+
className: 'text-sm text-muted-foreground mt-1',
168+
body: {
169+
type: 'text',
170+
content: 'Start with an empty filter - try adding conditions!'
171+
}
172+
}
173+
]
174+
},
175+
{
176+
type: 'div',
177+
className: 'p-6',
178+
body: {
179+
type: 'filter-builder',
180+
name: 'orderFilters',
181+
label: 'Order Filters',
182+
fields: [
183+
{ value: 'orderId', label: 'Order ID', type: 'text' },
184+
{ value: 'customer', label: 'Customer Name', type: 'text' },
185+
{ value: 'total', label: 'Total Amount', type: 'number' },
186+
{ value: 'status', label: 'Order Status', type: 'text' },
187+
{ value: 'date', label: 'Order Date', type: 'text' },
188+
{ value: 'shipped', label: 'Shipped', type: 'boolean' }
189+
],
190+
value: {
191+
id: 'root',
192+
logic: 'and',
193+
conditions: []
194+
}
195+
}
196+
}
197+
]
198+
},
199+
200+
// Features section
201+
{
202+
type: 'card',
203+
className: 'shadow-lg bg-primary/5 border-primary/20',
204+
body: {
205+
type: 'div',
206+
className: 'p-6',
207+
body: [
208+
{
209+
type: 'div',
210+
className: 'text-lg font-semibold mb-4',
211+
body: { type: 'text', content: '✨ Features' }
212+
},
213+
{
214+
type: 'div',
215+
className: 'grid md:grid-cols-2 gap-4 text-sm',
216+
body: [
217+
{
218+
type: 'div',
219+
className: 'flex items-start gap-2',
220+
body: [
221+
{ type: 'div', body: { type: 'text', content: '✓' }, className: 'text-primary font-bold' },
222+
{ type: 'div', body: { type: 'text', content: 'Dynamic add/remove filter conditions' } }
223+
]
224+
},
225+
{
226+
type: 'div',
227+
className: 'flex items-start gap-2',
228+
body: [
229+
{ type: 'div', body: { type: 'text', content: '✓' }, className: 'text-primary font-bold' },
230+
{ type: 'div', body: { type: 'text', content: 'Field-type aware operators' } }
231+
]
232+
},
233+
{
234+
type: 'div',
235+
className: 'flex items-start gap-2',
236+
body: [
237+
{ type: 'div', body: { type: 'text', content: '✓' }, className: 'text-primary font-bold' },
238+
{ type: 'div', body: { type: 'text', content: 'AND/OR logic toggling' } }
239+
]
240+
},
241+
{
242+
type: 'div',
243+
className: 'flex items-start gap-2',
244+
body: [
245+
{ type: 'div', body: { type: 'text', content: '✓' }, className: 'text-primary font-bold' },
246+
{ type: 'div', body: { type: 'text', content: 'Tailwind CSS styled' } }
247+
]
248+
},
249+
{
250+
type: 'div',
251+
className: 'flex items-start gap-2',
252+
body: [
253+
{ type: 'div', body: { type: 'text', content: '✓' }, className: 'text-primary font-bold' },
254+
{ type: 'div', body: { type: 'text', content: 'Shadcn UI components' } }
255+
]
256+
},
257+
{
258+
type: 'div',
259+
className: 'flex items-start gap-2',
260+
body: [
261+
{ type: 'div', body: { type: 'text', content: '✓' }, className: 'text-primary font-bold' },
262+
{ type: 'div', body: { type: 'text', content: 'Schema-driven configuration' } }
263+
]
264+
}
265+
]
266+
}
267+
]
268+
}
269+
}
270+
]
271+
}
272+
]
273+
};
274+
275+
function FilterBuilderDemo() {
276+
return (
277+
<SchemaRenderer
278+
schema={filterBuilderSchema}
279+
/>
280+
);
281+
}
282+
283+
export default FilterBuilderDemo;

examples/prototype/src/main.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@ import { StrictMode } from 'react'
22
import { createRoot } from 'react-dom/client'
33
import './index.css'
44
import App from './App.tsx'
5+
import FilterBuilderDemo from './FilterBuilderDemo.tsx'
6+
7+
// Check if URL parameter specifies which demo to show
8+
const urlParams = new URLSearchParams(window.location.search);
9+
const demo = urlParams.get('demo');
10+
11+
const DemoApp = demo === 'filter-builder' ? FilterBuilderDemo : App;
512

613
createRoot(document.getElementById('root')!).render(
714
<StrictMode>
8-
<App />
15+
<DemoApp />
916
</StrictMode>,
1017
)

0 commit comments

Comments
 (0)