@djangocfg/layouts 1.4.27 → 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.
Files changed (76) hide show
  1. package/package.json +5 -5
  2. package/src/auth/middlewares/index.ts +1 -1
  3. package/src/auth/middlewares/proxy.ts +10 -2
  4. package/src/layouts/index.ts +0 -3
  5. package/src/snippets/ContactForm/ContactPage.tsx +16 -3
  6. package/src/layouts/UILayout/README.md +0 -267
  7. package/src/layouts/UILayout/SUMMARY.md +0 -298
  8. package/src/layouts/UILayout/TOOLS_INTEGRATION.md +0 -216
  9. package/src/layouts/UILayout/components/AutoComponentDemo.tsx +0 -77
  10. package/src/layouts/UILayout/components/CategoryRenderer.tsx +0 -45
  11. package/src/layouts/UILayout/components/TailwindGuideRenderer.tsx +0 -138
  12. package/src/layouts/UILayout/components/index.ts +0 -15
  13. package/src/layouts/UILayout/components/layout/Header/CopyAIButton.tsx +0 -58
  14. package/src/layouts/UILayout/components/layout/Header/Header.tsx +0 -60
  15. package/src/layouts/UILayout/components/layout/Header/HeaderDesktop.tsx +0 -51
  16. package/src/layouts/UILayout/components/layout/Header/HeaderMobile.tsx +0 -71
  17. package/src/layouts/UILayout/components/layout/Header/TestValidationButton.tsx +0 -268
  18. package/src/layouts/UILayout/components/layout/Header/index.ts +0 -11
  19. package/src/layouts/UILayout/components/layout/MobileOverlay/MobileOverlay.tsx +0 -47
  20. package/src/layouts/UILayout/components/layout/MobileOverlay/index.ts +0 -6
  21. package/src/layouts/UILayout/components/layout/Sidebar/Sidebar.tsx +0 -95
  22. package/src/layouts/UILayout/components/layout/Sidebar/SidebarCategory.tsx +0 -54
  23. package/src/layouts/UILayout/components/layout/Sidebar/SidebarContent.tsx +0 -93
  24. package/src/layouts/UILayout/components/layout/Sidebar/SidebarFooter.tsx +0 -49
  25. package/src/layouts/UILayout/components/layout/Sidebar/index.ts +0 -9
  26. package/src/layouts/UILayout/components/layout/index.ts +0 -8
  27. package/src/layouts/UILayout/components/shared/Badge/CountBadge.tsx +0 -38
  28. package/src/layouts/UILayout/components/shared/Badge/index.ts +0 -5
  29. package/src/layouts/UILayout/components/shared/CodeBlock/CodeBlock.tsx +0 -48
  30. package/src/layouts/UILayout/components/shared/CodeBlock/CopyButton.tsx +0 -49
  31. package/src/layouts/UILayout/components/shared/CodeBlock/index.ts +0 -6
  32. package/src/layouts/UILayout/components/shared/Section/Section.tsx +0 -63
  33. package/src/layouts/UILayout/components/shared/Section/index.ts +0 -5
  34. package/src/layouts/UILayout/components/shared/index.ts +0 -8
  35. package/src/layouts/UILayout/config/ai-export.config.ts +0 -89
  36. package/src/layouts/UILayout/config/categories.config.tsx +0 -122
  37. package/src/layouts/UILayout/config/components/blocks.config.tsx +0 -239
  38. package/src/layouts/UILayout/config/components/data.config.tsx +0 -433
  39. package/src/layouts/UILayout/config/components/feedback.config.tsx +0 -290
  40. package/src/layouts/UILayout/config/components/forms.config.tsx +0 -996
  41. package/src/layouts/UILayout/config/components/hooks.config.tsx +0 -168
  42. package/src/layouts/UILayout/config/components/index.ts +0 -72
  43. package/src/layouts/UILayout/config/components/layout.config.tsx +0 -246
  44. package/src/layouts/UILayout/config/components/navigation.config.tsx +0 -352
  45. package/src/layouts/UILayout/config/components/overlay.config.tsx +0 -569
  46. package/src/layouts/UILayout/config/components/specialized.config.tsx +0 -400
  47. package/src/layouts/UILayout/config/components/tools.config.tsx +0 -234
  48. package/src/layouts/UILayout/config/components/types.ts +0 -14
  49. package/src/layouts/UILayout/config/index.ts +0 -42
  50. package/src/layouts/UILayout/config/tailwind.config.ts +0 -131
  51. package/src/layouts/UILayout/constants.ts +0 -23
  52. package/src/layouts/UILayout/context/ShowcaseContext.tsx +0 -81
  53. package/src/layouts/UILayout/context/index.ts +0 -1
  54. package/src/layouts/UILayout/core/UIGuideApp.client.tsx +0 -18
  55. package/src/layouts/UILayout/core/UIGuideApp.tsx +0 -33
  56. package/src/layouts/UILayout/core/UIGuideLanding.tsx +0 -172
  57. package/src/layouts/UILayout/core/UIGuideView.tsx +0 -61
  58. package/src/layouts/UILayout/core/UILayout.tsx +0 -125
  59. package/src/layouts/UILayout/core/UILayoutSidebar.tsx +0 -11
  60. package/src/layouts/UILayout/core/index.ts +0 -10
  61. package/src/layouts/UILayout/hooks/index.ts +0 -9
  62. package/src/layouts/UILayout/hooks/useAIExport.ts +0 -78
  63. package/src/layouts/UILayout/hooks/useCategoryNavigation.ts +0 -92
  64. package/src/layouts/UILayout/hooks/useComponentSearch.ts +0 -81
  65. package/src/layouts/UILayout/hooks/useSidebarState.ts +0 -36
  66. package/src/layouts/UILayout/index.ts +0 -160
  67. package/src/layouts/UILayout/types/component.ts +0 -45
  68. package/src/layouts/UILayout/types/index.ts +0 -23
  69. package/src/layouts/UILayout/types/layout.ts +0 -57
  70. package/src/layouts/UILayout/types/navigation.ts +0 -33
  71. package/src/layouts/UILayout/utils/ai-export/formatters.ts +0 -71
  72. package/src/layouts/UILayout/utils/ai-export/index.ts +0 -5
  73. package/src/layouts/UILayout/utils/component-helpers/filter.ts +0 -109
  74. package/src/layouts/UILayout/utils/component-helpers/index.ts +0 -6
  75. package/src/layouts/UILayout/utils/component-helpers/search.ts +0 -95
  76. 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
- ];