@djangocfg/layouts 1.4.26 → 1.4.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/auth/middlewares/index.ts +1 -1
- package/src/auth/middlewares/proxy.ts +10 -2
- package/src/layouts/index.ts +0 -3
- package/src/snippets/ContactForm/ContactForm.tsx +59 -12
- package/src/snippets/ContactForm/ContactPage.tsx +21 -20
- package/src/snippets/ContactForm/dynamic.tsx +55 -0
- package/src/snippets/ContactForm/index.ts +6 -3
- package/src/layouts/UILayout/README.md +0 -267
- package/src/layouts/UILayout/SUMMARY.md +0 -298
- package/src/layouts/UILayout/TOOLS_INTEGRATION.md +0 -216
- package/src/layouts/UILayout/components/AutoComponentDemo.tsx +0 -77
- package/src/layouts/UILayout/components/CategoryRenderer.tsx +0 -45
- package/src/layouts/UILayout/components/TailwindGuideRenderer.tsx +0 -138
- package/src/layouts/UILayout/components/index.ts +0 -15
- package/src/layouts/UILayout/components/layout/Header/CopyAIButton.tsx +0 -58
- package/src/layouts/UILayout/components/layout/Header/Header.tsx +0 -60
- package/src/layouts/UILayout/components/layout/Header/HeaderDesktop.tsx +0 -51
- package/src/layouts/UILayout/components/layout/Header/HeaderMobile.tsx +0 -71
- package/src/layouts/UILayout/components/layout/Header/TestValidationButton.tsx +0 -268
- package/src/layouts/UILayout/components/layout/Header/index.ts +0 -11
- package/src/layouts/UILayout/components/layout/MobileOverlay/MobileOverlay.tsx +0 -47
- package/src/layouts/UILayout/components/layout/MobileOverlay/index.ts +0 -6
- package/src/layouts/UILayout/components/layout/Sidebar/Sidebar.tsx +0 -95
- package/src/layouts/UILayout/components/layout/Sidebar/SidebarCategory.tsx +0 -54
- package/src/layouts/UILayout/components/layout/Sidebar/SidebarContent.tsx +0 -93
- package/src/layouts/UILayout/components/layout/Sidebar/SidebarFooter.tsx +0 -49
- package/src/layouts/UILayout/components/layout/Sidebar/index.ts +0 -9
- package/src/layouts/UILayout/components/layout/index.ts +0 -8
- package/src/layouts/UILayout/components/shared/Badge/CountBadge.tsx +0 -38
- package/src/layouts/UILayout/components/shared/Badge/index.ts +0 -5
- package/src/layouts/UILayout/components/shared/CodeBlock/CodeBlock.tsx +0 -48
- package/src/layouts/UILayout/components/shared/CodeBlock/CopyButton.tsx +0 -49
- package/src/layouts/UILayout/components/shared/CodeBlock/index.ts +0 -6
- package/src/layouts/UILayout/components/shared/Section/Section.tsx +0 -63
- package/src/layouts/UILayout/components/shared/Section/index.ts +0 -5
- package/src/layouts/UILayout/components/shared/index.ts +0 -8
- package/src/layouts/UILayout/config/ai-export.config.ts +0 -89
- package/src/layouts/UILayout/config/categories.config.tsx +0 -122
- package/src/layouts/UILayout/config/components/blocks.config.tsx +0 -239
- package/src/layouts/UILayout/config/components/data.config.tsx +0 -433
- package/src/layouts/UILayout/config/components/feedback.config.tsx +0 -290
- package/src/layouts/UILayout/config/components/forms.config.tsx +0 -996
- package/src/layouts/UILayout/config/components/hooks.config.tsx +0 -168
- package/src/layouts/UILayout/config/components/index.ts +0 -72
- package/src/layouts/UILayout/config/components/layout.config.tsx +0 -246
- package/src/layouts/UILayout/config/components/navigation.config.tsx +0 -352
- package/src/layouts/UILayout/config/components/overlay.config.tsx +0 -569
- package/src/layouts/UILayout/config/components/specialized.config.tsx +0 -400
- package/src/layouts/UILayout/config/components/tools.config.tsx +0 -234
- package/src/layouts/UILayout/config/components/types.ts +0 -14
- package/src/layouts/UILayout/config/index.ts +0 -42
- package/src/layouts/UILayout/config/tailwind.config.ts +0 -131
- package/src/layouts/UILayout/constants.ts +0 -23
- package/src/layouts/UILayout/context/ShowcaseContext.tsx +0 -81
- package/src/layouts/UILayout/context/index.ts +0 -1
- package/src/layouts/UILayout/core/UIGuideApp.client.tsx +0 -18
- package/src/layouts/UILayout/core/UIGuideApp.tsx +0 -33
- package/src/layouts/UILayout/core/UIGuideLanding.tsx +0 -172
- package/src/layouts/UILayout/core/UIGuideView.tsx +0 -61
- package/src/layouts/UILayout/core/UILayout.tsx +0 -125
- package/src/layouts/UILayout/core/UILayoutSidebar.tsx +0 -11
- package/src/layouts/UILayout/core/index.ts +0 -10
- package/src/layouts/UILayout/hooks/index.ts +0 -9
- package/src/layouts/UILayout/hooks/useAIExport.ts +0 -78
- package/src/layouts/UILayout/hooks/useCategoryNavigation.ts +0 -92
- package/src/layouts/UILayout/hooks/useComponentSearch.ts +0 -81
- package/src/layouts/UILayout/hooks/useSidebarState.ts +0 -36
- package/src/layouts/UILayout/index.ts +0 -160
- package/src/layouts/UILayout/types/component.ts +0 -45
- package/src/layouts/UILayout/types/index.ts +0 -23
- package/src/layouts/UILayout/types/layout.ts +0 -57
- package/src/layouts/UILayout/types/navigation.ts +0 -33
- package/src/layouts/UILayout/utils/ai-export/formatters.ts +0 -71
- package/src/layouts/UILayout/utils/ai-export/index.ts +0 -5
- package/src/layouts/UILayout/utils/component-helpers/filter.ts +0 -109
- package/src/layouts/UILayout/utils/component-helpers/index.ts +0 -6
- package/src/layouts/UILayout/utils/component-helpers/search.ts +0 -95
- package/src/layouts/UILayout/utils/index.ts +0 -6
|
@@ -1,996 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Form Components Configuration
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import React, { useState, useEffect, useMemo } from 'react';
|
|
6
|
-
import {
|
|
7
|
-
Button,
|
|
8
|
-
ButtonLink,
|
|
9
|
-
DownloadButton,
|
|
10
|
-
Input,
|
|
11
|
-
Checkbox,
|
|
12
|
-
Label,
|
|
13
|
-
RadioGroup,
|
|
14
|
-
RadioGroupItem,
|
|
15
|
-
Select,
|
|
16
|
-
SelectContent,
|
|
17
|
-
SelectItem,
|
|
18
|
-
SelectTrigger,
|
|
19
|
-
SelectValue,
|
|
20
|
-
Textarea,
|
|
21
|
-
Switch,
|
|
22
|
-
Slider,
|
|
23
|
-
Combobox,
|
|
24
|
-
MultiSelect,
|
|
25
|
-
MultiSelectPro,
|
|
26
|
-
MultiSelectProAsync,
|
|
27
|
-
InputOTP,
|
|
28
|
-
InputOTPGroup,
|
|
29
|
-
InputOTPSlot,
|
|
30
|
-
PhoneInput,
|
|
31
|
-
Form,
|
|
32
|
-
FormControl,
|
|
33
|
-
FormDescription,
|
|
34
|
-
FormField,
|
|
35
|
-
FormItem,
|
|
36
|
-
FormLabel,
|
|
37
|
-
FormMessage,
|
|
38
|
-
Field,
|
|
39
|
-
useDebounce,
|
|
40
|
-
} from '@djangocfg/ui';
|
|
41
|
-
import { JsonSchemaForm } from '@djangocfg/ui/tools';
|
|
42
|
-
import type { ComponentConfig } from './types';
|
|
43
|
-
|
|
44
|
-
export const FORM_COMPONENTS: ComponentConfig[] = [
|
|
45
|
-
{
|
|
46
|
-
name: 'Label',
|
|
47
|
-
category: 'forms',
|
|
48
|
-
description: 'Accessible label component for form inputs',
|
|
49
|
-
importPath: "import { Label } from '@djangocfg/ui';",
|
|
50
|
-
example: `<div className="space-y-2">
|
|
51
|
-
<Label htmlFor="email">Email address</Label>
|
|
52
|
-
<Input id="email" type="email" placeholder="Enter your email" />
|
|
53
|
-
</div>`,
|
|
54
|
-
preview: (
|
|
55
|
-
<div className="space-y-2 max-w-sm">
|
|
56
|
-
<Label htmlFor="demo-email">Email address</Label>
|
|
57
|
-
<Input id="demo-email" type="email" placeholder="Enter your email" />
|
|
58
|
-
</div>
|
|
59
|
-
),
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: 'Button',
|
|
63
|
-
category: 'forms',
|
|
64
|
-
description: 'Interactive button with multiple variants, sizes, and loading state. Use ButtonLink for navigation.',
|
|
65
|
-
importPath: "import { Button, ButtonLink } from '@djangocfg/ui';",
|
|
66
|
-
example: `// Variants
|
|
67
|
-
<Button variant="default">Click me</Button>
|
|
68
|
-
<Button variant="destructive">Delete</Button>
|
|
69
|
-
<Button variant="outline">Outline</Button>
|
|
70
|
-
<Button variant="ghost">Ghost</Button>
|
|
71
|
-
|
|
72
|
-
// Sizes
|
|
73
|
-
<Button size="sm">Small</Button>
|
|
74
|
-
<Button size="lg">Large</Button>
|
|
75
|
-
|
|
76
|
-
// Loading state
|
|
77
|
-
<Button loading={true}>Saving...</Button>
|
|
78
|
-
<Button loading={false}>Save</Button>
|
|
79
|
-
|
|
80
|
-
// ButtonLink for navigation with Next.js Link
|
|
81
|
-
<ButtonLink href="/dashboard">Go to Dashboard</ButtonLink>
|
|
82
|
-
<ButtonLink href="/settings" variant="outline">Settings</ButtonLink>`,
|
|
83
|
-
preview: (
|
|
84
|
-
<div className="flex flex-col gap-4">
|
|
85
|
-
<div className="flex flex-wrap gap-3">
|
|
86
|
-
<Button variant="default">Click me</Button>
|
|
87
|
-
<Button variant="destructive">Delete</Button>
|
|
88
|
-
<Button variant="outline">Outline</Button>
|
|
89
|
-
<Button variant="ghost">Ghost</Button>
|
|
90
|
-
</div>
|
|
91
|
-
<div className="flex flex-wrap gap-3">
|
|
92
|
-
<Button size="sm">Small</Button>
|
|
93
|
-
<Button size="lg">Large</Button>
|
|
94
|
-
</div>
|
|
95
|
-
<div className="flex flex-wrap gap-3">
|
|
96
|
-
<Button loading={true}>Saving...</Button>
|
|
97
|
-
<Button loading={false}>Save</Button>
|
|
98
|
-
</div>
|
|
99
|
-
<div className="flex flex-wrap gap-3">
|
|
100
|
-
<ButtonLink href="#">Go to Dashboard</ButtonLink>
|
|
101
|
-
<ButtonLink href="#" variant="outline">Settings</ButtonLink>
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
104
|
-
),
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
name: 'DownloadButton',
|
|
108
|
-
category: 'forms',
|
|
109
|
-
description: 'Button with download functionality, status indicators, and authentication support',
|
|
110
|
-
importPath: `import { DownloadButton } from '@djangocfg/ui';`,
|
|
111
|
-
example: `// Simple download
|
|
112
|
-
<DownloadButton
|
|
113
|
-
url="/api/files/report.pdf"
|
|
114
|
-
filename="monthly-report.pdf"
|
|
115
|
-
>
|
|
116
|
-
Download Report
|
|
117
|
-
</DownloadButton>
|
|
118
|
-
|
|
119
|
-
// With callbacks
|
|
120
|
-
<DownloadButton
|
|
121
|
-
url="/api/export/users"
|
|
122
|
-
method="POST"
|
|
123
|
-
body={{ format: "csv" }}
|
|
124
|
-
onDownloadStart={() => console.log("Starting...")}
|
|
125
|
-
onDownloadComplete={(filename) => console.log("Done:", filename)}
|
|
126
|
-
onDownloadError={(error) => console.error("Error:", error)}
|
|
127
|
-
>
|
|
128
|
-
Export Users
|
|
129
|
-
</DownloadButton>
|
|
130
|
-
|
|
131
|
-
// Different variants
|
|
132
|
-
<DownloadButton url="/api/data" variant="outline" size="sm">
|
|
133
|
-
Download Data
|
|
134
|
-
</DownloadButton>`,
|
|
135
|
-
preview: (
|
|
136
|
-
<div className="space-y-4">
|
|
137
|
-
{/* Basic example */}
|
|
138
|
-
<div className="space-y-2">
|
|
139
|
-
<p className="text-sm font-medium">Basic Download:</p>
|
|
140
|
-
<DownloadButton
|
|
141
|
-
url="https://jsonplaceholder.typicode.com/posts/1"
|
|
142
|
-
filename="example-data.json"
|
|
143
|
-
>
|
|
144
|
-
Download Example
|
|
145
|
-
</DownloadButton>
|
|
146
|
-
</div>
|
|
147
|
-
|
|
148
|
-
{/* Variants */}
|
|
149
|
-
<div className="space-y-2">
|
|
150
|
-
<p className="text-sm font-medium">Variants:</p>
|
|
151
|
-
<div className="flex flex-wrap gap-2">
|
|
152
|
-
<DownloadButton
|
|
153
|
-
url="https://jsonplaceholder.typicode.com/posts/1"
|
|
154
|
-
filename="data.json"
|
|
155
|
-
variant="default"
|
|
156
|
-
>
|
|
157
|
-
Default
|
|
158
|
-
</DownloadButton>
|
|
159
|
-
<DownloadButton
|
|
160
|
-
url="https://jsonplaceholder.typicode.com/posts/1"
|
|
161
|
-
filename="data.json"
|
|
162
|
-
variant="outline"
|
|
163
|
-
>
|
|
164
|
-
Outline
|
|
165
|
-
</DownloadButton>
|
|
166
|
-
<DownloadButton
|
|
167
|
-
url="https://jsonplaceholder.typicode.com/posts/1"
|
|
168
|
-
filename="data.json"
|
|
169
|
-
variant="secondary"
|
|
170
|
-
>
|
|
171
|
-
Secondary
|
|
172
|
-
</DownloadButton>
|
|
173
|
-
</div>
|
|
174
|
-
</div>
|
|
175
|
-
|
|
176
|
-
{/* Sizes */}
|
|
177
|
-
<div className="space-y-2">
|
|
178
|
-
<p className="text-sm font-medium">Sizes:</p>
|
|
179
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
180
|
-
<DownloadButton
|
|
181
|
-
url="https://jsonplaceholder.typicode.com/posts/1"
|
|
182
|
-
filename="data.json"
|
|
183
|
-
size="sm"
|
|
184
|
-
>
|
|
185
|
-
Small
|
|
186
|
-
</DownloadButton>
|
|
187
|
-
<DownloadButton
|
|
188
|
-
url="https://jsonplaceholder.typicode.com/posts/1"
|
|
189
|
-
filename="data.json"
|
|
190
|
-
size="default"
|
|
191
|
-
>
|
|
192
|
-
Default
|
|
193
|
-
</DownloadButton>
|
|
194
|
-
<DownloadButton
|
|
195
|
-
url="https://jsonplaceholder.typicode.com/posts/1"
|
|
196
|
-
filename="data.json"
|
|
197
|
-
size="lg"
|
|
198
|
-
>
|
|
199
|
-
Large
|
|
200
|
-
</DownloadButton>
|
|
201
|
-
</div>
|
|
202
|
-
</div>
|
|
203
|
-
</div>
|
|
204
|
-
),
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
name: 'Input',
|
|
208
|
-
category: 'forms',
|
|
209
|
-
description: 'Text input field with validation support',
|
|
210
|
-
importPath: "import { Input } from '@djangocfg/ui';",
|
|
211
|
-
example: `<Input type="text" placeholder="Enter text..." />
|
|
212
|
-
<Input type="email" placeholder="Email" />
|
|
213
|
-
<Input type="password" placeholder="Password" disabled />`,
|
|
214
|
-
preview: (
|
|
215
|
-
<div className="space-y-3 max-w-sm">
|
|
216
|
-
<Input type="text" placeholder="Enter text..." />
|
|
217
|
-
<Input type="email" placeholder="Email" />
|
|
218
|
-
<Input type="password" placeholder="Password" disabled />
|
|
219
|
-
</div>
|
|
220
|
-
),
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
name: 'Checkbox',
|
|
224
|
-
category: 'forms',
|
|
225
|
-
description: 'Checkbox with label support',
|
|
226
|
-
importPath: "import { Checkbox, Label } from '@djangocfg/ui';",
|
|
227
|
-
example: `<div className="flex items-center gap-2">
|
|
228
|
-
<Checkbox id="terms" />
|
|
229
|
-
<Label htmlFor="terms">Accept terms and conditions</Label>
|
|
230
|
-
</div>`,
|
|
231
|
-
preview: (
|
|
232
|
-
<div className="flex items-center gap-2">
|
|
233
|
-
<Checkbox id="terms" />
|
|
234
|
-
<Label htmlFor="terms">Accept terms and conditions</Label>
|
|
235
|
-
</div>
|
|
236
|
-
),
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
name: 'RadioGroup',
|
|
240
|
-
category: 'forms',
|
|
241
|
-
description: 'Radio button group for single selection',
|
|
242
|
-
importPath: "import { RadioGroup, RadioGroupItem, Label } from '@djangocfg/ui';",
|
|
243
|
-
example: `<RadioGroup defaultValue="option1">
|
|
244
|
-
<div className="flex items-center gap-2">
|
|
245
|
-
<RadioGroupItem value="option1" id="opt1" />
|
|
246
|
-
<Label htmlFor="opt1">Option 1</Label>
|
|
247
|
-
</div>
|
|
248
|
-
<div className="flex items-center gap-2">
|
|
249
|
-
<RadioGroupItem value="option2" id="opt2" />
|
|
250
|
-
<Label htmlFor="opt2">Option 2</Label>
|
|
251
|
-
</div>
|
|
252
|
-
</RadioGroup>`,
|
|
253
|
-
preview: (
|
|
254
|
-
<RadioGroup defaultValue="option1">
|
|
255
|
-
<div className="flex items-center gap-2">
|
|
256
|
-
<RadioGroupItem value="option1" id="opt1" />
|
|
257
|
-
<Label htmlFor="opt1">Option 1</Label>
|
|
258
|
-
</div>
|
|
259
|
-
<div className="flex items-center gap-2">
|
|
260
|
-
<RadioGroupItem value="option2" id="opt2" />
|
|
261
|
-
<Label htmlFor="opt2">Option 2</Label>
|
|
262
|
-
</div>
|
|
263
|
-
</RadioGroup>
|
|
264
|
-
),
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
name: 'Select',
|
|
268
|
-
category: 'forms',
|
|
269
|
-
description: 'Dropdown select component',
|
|
270
|
-
importPath: "import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@djangocfg/ui';",
|
|
271
|
-
example: `<Select>
|
|
272
|
-
<SelectTrigger className="w-[200px]">
|
|
273
|
-
<SelectValue placeholder="Select option" />
|
|
274
|
-
</SelectTrigger>
|
|
275
|
-
<SelectContent>
|
|
276
|
-
<SelectItem value="1">Option 1</SelectItem>
|
|
277
|
-
<SelectItem value="2">Option 2</SelectItem>
|
|
278
|
-
<SelectItem value="3">Option 3</SelectItem>
|
|
279
|
-
</SelectContent>
|
|
280
|
-
</Select>`,
|
|
281
|
-
preview: (
|
|
282
|
-
<Select>
|
|
283
|
-
<SelectTrigger className="w-[200px]">
|
|
284
|
-
<SelectValue placeholder="Select option" />
|
|
285
|
-
</SelectTrigger>
|
|
286
|
-
<SelectContent>
|
|
287
|
-
<SelectItem value="1">Option 1</SelectItem>
|
|
288
|
-
<SelectItem value="2">Option 2</SelectItem>
|
|
289
|
-
<SelectItem value="3">Option 3</SelectItem>
|
|
290
|
-
</SelectContent>
|
|
291
|
-
</Select>
|
|
292
|
-
),
|
|
293
|
-
},
|
|
294
|
-
{
|
|
295
|
-
name: 'Textarea',
|
|
296
|
-
category: 'forms',
|
|
297
|
-
description: 'Multi-line text input',
|
|
298
|
-
importPath: "import { Textarea } from '@djangocfg/ui';",
|
|
299
|
-
example: `<Textarea placeholder="Enter your message..." rows={4} />`,
|
|
300
|
-
preview: (
|
|
301
|
-
<Textarea placeholder="Enter your message..." rows={4} className="max-w-sm" />
|
|
302
|
-
),
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
name: 'Switch',
|
|
306
|
-
category: 'forms',
|
|
307
|
-
description: 'Toggle switch component',
|
|
308
|
-
importPath: "import { Switch, Label } from '@djangocfg/ui';",
|
|
309
|
-
example: `<div className="flex items-center gap-2">
|
|
310
|
-
<Switch id="notifications" />
|
|
311
|
-
<Label htmlFor="notifications">Enable notifications</Label>
|
|
312
|
-
</div>`,
|
|
313
|
-
preview: (
|
|
314
|
-
<div className="flex items-center gap-2">
|
|
315
|
-
<Switch id="notifications" />
|
|
316
|
-
<Label htmlFor="notifications">Enable notifications</Label>
|
|
317
|
-
</div>
|
|
318
|
-
),
|
|
319
|
-
},
|
|
320
|
-
{
|
|
321
|
-
name: 'Slider',
|
|
322
|
-
category: 'forms',
|
|
323
|
-
description: 'Range slider input',
|
|
324
|
-
importPath: "import { Slider } from '@djangocfg/ui';",
|
|
325
|
-
example: `<Slider defaultValue={[50]} max={100} step={1} className="w-[200px]" />`,
|
|
326
|
-
preview: (
|
|
327
|
-
<Slider defaultValue={[50]} max={100} step={1} className="w-[200px]" />
|
|
328
|
-
),
|
|
329
|
-
},
|
|
330
|
-
{
|
|
331
|
-
name: 'Combobox',
|
|
332
|
-
category: 'forms',
|
|
333
|
-
description: 'Searchable dropdown with autocomplete',
|
|
334
|
-
importPath: "import { Combobox } from '@djangocfg/ui';",
|
|
335
|
-
example: `<Combobox
|
|
336
|
-
options={[
|
|
337
|
-
{ value: "javascript", label: "JavaScript" },
|
|
338
|
-
{ value: "typescript", label: "TypeScript" },
|
|
339
|
-
{ value: "python", label: "Python" },
|
|
340
|
-
{ value: "rust", label: "Rust" },
|
|
341
|
-
]}
|
|
342
|
-
placeholder="Select language..."
|
|
343
|
-
searchPlaceholder="Search language..."
|
|
344
|
-
emptyText="No language found."
|
|
345
|
-
/>`,
|
|
346
|
-
preview: (
|
|
347
|
-
<Combobox
|
|
348
|
-
options={[
|
|
349
|
-
{ value: "javascript", label: "JavaScript" },
|
|
350
|
-
{ value: "typescript", label: "TypeScript" },
|
|
351
|
-
{ value: "python", label: "Python" },
|
|
352
|
-
{ value: "rust", label: "Rust" },
|
|
353
|
-
]}
|
|
354
|
-
placeholder="Select language..."
|
|
355
|
-
searchPlaceholder="Search language..."
|
|
356
|
-
emptyText="No language found."
|
|
357
|
-
className="w-[200px]"
|
|
358
|
-
/>
|
|
359
|
-
),
|
|
360
|
-
},
|
|
361
|
-
{
|
|
362
|
-
name: 'MultiSelect',
|
|
363
|
-
category: 'forms',
|
|
364
|
-
description: 'Multi-select dropdown with badges and search functionality',
|
|
365
|
-
importPath: "import { MultiSelect } from '@djangocfg/ui';",
|
|
366
|
-
example: `<MultiSelect
|
|
367
|
-
options={[
|
|
368
|
-
{ value: "react", label: "React", description: "A JavaScript library for building user interfaces" },
|
|
369
|
-
{ value: "vue", label: "Vue", description: "The Progressive JavaScript Framework" },
|
|
370
|
-
{ value: "angular", label: "Angular", description: "Platform for building web applications" },
|
|
371
|
-
{ value: "svelte", label: "Svelte", description: "Cybernetically enhanced web apps" },
|
|
372
|
-
{ value: "next", label: "Next.js", description: "The React Framework for Production" },
|
|
373
|
-
]}
|
|
374
|
-
placeholder="Select frameworks..."
|
|
375
|
-
searchPlaceholder="Search frameworks..."
|
|
376
|
-
emptyText="No framework found."
|
|
377
|
-
maxDisplay={2}
|
|
378
|
-
onChange={(value) => console.log('Selected:', value)}
|
|
379
|
-
/>`,
|
|
380
|
-
preview: (
|
|
381
|
-
<MultiSelect
|
|
382
|
-
options={[
|
|
383
|
-
{ value: "react", label: "React", description: "A JavaScript library for building user interfaces" },
|
|
384
|
-
{ value: "vue", label: "Vue", description: "The Progressive JavaScript Framework" },
|
|
385
|
-
{ value: "angular", label: "Angular", description: "Platform for building web applications" },
|
|
386
|
-
{ value: "svelte", label: "Svelte", description: "Cybernetically enhanced web apps" },
|
|
387
|
-
{ value: "next", label: "Next.js", description: "The React Framework for Production" },
|
|
388
|
-
]}
|
|
389
|
-
placeholder="Select frameworks..."
|
|
390
|
-
searchPlaceholder="Search frameworks..."
|
|
391
|
-
emptyText="No framework found."
|
|
392
|
-
maxDisplay={2}
|
|
393
|
-
className="w-[300px]"
|
|
394
|
-
/>
|
|
395
|
-
),
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
name: 'MultiSelectPro',
|
|
399
|
-
category: 'forms',
|
|
400
|
-
description: 'Advanced multi-select with animations, custom styling, grouped options, and comprehensive accessibility. Supports variants, icons, gradients, responsive design, and imperative control via ref.',
|
|
401
|
-
importPath: "import { MultiSelectPro } from '@djangocfg/ui';",
|
|
402
|
-
example: `import { MultiSelectPro } from '@djangocfg/ui';
|
|
403
|
-
import type { MultiSelectProOption } from '@djangocfg/ui';
|
|
404
|
-
import { useState } from 'react';
|
|
405
|
-
|
|
406
|
-
// Basic usage
|
|
407
|
-
const [selected, setSelected] = useState<string[]>([]);
|
|
408
|
-
|
|
409
|
-
<MultiSelectPro
|
|
410
|
-
options={[
|
|
411
|
-
{ value: "react", label: "React" },
|
|
412
|
-
{ value: "vue", label: "Vue.js" },
|
|
413
|
-
{ value: "angular", label: "Angular" },
|
|
414
|
-
]}
|
|
415
|
-
onValueChange={setSelected}
|
|
416
|
-
defaultValue={selected}
|
|
417
|
-
placeholder="Select frameworks..."
|
|
418
|
-
/>
|
|
419
|
-
|
|
420
|
-
// With custom styling and icons
|
|
421
|
-
const styledOptions = [
|
|
422
|
-
{
|
|
423
|
-
value: "react",
|
|
424
|
-
label: "React",
|
|
425
|
-
style: {
|
|
426
|
-
badgeColor: "#61DAFB",
|
|
427
|
-
iconColor: "#282C34",
|
|
428
|
-
},
|
|
429
|
-
},
|
|
430
|
-
{
|
|
431
|
-
value: "vue",
|
|
432
|
-
label: "Vue.js",
|
|
433
|
-
style: {
|
|
434
|
-
gradient: "linear-gradient(135deg, #4FC08D 0%, #42B883 100%)",
|
|
435
|
-
},
|
|
436
|
-
},
|
|
437
|
-
];
|
|
438
|
-
|
|
439
|
-
<MultiSelectPro
|
|
440
|
-
options={styledOptions}
|
|
441
|
-
onValueChange={setSelected}
|
|
442
|
-
variant="secondary"
|
|
443
|
-
animationConfig={{
|
|
444
|
-
badgeAnimation: "bounce",
|
|
445
|
-
popoverAnimation: "scale",
|
|
446
|
-
duration: 0.3,
|
|
447
|
-
}}
|
|
448
|
-
maxCount={3}
|
|
449
|
-
closeOnSelect={false}
|
|
450
|
-
/>
|
|
451
|
-
|
|
452
|
-
// With grouped options
|
|
453
|
-
const groupedOptions = [
|
|
454
|
-
{
|
|
455
|
-
heading: "Frontend Frameworks",
|
|
456
|
-
options: [
|
|
457
|
-
{ value: "react", label: "React" },
|
|
458
|
-
{ value: "vue", label: "Vue.js" },
|
|
459
|
-
{ value: "angular", label: "Angular", disabled: true },
|
|
460
|
-
],
|
|
461
|
-
},
|
|
462
|
-
{
|
|
463
|
-
heading: "Backend Technologies",
|
|
464
|
-
options: [
|
|
465
|
-
{ value: "node", label: "Node.js" },
|
|
466
|
-
{ value: "python", label: "Python" },
|
|
467
|
-
],
|
|
468
|
-
},
|
|
469
|
-
];
|
|
470
|
-
|
|
471
|
-
<MultiSelectPro
|
|
472
|
-
options={groupedOptions}
|
|
473
|
-
onValueChange={setSelected}
|
|
474
|
-
placeholder="Select technologies..."
|
|
475
|
-
searchable={true}
|
|
476
|
-
responsive={true}
|
|
477
|
-
minWidth="200px"
|
|
478
|
-
maxWidth="500px"
|
|
479
|
-
/>
|
|
480
|
-
|
|
481
|
-
// With imperative control via ref
|
|
482
|
-
import { useRef } from 'react';
|
|
483
|
-
import type { MultiSelectProRef } from '@djangocfg/ui';
|
|
484
|
-
|
|
485
|
-
const ref = useRef<MultiSelectProRef>(null);
|
|
486
|
-
|
|
487
|
-
// Later in code:
|
|
488
|
-
ref.current?.clear();
|
|
489
|
-
ref.current?.reset();
|
|
490
|
-
ref.current?.setSelectedValues(['react', 'vue']);
|
|
491
|
-
const values = ref.current?.getSelectedValues();`,
|
|
492
|
-
preview: (
|
|
493
|
-
<div className="space-y-6">
|
|
494
|
-
<div className="space-y-2">
|
|
495
|
-
<p className="text-sm font-medium">Basic with animations:</p>
|
|
496
|
-
<MultiSelectPro
|
|
497
|
-
options={[
|
|
498
|
-
{ value: "react", label: "React" },
|
|
499
|
-
{ value: "vue", label: "Vue" },
|
|
500
|
-
{ value: "angular", label: "Angular" },
|
|
501
|
-
{ value: "svelte", label: "Svelte" },
|
|
502
|
-
]}
|
|
503
|
-
defaultValue={[]}
|
|
504
|
-
onValueChange={() => {}}
|
|
505
|
-
placeholder="Select frameworks..."
|
|
506
|
-
animationConfig={{
|
|
507
|
-
badgeAnimation: "bounce",
|
|
508
|
-
popoverAnimation: "scale",
|
|
509
|
-
duration: 0.3,
|
|
510
|
-
}}
|
|
511
|
-
maxCount={2}
|
|
512
|
-
className="w-[350px]"
|
|
513
|
-
/>
|
|
514
|
-
</div>
|
|
515
|
-
|
|
516
|
-
<div className="space-y-2">
|
|
517
|
-
<p className="text-sm font-medium">With variants and styles:</p>
|
|
518
|
-
<div className="grid grid-cols-2 gap-2">
|
|
519
|
-
<MultiSelectPro
|
|
520
|
-
options={[
|
|
521
|
-
{ value: "1", label: "Option 1" },
|
|
522
|
-
{ value: "2", label: "Option 2" },
|
|
523
|
-
]}
|
|
524
|
-
defaultValue={["1"]}
|
|
525
|
-
onValueChange={() => {}}
|
|
526
|
-
variant="secondary"
|
|
527
|
-
placeholder="Secondary"
|
|
528
|
-
maxCount={1}
|
|
529
|
-
/>
|
|
530
|
-
<MultiSelectPro
|
|
531
|
-
options={[
|
|
532
|
-
{ value: "1", label: "Option 1" },
|
|
533
|
-
{ value: "2", label: "Option 2" },
|
|
534
|
-
]}
|
|
535
|
-
defaultValue={["2"]}
|
|
536
|
-
onValueChange={() => {}}
|
|
537
|
-
variant="destructive"
|
|
538
|
-
placeholder="Destructive"
|
|
539
|
-
maxCount={1}
|
|
540
|
-
/>
|
|
541
|
-
</div>
|
|
542
|
-
</div>
|
|
543
|
-
|
|
544
|
-
<div className="space-y-2">
|
|
545
|
-
<p className="text-sm font-medium">Grouped options:</p>
|
|
546
|
-
<MultiSelectPro
|
|
547
|
-
options={[
|
|
548
|
-
{
|
|
549
|
-
heading: "Frontend",
|
|
550
|
-
options: [
|
|
551
|
-
{ value: "react", label: "React" },
|
|
552
|
-
{ value: "vue", label: "Vue" },
|
|
553
|
-
],
|
|
554
|
-
},
|
|
555
|
-
{
|
|
556
|
-
heading: "Backend",
|
|
557
|
-
options: [
|
|
558
|
-
{ value: "node", label: "Node.js" },
|
|
559
|
-
{ value: "python", label: "Python" },
|
|
560
|
-
],
|
|
561
|
-
},
|
|
562
|
-
]}
|
|
563
|
-
defaultValue={[]}
|
|
564
|
-
onValueChange={() => {}}
|
|
565
|
-
placeholder="Select from groups..."
|
|
566
|
-
className="w-[350px]"
|
|
567
|
-
maxCount={2}
|
|
568
|
-
/>
|
|
569
|
-
</div>
|
|
570
|
-
|
|
571
|
-
<div className="p-4 border rounded-md bg-muted/50">
|
|
572
|
-
<p className="text-sm font-medium mb-2">Features:</p>
|
|
573
|
-
<ul className="grid grid-cols-2 gap-x-4 gap-y-1 text-sm text-muted-foreground">
|
|
574
|
-
<li>✨ Multiple variants (default, secondary, destructive, inverted)</li>
|
|
575
|
-
<li>🌈 Custom badge colors & gradients</li>
|
|
576
|
-
<li>📁 Grouped options with separators</li>
|
|
577
|
-
<li>🚫 Disabled options support</li>
|
|
578
|
-
<li>🎨 Badge animations (bounce, pulse, wiggle, fade, slide)</li>
|
|
579
|
-
<li>🔍 Built-in search & filter</li>
|
|
580
|
-
<li>📱 Responsive design (mobile/tablet/desktop)</li>
|
|
581
|
-
<li>📐 Width constraints (min/max)</li>
|
|
582
|
-
<li>♿ Full accessibility (ARIA, keyboard nav)</li>
|
|
583
|
-
<li>🔧 Imperative control via ref</li>
|
|
584
|
-
<li>🔄 Duplicate handling</li>
|
|
585
|
-
<li>🎛️ Auto-close, single-line, auto-size modes</li>
|
|
586
|
-
</ul>
|
|
587
|
-
</div>
|
|
588
|
-
</div>
|
|
589
|
-
),
|
|
590
|
-
},
|
|
591
|
-
{
|
|
592
|
-
name: 'MultiSelectProAsync',
|
|
593
|
-
category: 'forms',
|
|
594
|
-
description: 'Async multi-select with external API search, debouncing, and loading states. Perfect for large datasets and server-side filtering.',
|
|
595
|
-
importPath: "import { MultiSelectProAsync, useDebounce } from '@djangocfg/ui';",
|
|
596
|
-
example: `import { MultiSelectProAsync, useDebounce } from '@djangocfg/ui';
|
|
597
|
-
import { useState, useEffect } from 'react';
|
|
598
|
-
|
|
599
|
-
// Mock API function (replace with your actual API)
|
|
600
|
-
const searchAPI = async (query: string) => {
|
|
601
|
-
const response = await fetch(\`/api/search?q=\${query}\`);
|
|
602
|
-
return response.json();
|
|
603
|
-
};
|
|
604
|
-
|
|
605
|
-
function AsyncExample() {
|
|
606
|
-
const [searchValue, setSearchValue] = useState('');
|
|
607
|
-
const [options, setOptions] = useState([]);
|
|
608
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
609
|
-
const [selected, setSelected] = useState<string[]>([]);
|
|
610
|
-
|
|
611
|
-
// Debounce search to reduce API calls
|
|
612
|
-
const debouncedSearch = useDebounce(searchValue, 300);
|
|
613
|
-
|
|
614
|
-
// Fetch options when debounced search changes
|
|
615
|
-
useEffect(() => {
|
|
616
|
-
if (!debouncedSearch) {
|
|
617
|
-
setOptions([]);
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
const fetchOptions = async () => {
|
|
622
|
-
setIsLoading(true);
|
|
623
|
-
try {
|
|
624
|
-
const results = await searchAPI(debouncedSearch);
|
|
625
|
-
setOptions(results);
|
|
626
|
-
} catch (error) {
|
|
627
|
-
console.error('Search failed:', error);
|
|
628
|
-
} finally {
|
|
629
|
-
setIsLoading(false);
|
|
630
|
-
}
|
|
631
|
-
};
|
|
632
|
-
|
|
633
|
-
fetchOptions();
|
|
634
|
-
}, [debouncedSearch]);
|
|
635
|
-
|
|
636
|
-
return (
|
|
637
|
-
<MultiSelectProAsync
|
|
638
|
-
// Search control (managed by parent)
|
|
639
|
-
searchValue={searchValue}
|
|
640
|
-
onSearchChange={setSearchValue}
|
|
641
|
-
isLoading={isLoading}
|
|
642
|
-
|
|
643
|
-
// Options from API
|
|
644
|
-
options={options}
|
|
645
|
-
|
|
646
|
-
// Selection
|
|
647
|
-
onValueChange={setSelected}
|
|
648
|
-
defaultValue={selected}
|
|
649
|
-
|
|
650
|
-
// UI
|
|
651
|
-
placeholder="Search and select..."
|
|
652
|
-
searchPlaceholder="Type to search..."
|
|
653
|
-
emptyText="No results found"
|
|
654
|
-
loadingText="Searching..."
|
|
655
|
-
|
|
656
|
-
// Features
|
|
657
|
-
variant="default"
|
|
658
|
-
maxCount={3}
|
|
659
|
-
closeOnSelect={false}
|
|
660
|
-
/>
|
|
661
|
-
);
|
|
662
|
-
}`,
|
|
663
|
-
preview: (() => {
|
|
664
|
-
// Demo component with mock data
|
|
665
|
-
const DemoAsync = () => {
|
|
666
|
-
const [searchValue, setSearchValue] = useState('');
|
|
667
|
-
const [selected, setSelected] = useState<string[]>([]);
|
|
668
|
-
const debouncedSearch = useDebounce(searchValue, 300);
|
|
669
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
670
|
-
|
|
671
|
-
// Mock database of countries
|
|
672
|
-
const allCountries = useMemo(() => [
|
|
673
|
-
{ value: 'us', label: 'United States' },
|
|
674
|
-
{ value: 'uk', label: 'United Kingdom' },
|
|
675
|
-
{ value: 'ca', label: 'Canada' },
|
|
676
|
-
{ value: 'au', label: 'Australia' },
|
|
677
|
-
{ value: 'de', label: 'Germany' },
|
|
678
|
-
{ value: 'fr', label: 'France' },
|
|
679
|
-
{ value: 'it', label: 'Italy' },
|
|
680
|
-
{ value: 'es', label: 'Spain' },
|
|
681
|
-
{ value: 'jp', label: 'Japan' },
|
|
682
|
-
{ value: 'cn', label: 'China' },
|
|
683
|
-
{ value: 'in', label: 'India' },
|
|
684
|
-
{ value: 'br', label: 'Brazil' },
|
|
685
|
-
{ value: 'mx', label: 'Mexico' },
|
|
686
|
-
{ value: 'ru', label: 'Russia' },
|
|
687
|
-
{ value: 'za', label: 'South Africa' },
|
|
688
|
-
], []);
|
|
689
|
-
|
|
690
|
-
// Filtered options based on debounced search
|
|
691
|
-
const filteredOptions = useMemo(() => {
|
|
692
|
-
if (!debouncedSearch) return [];
|
|
693
|
-
return allCountries.filter(country =>
|
|
694
|
-
country.label.toLowerCase().includes(debouncedSearch.toLowerCase())
|
|
695
|
-
);
|
|
696
|
-
}, [debouncedSearch, allCountries]);
|
|
697
|
-
|
|
698
|
-
// Simulate API loading
|
|
699
|
-
useEffect(() => {
|
|
700
|
-
if (debouncedSearch) {
|
|
701
|
-
setIsLoading(true);
|
|
702
|
-
const timer = setTimeout(() => setIsLoading(false), 300);
|
|
703
|
-
return () => clearTimeout(timer);
|
|
704
|
-
}
|
|
705
|
-
}, [debouncedSearch]);
|
|
706
|
-
|
|
707
|
-
return (
|
|
708
|
-
<div className="space-y-4">
|
|
709
|
-
<MultiSelectProAsync
|
|
710
|
-
searchValue={searchValue}
|
|
711
|
-
onSearchChange={setSearchValue}
|
|
712
|
-
isLoading={isLoading}
|
|
713
|
-
options={filteredOptions}
|
|
714
|
-
onValueChange={setSelected}
|
|
715
|
-
defaultValue={selected}
|
|
716
|
-
placeholder="Search countries..."
|
|
717
|
-
searchPlaceholder="Type to search countries..."
|
|
718
|
-
emptyText="No countries found"
|
|
719
|
-
loadingText="Searching..."
|
|
720
|
-
maxCount={2}
|
|
721
|
-
className="w-[350px]"
|
|
722
|
-
/>
|
|
723
|
-
|
|
724
|
-
<div className="p-4 border rounded-md bg-muted/50">
|
|
725
|
-
<p className="text-sm font-medium mb-2">Features:</p>
|
|
726
|
-
<ul className="space-y-1 text-sm text-muted-foreground">
|
|
727
|
-
<li>🔍 <strong>Async search</strong> - Controlled search value for API integration</li>
|
|
728
|
-
<li>⏱️ <strong>Debouncing</strong> - Use useDebounce hook to reduce API calls</li>
|
|
729
|
-
<li>🔄 <strong>Loading states</strong> - Shows spinner during data fetching</li>
|
|
730
|
-
<li>🧹 <strong>Auto-clear</strong> - Clears search when popover closes</li>
|
|
731
|
-
<li>✨ <strong>All MultiSelectPro features</strong> - Animations, variants, groups, etc.</li>
|
|
732
|
-
</ul>
|
|
733
|
-
<div className="mt-3 p-2 bg-muted rounded text-xs">
|
|
734
|
-
<strong>Try it:</strong> Type "united" or "india" to see async filtering
|
|
735
|
-
</div>
|
|
736
|
-
</div>
|
|
737
|
-
</div>
|
|
738
|
-
);
|
|
739
|
-
};
|
|
740
|
-
|
|
741
|
-
return <DemoAsync />;
|
|
742
|
-
})(),
|
|
743
|
-
},
|
|
744
|
-
{
|
|
745
|
-
name: 'InputOTP',
|
|
746
|
-
category: 'forms',
|
|
747
|
-
description: 'One-time password input component',
|
|
748
|
-
importPath: "import { InputOTP, InputOTPGroup, InputOTPSlot } from '@djangocfg/ui';",
|
|
749
|
-
example: `<InputOTP maxLength={6}>
|
|
750
|
-
<InputOTPGroup>
|
|
751
|
-
<InputOTPSlot index={0} />
|
|
752
|
-
<InputOTPSlot index={1} />
|
|
753
|
-
<InputOTPSlot index={2} />
|
|
754
|
-
<InputOTPSlot index={3} />
|
|
755
|
-
<InputOTPSlot index={4} />
|
|
756
|
-
<InputOTPSlot index={5} />
|
|
757
|
-
</InputOTPGroup>
|
|
758
|
-
</InputOTP>`,
|
|
759
|
-
preview: (
|
|
760
|
-
<InputOTP maxLength={6}>
|
|
761
|
-
<InputOTPGroup>
|
|
762
|
-
<InputOTPSlot index={0} />
|
|
763
|
-
<InputOTPSlot index={1} />
|
|
764
|
-
<InputOTPSlot index={2} />
|
|
765
|
-
<InputOTPSlot index={3} />
|
|
766
|
-
<InputOTPSlot index={4} />
|
|
767
|
-
<InputOTPSlot index={5} />
|
|
768
|
-
</InputOTPGroup>
|
|
769
|
-
</InputOTP>
|
|
770
|
-
),
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
name: 'PhoneInput',
|
|
774
|
-
category: 'forms',
|
|
775
|
-
description: 'International phone number input with country selector',
|
|
776
|
-
importPath: "import { PhoneInput } from '@djangocfg/ui';",
|
|
777
|
-
example: `<PhoneInput
|
|
778
|
-
defaultCountry="US"
|
|
779
|
-
placeholder="Enter phone number"
|
|
780
|
-
className="max-w-sm"
|
|
781
|
-
/>`,
|
|
782
|
-
preview: (
|
|
783
|
-
<PhoneInput
|
|
784
|
-
defaultCountry="US"
|
|
785
|
-
placeholder="Enter phone number"
|
|
786
|
-
className="max-w-sm"
|
|
787
|
-
/>
|
|
788
|
-
),
|
|
789
|
-
},
|
|
790
|
-
{
|
|
791
|
-
name: 'Form',
|
|
792
|
-
category: 'forms',
|
|
793
|
-
description: 'React Hook Form wrapper with form validation',
|
|
794
|
-
importPath: "import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@djangocfg/ui';",
|
|
795
|
-
example: `// Requires react-hook-form
|
|
796
|
-
import { useForm } from 'react-hook-form';
|
|
797
|
-
|
|
798
|
-
function MyForm() {
|
|
799
|
-
const form = useForm();
|
|
800
|
-
|
|
801
|
-
return (
|
|
802
|
-
<Form {...form}>
|
|
803
|
-
<form onSubmit={form.handleSubmit(onSubmit)}>
|
|
804
|
-
<FormField
|
|
805
|
-
control={form.control}
|
|
806
|
-
name="username"
|
|
807
|
-
render={({ field }) => (
|
|
808
|
-
<FormItem>
|
|
809
|
-
<FormLabel>Username</FormLabel>
|
|
810
|
-
<FormControl>
|
|
811
|
-
<Input placeholder="Enter username" {...field} />
|
|
812
|
-
</FormControl>
|
|
813
|
-
<FormDescription>
|
|
814
|
-
Your public display name
|
|
815
|
-
</FormDescription>
|
|
816
|
-
<FormMessage />
|
|
817
|
-
</FormItem>
|
|
818
|
-
)}
|
|
819
|
-
/>
|
|
820
|
-
</form>
|
|
821
|
-
</Form>
|
|
822
|
-
);
|
|
823
|
-
}`,
|
|
824
|
-
preview: (
|
|
825
|
-
<div className="p-6 border rounded-md bg-muted/50">
|
|
826
|
-
<p className="text-sm text-muted-foreground mb-4">
|
|
827
|
-
The Form component is a wrapper around React Hook Form with:
|
|
828
|
-
</p>
|
|
829
|
-
<ul className="space-y-2 text-sm text-muted-foreground">
|
|
830
|
-
<li>• Integrated form validation</li>
|
|
831
|
-
<li>• Accessible form fields</li>
|
|
832
|
-
<li>• Error message handling</li>
|
|
833
|
-
<li>• Field descriptions and labels</li>
|
|
834
|
-
<li>• Type-safe with TypeScript</li>
|
|
835
|
-
</ul>
|
|
836
|
-
<p className="text-xs text-muted-foreground mt-4">
|
|
837
|
-
See the <strong>react-hook-form</strong> documentation for usage examples
|
|
838
|
-
</p>
|
|
839
|
-
</div>
|
|
840
|
-
),
|
|
841
|
-
},
|
|
842
|
-
{
|
|
843
|
-
name: 'Field',
|
|
844
|
-
category: 'forms',
|
|
845
|
-
description: 'Advanced field component with label, description and validation',
|
|
846
|
-
importPath: "import { Field, FieldGroup, FieldSet, FieldLegend } from '@djangocfg/ui';",
|
|
847
|
-
example: `<FieldSet>
|
|
848
|
-
<FieldLegend>Account Information</FieldLegend>
|
|
849
|
-
<FieldGroup>
|
|
850
|
-
<Field>
|
|
851
|
-
<FieldLabel>Username</FieldLabel>
|
|
852
|
-
<Input placeholder="Enter username" />
|
|
853
|
-
<FieldDescription>
|
|
854
|
-
Your unique username for the platform
|
|
855
|
-
</FieldDescription>
|
|
856
|
-
<FieldError>Username is required</FieldError>
|
|
857
|
-
</Field>
|
|
858
|
-
</FieldGroup>
|
|
859
|
-
</FieldSet>`,
|
|
860
|
-
preview: (
|
|
861
|
-
<div className="p-6 border rounded-md bg-muted/50">
|
|
862
|
-
<p className="text-sm text-muted-foreground mb-4">
|
|
863
|
-
Field component provides structured form fields with:
|
|
864
|
-
</p>
|
|
865
|
-
<ul className="space-y-2 text-sm text-muted-foreground">
|
|
866
|
-
<li>• Label and description support</li>
|
|
867
|
-
<li>• Error message handling</li>
|
|
868
|
-
<li>• Field grouping (FieldSet, FieldGroup)</li>
|
|
869
|
-
<li>• Accessible by default</li>
|
|
870
|
-
<li>• Consistent styling</li>
|
|
871
|
-
</ul>
|
|
872
|
-
<p className="text-xs text-muted-foreground mt-4">
|
|
873
|
-
Perfect for complex forms with validation
|
|
874
|
-
</p>
|
|
875
|
-
</div>
|
|
876
|
-
),
|
|
877
|
-
},
|
|
878
|
-
{
|
|
879
|
-
name: 'JsonSchemaForm',
|
|
880
|
-
category: 'forms',
|
|
881
|
-
description: 'Automatic form generator from JSON Schema with validation, custom widgets, and full type safety',
|
|
882
|
-
importPath: "import { JsonSchemaForm } from '@djangocfg/ui/tools';",
|
|
883
|
-
example: `// Basic usage
|
|
884
|
-
const schema = {
|
|
885
|
-
type: 'object',
|
|
886
|
-
required: ['name', 'email'],
|
|
887
|
-
properties: {
|
|
888
|
-
name: {
|
|
889
|
-
type: 'string',
|
|
890
|
-
title: 'Full Name',
|
|
891
|
-
minLength: 2
|
|
892
|
-
},
|
|
893
|
-
email: {
|
|
894
|
-
type: 'string',
|
|
895
|
-
title: 'Email',
|
|
896
|
-
format: 'email'
|
|
897
|
-
},
|
|
898
|
-
age: {
|
|
899
|
-
type: 'number',
|
|
900
|
-
title: 'Age',
|
|
901
|
-
minimum: 18
|
|
902
|
-
},
|
|
903
|
-
subscribe: {
|
|
904
|
-
type: 'boolean',
|
|
905
|
-
title: 'Subscribe to newsletter'
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
};
|
|
909
|
-
|
|
910
|
-
<JsonSchemaForm
|
|
911
|
-
schema={schema}
|
|
912
|
-
onSubmit={(data) => console.log(data.formData)}
|
|
913
|
-
liveValidate={false}
|
|
914
|
-
/>
|
|
915
|
-
|
|
916
|
-
// With UI Schema for customization
|
|
917
|
-
const uiSchema = {
|
|
918
|
-
subscribe: {
|
|
919
|
-
'ui:widget': 'SwitchWidget'
|
|
920
|
-
}
|
|
921
|
-
};
|
|
922
|
-
|
|
923
|
-
<JsonSchemaForm
|
|
924
|
-
schema={schema}
|
|
925
|
-
uiSchema={uiSchema}
|
|
926
|
-
formData={initialData}
|
|
927
|
-
onChange={(data) => setFormData(data.formData)}
|
|
928
|
-
onSubmit={handleSubmit}
|
|
929
|
-
/>`,
|
|
930
|
-
preview: (
|
|
931
|
-
<div className="space-y-4">
|
|
932
|
-
<JsonSchemaForm
|
|
933
|
-
schema={{
|
|
934
|
-
type: 'object',
|
|
935
|
-
required: ['name', 'email'],
|
|
936
|
-
properties: {
|
|
937
|
-
name: {
|
|
938
|
-
type: 'string',
|
|
939
|
-
title: 'Full Name',
|
|
940
|
-
description: 'Enter your first and last name',
|
|
941
|
-
minLength: 2
|
|
942
|
-
},
|
|
943
|
-
email: {
|
|
944
|
-
type: 'string',
|
|
945
|
-
title: 'Email Address',
|
|
946
|
-
format: 'email'
|
|
947
|
-
},
|
|
948
|
-
role: {
|
|
949
|
-
type: 'string',
|
|
950
|
-
title: 'Role',
|
|
951
|
-
enum: ['developer', 'designer', 'manager', 'other'],
|
|
952
|
-
default: 'developer'
|
|
953
|
-
},
|
|
954
|
-
experience: {
|
|
955
|
-
type: 'number',
|
|
956
|
-
title: 'Years of Experience',
|
|
957
|
-
minimum: 0,
|
|
958
|
-
maximum: 50,
|
|
959
|
-
default: 5
|
|
960
|
-
},
|
|
961
|
-
subscribe: {
|
|
962
|
-
type: 'boolean',
|
|
963
|
-
title: 'Subscribe to newsletter',
|
|
964
|
-
default: false
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
}}
|
|
968
|
-
uiSchema={{
|
|
969
|
-
subscribe: {
|
|
970
|
-
'ui:widget': 'SwitchWidget'
|
|
971
|
-
}
|
|
972
|
-
}}
|
|
973
|
-
onSubmit={(data) => {
|
|
974
|
-
console.log('Form submitted:', data.formData);
|
|
975
|
-
alert('Form submitted! Check console for data.');
|
|
976
|
-
}}
|
|
977
|
-
liveValidate={false}
|
|
978
|
-
showErrorList="top"
|
|
979
|
-
/>
|
|
980
|
-
|
|
981
|
-
<div className="p-4 border rounded-md bg-muted/50">
|
|
982
|
-
<p className="text-sm font-medium mb-2">Features:</p>
|
|
983
|
-
<ul className="space-y-1 text-sm text-muted-foreground">
|
|
984
|
-
<li>• Automatic form generation from JSON Schema 7</li>
|
|
985
|
-
<li>• Built-in validation with ajv8</li>
|
|
986
|
-
<li>• Custom widgets (Switch, Select, Textarea, etc.)</li>
|
|
987
|
-
<li>• Custom templates for fields, objects, arrays</li>
|
|
988
|
-
<li>• Live validation support</li>
|
|
989
|
-
<li>• Full TypeScript support</li>
|
|
990
|
-
<li>• Radix UI components integration</li>
|
|
991
|
-
</ul>
|
|
992
|
-
</div>
|
|
993
|
-
</div>
|
|
994
|
-
),
|
|
995
|
-
},
|
|
996
|
-
];
|