@delightui/components 0.1.104 → 0.1.106

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 (120) hide show
  1. package/README.md +104 -1
  2. package/dist/cjs/components/molecules/Modal/DemoModal.d.ts +8 -0
  3. package/dist/cjs/components/molecules/Modal/ModalContext/ModalContext.d.ts +41 -0
  4. package/dist/cjs/components/molecules/Modal/ModalContext/ModalContext.types.d.ts +87 -0
  5. package/dist/cjs/components/molecules/Modal/ModalContext/index.d.ts +3 -0
  6. package/dist/cjs/components/molecules/Modal/ModalContext/useModal.d.ts +34 -0
  7. package/dist/cjs/components/molecules/Modal/index.d.ts +2 -0
  8. package/dist/cjs/components/molecules/Popover/Popover.presenter.d.ts +26 -0
  9. package/dist/cjs/components/molecules/Select/Option/Option.types.d.ts +6 -0
  10. package/dist/cjs/components/molecules/Select/Select.Context.d.ts +1 -1
  11. package/dist/cjs/components/molecules/Select/Select.d.ts +5 -5
  12. package/dist/cjs/components/molecules/Select/Select.presenter.d.ts +1 -0
  13. package/dist/cjs/components/molecules/Select/Select.types.d.ts +5 -0
  14. package/dist/cjs/components/molecules/Select/index.d.ts +2 -9
  15. package/dist/cjs/components/molecules/index.d.ts +2 -0
  16. package/dist/cjs/components/utils/accessibilityUtils.d.ts +41 -0
  17. package/dist/cjs/components/utils/index.d.ts +2 -0
  18. package/dist/cjs/library.css +13 -0
  19. package/dist/cjs/library.js +2 -2
  20. package/dist/cjs/library.js.map +1 -1
  21. package/dist/esm/components/molecules/Modal/DemoModal.d.ts +8 -0
  22. package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.d.ts +41 -0
  23. package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.types.d.ts +87 -0
  24. package/dist/esm/components/molecules/Modal/ModalContext/index.d.ts +3 -0
  25. package/dist/esm/components/molecules/Modal/ModalContext/useModal.d.ts +34 -0
  26. package/dist/esm/components/molecules/Modal/index.d.ts +2 -0
  27. package/dist/esm/components/molecules/Popover/Popover.presenter.d.ts +26 -0
  28. package/dist/esm/components/molecules/Select/Option/Option.types.d.ts +6 -0
  29. package/dist/esm/components/molecules/Select/Select.Context.d.ts +1 -1
  30. package/dist/esm/components/molecules/Select/Select.d.ts +5 -5
  31. package/dist/esm/components/molecules/Select/Select.presenter.d.ts +1 -0
  32. package/dist/esm/components/molecules/Select/Select.types.d.ts +5 -0
  33. package/dist/esm/components/molecules/Select/index.d.ts +2 -9
  34. package/dist/esm/components/molecules/index.d.ts +2 -0
  35. package/dist/esm/components/utils/accessibilityUtils.d.ts +41 -0
  36. package/dist/esm/components/utils/index.d.ts +2 -0
  37. package/dist/esm/library.css +13 -0
  38. package/dist/esm/library.js +3 -3
  39. package/dist/esm/library.js.map +1 -1
  40. package/dist/index.d.ts +156 -12
  41. package/docs/README.md +264 -0
  42. package/docs/components/atoms/ActionImage.md +119 -0
  43. package/docs/components/atoms/Button.md +197 -0
  44. package/docs/components/atoms/Checkbox.md +299 -0
  45. package/docs/components/atoms/CheckboxItem.md +314 -0
  46. package/docs/components/atoms/Chip.md +380 -0
  47. package/docs/components/atoms/CustomToggle.md +270 -0
  48. package/docs/components/atoms/Icon.md +365 -0
  49. package/docs/components/atoms/IconButton.md +407 -0
  50. package/docs/components/atoms/Image.md +448 -0
  51. package/docs/components/atoms/Input.md +430 -0
  52. package/docs/components/atoms/ListItem.md +502 -0
  53. package/docs/components/atoms/Password.md +472 -0
  54. package/docs/components/atoms/RadioButton.md +614 -0
  55. package/docs/components/atoms/RadioButtonItem.md +588 -0
  56. package/docs/components/atoms/ResponsiveComponent.md +612 -0
  57. package/docs/components/atoms/SelectListItem.md +609 -0
  58. package/docs/components/atoms/Slider.md +605 -0
  59. package/docs/components/atoms/Spinner.md +605 -0
  60. package/docs/components/atoms/Text.md +463 -0
  61. package/docs/components/atoms/TextArea.md +670 -0
  62. package/docs/components/atoms/ToastNotification.md +668 -0
  63. package/docs/components/atoms/Toggle.md +737 -0
  64. package/docs/components/atoms/ToggleButton.md +751 -0
  65. package/docs/components/atoms/Tooltip.md +391 -0
  66. package/docs/components/molecules/Accordion.md +440 -0
  67. package/docs/components/molecules/AccordionGroup.md +547 -0
  68. package/docs/components/molecules/ActionCard.md +546 -0
  69. package/docs/components/molecules/Breadcrumb.md +403 -0
  70. package/docs/components/molecules/Breadcrumbs.md +485 -0
  71. package/docs/components/molecules/ButtonGroup.md +383 -0
  72. package/docs/components/molecules/Card.md +298 -0
  73. package/docs/components/molecules/ChipInput.md +646 -0
  74. package/docs/components/molecules/ContextMenu.md +768 -0
  75. package/docs/components/molecules/CustomTimeSelector.md +116 -0
  76. package/docs/components/molecules/DatePicker.md +516 -0
  77. package/docs/components/molecules/DateTimeSelector.md +166 -0
  78. package/docs/components/molecules/FormField.md +312 -0
  79. package/docs/components/molecules/Grid.md +577 -0
  80. package/docs/components/molecules/GridItem.md +834 -0
  81. package/docs/components/molecules/GridList.md +244 -0
  82. package/docs/components/molecules/List.md +485 -0
  83. package/docs/components/molecules/Modal.md +470 -0
  84. package/docs/components/molecules/ModalFooter.md +702 -0
  85. package/docs/components/molecules/ModalHeader.md +756 -0
  86. package/docs/components/molecules/ModalProvider.md +205 -0
  87. package/docs/components/molecules/Nav.md +530 -0
  88. package/docs/components/molecules/NavItem.md +572 -0
  89. package/docs/components/molecules/NavLink.md +499 -0
  90. package/docs/components/molecules/Option.md +521 -0
  91. package/docs/components/molecules/Pagination.md +592 -0
  92. package/docs/components/molecules/PaginationNumberField.md +722 -0
  93. package/docs/components/molecules/Popover.md +516 -0
  94. package/docs/components/molecules/ProgressBar.md +624 -0
  95. package/docs/components/molecules/RadioGroup.md +831 -0
  96. package/docs/components/molecules/RepeaterList.md +185 -0
  97. package/docs/components/molecules/Select.md +402 -0
  98. package/docs/components/molecules/SortableTrigger.md +82 -0
  99. package/docs/components/molecules/useModal.md +379 -0
  100. package/docs/components/organisms/Dropzone.md +346 -0
  101. package/docs/components/organisms/DropzoneClear.md +135 -0
  102. package/docs/components/organisms/DropzoneContent.md +216 -0
  103. package/docs/components/organisms/DropzoneFilename.md +191 -0
  104. package/docs/components/organisms/DropzoneSupportedFormats.md +184 -0
  105. package/docs/components/organisms/DropzoneTrigger.md +209 -0
  106. package/docs/components/organisms/Form.md +533 -0
  107. package/docs/components/organisms/SlideOutPanel.md +662 -0
  108. package/docs/components/organisms/TabContent.md +902 -0
  109. package/docs/components/organisms/TabItem.md +1091 -0
  110. package/docs/components/organisms/Table.md +611 -0
  111. package/docs/components/organisms/TableBody.md +679 -0
  112. package/docs/components/organisms/TableCell.md +482 -0
  113. package/docs/components/organisms/TableHeader.md +513 -0
  114. package/docs/components/organisms/TableHeaderCell.md +661 -0
  115. package/docs/components/organisms/TableRow.md +715 -0
  116. package/docs/components/organisms/Tabs.md +1330 -0
  117. package/docs/components/utils/ConditionalView.md +568 -0
  118. package/docs/components/utils/RenderStateView.md +726 -0
  119. package/docs/components/utils/WrapTextNodes.md +614 -0
  120. package/package.json +3 -2
@@ -0,0 +1,646 @@
1
+ # ChipInput
2
+
3
+ ## Description
4
+
5
+ An input field component that allows users to enter multiple values as chips or tags. Automatically converts typed input into removable chip elements, providing an intuitive interface for managing lists of items like tags, categories, email addresses, or any collection of discrete values.
6
+
7
+ ## Aliases
8
+
9
+ - ChipInput
10
+ - TagInput
11
+ - TokenInput
12
+ - MultiValueInput
13
+ - ChipField
14
+
15
+ ## Props Breakdown
16
+
17
+ **Extends:** `ControlledFormComponentProps<string[]>`
18
+
19
+ | Prop | Type | Default | Required | Description |
20
+ |------|------|---------|----------|-------------|
21
+ | `value` | `string[]` | - | No | Current array of chip values |
22
+ | `onValueChange` | `(value: string[]) => void` | - | No | Callback fired when chip values change |
23
+ | `inputValue` | `string` | - | No | Current input field value |
24
+ | `onInputChange` | `(value: string) => void` | - | No | Callback fired when input value changes |
25
+ | `options` | `string[]` | - | No | Predefined options for autocomplete |
26
+ | `placeholder` | `string` | - | No | Placeholder text for the input field |
27
+ | `className` | `string` | - | No | Additional CSS class names |
28
+ | `onAddNewOption` | `(value: string) => void` | - | No | Callback fired when a new option is added |
29
+
30
+ ## Examples
31
+
32
+ ### Basic Usage
33
+ ```tsx
34
+ import { ChipInput } from '@delightui/components';
35
+
36
+ function BasicExample() {
37
+ const [tags, setTags] = useState(['react', 'javascript']);
38
+
39
+ return (
40
+ <ChipInput
41
+ value={tags}
42
+ onValueChange={setTags}
43
+ placeholder="Add tags..."
44
+ />
45
+ );
46
+ }
47
+ ```
48
+
49
+ ### Email Recipients
50
+ ```tsx
51
+ function EmailRecipientsExample() {
52
+ const [recipients, setRecipients] = useState(['john@example.com']);
53
+ const [inputValue, setInputValue] = useState('');
54
+
55
+ const validateEmail = (email) => {
56
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
57
+ };
58
+
59
+ const handleAddRecipient = (email) => {
60
+ if (validateEmail(email) && !recipients.includes(email)) {
61
+ setRecipients([...recipients, email]);
62
+ setInputValue('');
63
+ }
64
+ };
65
+
66
+ return (
67
+ <div className="email-recipients">
68
+ <Text type="Heading6">Email Recipients</Text>
69
+ <ChipInput
70
+ value={recipients}
71
+ onValueChange={setRecipients}
72
+ inputValue={inputValue}
73
+ onInputChange={setInputValue}
74
+ placeholder="Enter email addresses..."
75
+ onAddNewOption={handleAddRecipient}
76
+ className="email-chip-input"
77
+ />
78
+ <Text type="BodySmall">
79
+ {recipients.length} recipient{recipients.length !== 1 ? 's' : ''} added
80
+ </Text>
81
+ </div>
82
+ );
83
+ }
84
+ ```
85
+
86
+ ### Skills Tags with Autocomplete
87
+ ```tsx
88
+ function SkillsTagsExample() {
89
+ const [skills, setSkills] = useState(['React', 'TypeScript']);
90
+ const [inputValue, setInputValue] = useState('');
91
+
92
+ const availableSkills = [
93
+ 'JavaScript', 'TypeScript', 'React', 'Vue.js', 'Angular',
94
+ 'Node.js', 'Python', 'Java', 'C++', 'HTML', 'CSS',
95
+ 'GraphQL', 'REST APIs', 'MongoDB', 'PostgreSQL'
96
+ ];
97
+
98
+ const handleAddSkill = (skill) => {
99
+ if (!skills.includes(skill)) {
100
+ setSkills([...skills, skill]);
101
+ }
102
+ setInputValue('');
103
+ };
104
+
105
+ return (
106
+ <div className="skills-section">
107
+ <Text type="Heading6">Technical Skills</Text>
108
+ <ChipInput
109
+ value={skills}
110
+ onValueChange={setSkills}
111
+ inputValue={inputValue}
112
+ onInputChange={setInputValue}
113
+ options={availableSkills}
114
+ placeholder="Add your skills..."
115
+ onAddNewOption={handleAddSkill}
116
+ className="skills-chip-input"
117
+ />
118
+ <Text type="BodySmall">
119
+ {skills.length} skill{skills.length !== 1 ? 's' : ''} selected
120
+ </Text>
121
+ </div>
122
+ );
123
+ }
124
+ ```
125
+
126
+ ### Product Categories
127
+ ```tsx
128
+ function ProductCategoriesExample() {
129
+ const [categories, setCategories] = useState(['Electronics']);
130
+ const [inputValue, setInputValue] = useState('');
131
+
132
+ const predefinedCategories = [
133
+ 'Electronics', 'Clothing', 'Home & Garden', 'Sports',
134
+ 'Books', 'Toys', 'Health', 'Beauty', 'Automotive'
135
+ ];
136
+
137
+ const handleCategoryChange = (newCategories) => {
138
+ // Limit to maximum 5 categories
139
+ if (newCategories.length <= 5) {
140
+ setCategories(newCategories);
141
+ }
142
+ };
143
+
144
+ return (
145
+ <div className="product-categories">
146
+ <div className="category-header">
147
+ <Text type="Heading6">Product Categories</Text>
148
+ <Chip size="Small" style="B">
149
+ {categories.length}/5 selected
150
+ </Chip>
151
+ </div>
152
+
153
+ <ChipInput
154
+ value={categories}
155
+ onValueChange={handleCategoryChange}
156
+ inputValue={inputValue}
157
+ onInputChange={setInputValue}
158
+ options={predefinedCategories}
159
+ placeholder="Select categories..."
160
+ className="category-chip-input"
161
+ />
162
+
163
+ <div className="category-suggestions">
164
+ <Text type="BodySmall">Suggested categories:</Text>
165
+ <div className="suggestion-chips">
166
+ <List
167
+ data={predefinedCategories
168
+ .filter(cat => !categories.includes(cat))
169
+ .slice(0, 3)
170
+ .map(category => ({ category }))}
171
+ component={({ category }) => (
172
+ <Chip
173
+ size="Small"
174
+ style="B"
175
+ onClick={() => handleCategoryChange([...categories, category])}
176
+ >
177
+ {category}
178
+ </Chip>
179
+ )}
180
+ keyExtractor={(item) => item.category}
181
+ />
182
+ </div>
183
+ </div>
184
+ </div>
185
+ );
186
+ }
187
+ ```
188
+
189
+ ### Search Filters
190
+ ```tsx
191
+ function SearchFiltersExample() {
192
+ const [filters, setFilters] = useState([]);
193
+ const [inputValue, setInputValue] = useState('');
194
+
195
+ const availableFilters = [
196
+ 'In Stock', 'On Sale', 'Free Shipping', 'New Arrivals',
197
+ 'Top Rated', 'Premium', 'Eco Friendly', 'Local Seller'
198
+ ];
199
+
200
+ const handleFilterToggle = (filter) => {
201
+ setFilters(prev =>
202
+ prev.includes(filter)
203
+ ? prev.filter(f => f !== filter)
204
+ : [...prev, filter]
205
+ );
206
+ };
207
+
208
+ return (
209
+ <div className="search-filters">
210
+ <div className="filter-header">
211
+ <Text type="Heading6">Search Filters</Text>
212
+ <Button
213
+ size="Small"
214
+ type="Ghost"
215
+ onClick={() => setFilters([])}
216
+ disabled={filters.length === 0}
217
+ >
218
+ Clear All
219
+ </Button>
220
+ </div>
221
+
222
+ <ChipInput
223
+ value={filters}
224
+ onValueChange={setFilters}
225
+ inputValue={inputValue}
226
+ onInputChange={setInputValue}
227
+ options={availableFilters}
228
+ placeholder="Add filters..."
229
+ className="filter-chip-input"
230
+ />
231
+
232
+ <div className="filter-options">
233
+ <Text type="BodySmall">Quick filters:</Text>
234
+ <div className="quick-filters">
235
+ <List
236
+ data={availableFilters.slice(0, 4).map(filter => ({ filter }))}
237
+ component={({ filter }) => (
238
+ <Button
239
+ size="Small"
240
+ type={filters.includes(filter) ? "Filled" : "Outlined"}
241
+ onClick={() => handleFilterToggle(filter)}
242
+ >
243
+ {filter}
244
+ </Button>
245
+ )}
246
+ keyExtractor={(item) => item.filter}
247
+ />
248
+ </div>
249
+ </div>
250
+ </div>
251
+ );
252
+ }
253
+ ```
254
+
255
+ ### Team Members Assignment
256
+ ```tsx
257
+ function TeamAssignmentExample() {
258
+ const [assignees, setAssignees] = useState(['john.doe']);
259
+ const [inputValue, setInputValue] = useState('');
260
+
261
+ const teamMembers = [
262
+ { id: 'john.doe', name: 'John Doe', role: 'Developer' },
263
+ { id: 'jane.smith', name: 'Jane Smith', role: 'Designer' },
264
+ { id: 'mike.johnson', name: 'Mike Johnson', role: 'PM' },
265
+ { id: 'sarah.wilson', name: 'Sarah Wilson', role: 'QA' }
266
+ ];
267
+
268
+ const memberOptions = teamMembers.map(member => member.id);
269
+
270
+ const getMemberName = (id) => {
271
+ const member = teamMembers.find(m => m.id === id);
272
+ return member ? member.name : id;
273
+ };
274
+
275
+ const handleAssigneeChange = (newAssignees) => {
276
+ setAssignees(newAssignees);
277
+ };
278
+
279
+ return (
280
+ <div className="team-assignment">
281
+ <div className="assignment-header">
282
+ <Text type="Heading6">Assign Team Members</Text>
283
+ <Chip size="Small" style="A">
284
+ {assignees.length} assigned
285
+ </Chip>
286
+ </div>
287
+
288
+ <ChipInput
289
+ value={assignees}
290
+ onValueChange={handleAssigneeChange}
291
+ inputValue={inputValue}
292
+ onInputChange={setInputValue}
293
+ options={memberOptions}
294
+ placeholder="Type to search team members..."
295
+ className="assignment-chip-input"
296
+ />
297
+
298
+ <div className="team-list">
299
+ <Text type="BodySmall">Available team members:</Text>
300
+ {teamMembers
301
+ .filter(member => !assignees.includes(member.id))
302
+ .map(member => (
303
+ <div key={member.id} className="member-item">
304
+ <div className="member-info">
305
+ <Text type="BodyMedium">{member.name}</Text>
306
+ <Text type="BodySmall">{member.role}</Text>
307
+ </div>
308
+ <Button
309
+ size="Small"
310
+ type="Outlined"
311
+ onClick={() => setAssignees([...assignees, member.id])}
312
+ >
313
+ Assign
314
+ </Button>
315
+ </div>
316
+ ))}
317
+ </div>
318
+ </div>
319
+ );
320
+ }
321
+ ```
322
+
323
+ ### Custom Validation
324
+ ```tsx
325
+ function CustomValidationExample() {
326
+ const [urls, setUrls] = useState([]);
327
+ const [inputValue, setInputValue] = useState('');
328
+ const [errors, setErrors] = useState([]);
329
+
330
+ const validateURL = (url) => {
331
+ try {
332
+ new URL(url);
333
+ return true;
334
+ } catch {
335
+ return false;
336
+ }
337
+ };
338
+
339
+ const handleAddURL = (url) => {
340
+ if (!validateURL(url)) {
341
+ setErrors([...errors, `Invalid URL: ${url}`]);
342
+ return;
343
+ }
344
+
345
+ if (urls.includes(url)) {
346
+ setErrors([...errors, `URL already exists: ${url}`]);
347
+ return;
348
+ }
349
+
350
+ setUrls([...urls, url]);
351
+ setInputValue('');
352
+ setErrors(errors.filter(error => !error.includes(url)));
353
+ };
354
+
355
+ const handleURLChange = (newUrls) => {
356
+ setUrls(newUrls);
357
+ // Clear related errors when URL is removed
358
+ setErrors(errors.filter(error =>
359
+ newUrls.some(url => error.includes(url))
360
+ ));
361
+ };
362
+
363
+ return (
364
+ <div className="url-validation">
365
+ <Text type="Heading6">Website URLs</Text>
366
+ <ChipInput
367
+ value={urls}
368
+ onValueChange={handleURLChange}
369
+ inputValue={inputValue}
370
+ onInputChange={setInputValue}
371
+ placeholder="Enter website URLs..."
372
+ onAddNewOption={handleAddURL}
373
+ className="url-chip-input"
374
+ />
375
+
376
+ {errors.length > 0 && (
377
+ <div className="error-messages">
378
+ {errors.map((error, index) => (
379
+ <div key={index} className="error-message">
380
+ <Icon icon="Error" size="Small" />
381
+ <Text type="BodySmall">{error}</Text>
382
+ </div>
383
+ ))}
384
+ </div>
385
+ )}
386
+
387
+ <div className="url-stats">
388
+ <Text type="BodySmall">
389
+ {urls.length} valid URL{urls.length !== 1 ? 's' : ''} added
390
+ </Text>
391
+ </div>
392
+ </div>
393
+ );
394
+ }
395
+ ```
396
+
397
+ ### Dynamic Options Loading
398
+ ```tsx
399
+ function DynamicOptionsExample() {
400
+ const [selectedTags, setSelectedTags] = useState([]);
401
+ const [inputValue, setInputValue] = useState('');
402
+ const [options, setOptions] = useState([]);
403
+ const [loading, setLoading] = useState(false);
404
+
405
+ const searchTags = async (query) => {
406
+ if (query.length < 2) return;
407
+
408
+ setLoading(true);
409
+ try {
410
+ // Simulate API call
411
+ await new Promise(resolve => setTimeout(resolve, 500));
412
+ const mockResults = [
413
+ `${query}-tag1`, `${query}-tag2`, `${query}-tag3`,
414
+ `popular-${query}`, `trending-${query}`
415
+ ];
416
+ setOptions(mockResults);
417
+ } finally {
418
+ setLoading(false);
419
+ }
420
+ };
421
+
422
+ useEffect(() => {
423
+ searchTags(inputValue);
424
+ }, [inputValue]);
425
+
426
+ return (
427
+ <div className="dynamic-options">
428
+ <div className="tags-header">
429
+ <Text type="Heading6">Dynamic Tag Search</Text>
430
+ {loading && <Spinner size="Small" />}
431
+ </div>
432
+
433
+ <ChipInput
434
+ value={selectedTags}
435
+ onValueChange={setSelectedTags}
436
+ inputValue={inputValue}
437
+ onInputChange={setInputValue}
438
+ options={options}
439
+ placeholder="Type to search tags..."
440
+ className="dynamic-chip-input"
441
+ />
442
+
443
+ {options.length > 0 && (
444
+ <div className="suggested-tags">
445
+ <Text type="BodySmall">Suggested tags:</Text>
446
+ <div className="tag-suggestions">
447
+ {options.slice(0, 5).map(tag => (
448
+ <Chip
449
+ key={tag}
450
+ size="Small"
451
+ style="B"
452
+ onClick={() => {
453
+ if (!selectedTags.includes(tag)) {
454
+ setSelectedTags([...selectedTags, tag]);
455
+ setInputValue('');
456
+ }
457
+ }}
458
+ >
459
+ {tag}
460
+ </Chip>
461
+ ))}
462
+ </div>
463
+ </div>
464
+ )}
465
+ </div>
466
+ );
467
+ }
468
+ ```
469
+
470
+ ### Form Integration
471
+ ```tsx
472
+ function FormIntegrationExample() {
473
+ const [formData, setFormData] = useState({
474
+ title: '',
475
+ description: '',
476
+ tags: ['frontend'],
477
+ skills: [],
478
+ collaborators: []
479
+ });
480
+
481
+ const handleSubmit = (e) => {
482
+ e.preventDefault();
483
+ console.log('Form submitted:', formData);
484
+ };
485
+
486
+ const updateFormField = (field, value) => {
487
+ setFormData(prev => ({
488
+ ...prev,
489
+ [field]: value
490
+ }));
491
+ };
492
+
493
+ return (
494
+ <Form onSubmit={handleSubmit} className="chip-input-form">
495
+ <FormField name="title" label="Project Title" required>
496
+ <Input
497
+ value={formData.title}
498
+ onValueChange={(value) => updateFormField('title', value)}
499
+ placeholder="Enter project title"
500
+ />
501
+ </FormField>
502
+
503
+ <FormField name="description" label="Description" required>
504
+ <TextArea
505
+ value={formData.description}
506
+ onValueChange={(value) => updateFormField('description', value)}
507
+ placeholder="Describe your project"
508
+ rows={3}
509
+ />
510
+ </FormField>
511
+
512
+ <FormField name="tags" label="Tags">
513
+ <ChipInput
514
+ value={formData.tags}
515
+ onValueChange={(value) => updateFormField('tags', value)}
516
+ placeholder="Add relevant tags..."
517
+ options={['frontend', 'backend', 'fullstack', 'mobile', 'design']}
518
+ />
519
+ </FormField>
520
+
521
+ <FormField name="skills" label="Required Skills">
522
+ <ChipInput
523
+ value={formData.skills}
524
+ onValueChange={(value) => updateFormField('skills', value)}
525
+ placeholder="What skills are needed?"
526
+ options={['React', 'Node.js', 'Python', 'Design', 'DevOps']}
527
+ />
528
+ </FormField>
529
+
530
+ <FormField name="collaborators" label="Collaborators">
531
+ <ChipInput
532
+ value={formData.collaborators}
533
+ onValueChange={(value) => updateFormField('collaborators', value)}
534
+ placeholder="Add collaborator emails..."
535
+ />
536
+ </FormField>
537
+
538
+ <ButtonGroup>
539
+ <Button type="Filled" actionType="submit">
540
+ Create Project
541
+ </Button>
542
+ <Button type="Outlined" actionType="reset">
543
+ Clear Form
544
+ </Button>
545
+ </ButtonGroup>
546
+ </Form>
547
+ );
548
+ }
549
+ ```
550
+
551
+ ### Advanced Filtering Interface
552
+ ```tsx
553
+ function AdvancedFilteringExample() {
554
+ const [filters, setFilters] = useState({
555
+ categories: [],
556
+ brands: [],
557
+ features: [],
558
+ priceRanges: []
559
+ });
560
+
561
+ const filterOptions = {
562
+ categories: ['Electronics', 'Clothing', 'Home', 'Sports'],
563
+ brands: ['Apple', 'Samsung', 'Nike', 'Adidas'],
564
+ features: ['Wireless', 'Waterproof', 'Bluetooth', 'Fast Charging'],
565
+ priceRanges: ['Under $50', '$50-$100', '$100-$200', 'Over $200']
566
+ };
567
+
568
+ const updateFilter = (filterType, values) => {
569
+ setFilters(prev => ({
570
+ ...prev,
571
+ [filterType]: values
572
+ }));
573
+ };
574
+
575
+ const clearAllFilters = () => {
576
+ setFilters({
577
+ categories: [],
578
+ brands: [],
579
+ features: [],
580
+ priceRanges: []
581
+ });
582
+ };
583
+
584
+ const totalFilters = Object.values(filters).flat().length;
585
+
586
+ return (
587
+ <div className="advanced-filtering">
588
+ <div className="filter-header">
589
+ <Text type="Heading5">Product Filters</Text>
590
+ <div className="filter-actions">
591
+ <Chip size="Small" style="A">
592
+ {totalFilters} filter{totalFilters !== 1 ? 's' : ''} active
593
+ </Chip>
594
+ <Button
595
+ size="Small"
596
+ type="Ghost"
597
+ onClick={clearAllFilters}
598
+ disabled={totalFilters === 0}
599
+ >
600
+ Clear All
601
+ </Button>
602
+ </div>
603
+ </div>
604
+
605
+ <div className="filter-sections">
606
+ {Object.entries(filterOptions).map(([filterType, options]) => (
607
+ <div key={filterType} className="filter-section">
608
+ <Text type="Heading6">
609
+ {filterType.charAt(0).toUpperCase() + filterType.slice(1)}
610
+ </Text>
611
+ <ChipInput
612
+ value={filters[filterType]}
613
+ onValueChange={(values) => updateFilter(filterType, values)}
614
+ options={options}
615
+ placeholder={`Select ${filterType}...`}
616
+ className="filter-chip-input"
617
+ />
618
+ </div>
619
+ ))}
620
+ </div>
621
+
622
+ <div className="filter-summary">
623
+ <Text type="BodyMedium">Active Filters:</Text>
624
+ {totalFilters > 0 ? (
625
+ <div className="active-filters">
626
+ {Object.entries(filters).map(([type, values]) =>
627
+ values.map(value => (
628
+ <Chip
629
+ key={`${type}-${value}`}
630
+ size="Small"
631
+ style="A"
632
+ onRemove={() => updateFilter(type, values.filter(v => v !== value))}
633
+ >
634
+ {value}
635
+ </Chip>
636
+ ))
637
+ )}
638
+ </div>
639
+ ) : (
640
+ <Text type="BodySmall">No filters applied</Text>
641
+ )}
642
+ </div>
643
+ </div>
644
+ );
645
+ }
646
+ ```