@ceed/cds 1.24.1-next.3 → 1.26.0
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/dist/chunks/rehype-accent-FZRUD7VI.js +39 -0
- package/dist/components/CurrencyInput/CurrencyInput.d.ts +1 -1
- package/dist/components/CurrencyInput/hooks/use-currency-setting.d.ts +2 -2
- package/dist/components/DataTable/components.d.ts +2 -1
- package/dist/components/DataTable/hooks.d.ts +1 -1
- package/dist/components/DataTable/styled.d.ts +3 -1
- package/dist/components/DataTable/types.d.ts +11 -0
- package/dist/components/DataTable/utils.d.ts +2 -2
- package/dist/components/RadioTileGroup/RadioTileGroup.d.ts +56 -0
- package/dist/components/RadioTileGroup/index.d.ts +3 -0
- package/dist/components/data-display/DataTable.md +177 -1
- package/dist/components/data-display/InfoSign.md +74 -91
- package/dist/components/data-display/Typography.md +411 -94
- package/dist/components/feedback/CircularProgress.md +257 -0
- package/dist/components/feedback/Dialog.md +76 -62
- package/dist/components/feedback/Modal.md +430 -138
- package/dist/components/feedback/Skeleton.md +280 -0
- package/dist/components/feedback/llms.txt +2 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/inputs/Autocomplete.md +356 -107
- package/dist/components/inputs/ButtonGroup.md +115 -104
- package/dist/components/inputs/CurrencyInput.md +183 -5
- package/dist/components/inputs/DatePicker.md +108 -431
- package/dist/components/inputs/DateRangePicker.md +131 -492
- package/dist/components/inputs/FilterableCheckboxGroup.md +145 -19
- package/dist/components/inputs/FormControl.md +361 -0
- package/dist/components/inputs/IconButton.md +137 -88
- package/dist/components/inputs/Input.md +204 -73
- package/dist/components/inputs/MonthPicker.md +95 -422
- package/dist/components/inputs/MonthRangePicker.md +89 -466
- package/dist/components/inputs/PercentageInput.md +185 -16
- package/dist/components/inputs/RadioButton.md +163 -35
- package/dist/components/inputs/RadioList.md +241 -0
- package/dist/components/inputs/RadioTileGroup.md +507 -0
- package/dist/components/inputs/Select.md +222 -326
- package/dist/components/inputs/Slider.md +334 -0
- package/dist/components/inputs/Switch.md +143 -376
- package/dist/components/inputs/Textarea.md +213 -10
- package/dist/components/inputs/Uploader/Uploader.md +145 -66
- package/dist/components/inputs/llms.txt +4 -0
- package/dist/components/navigation/Breadcrumbs.md +57 -308
- package/dist/components/navigation/Drawer.md +180 -0
- package/dist/components/navigation/Dropdown.md +98 -215
- package/dist/components/navigation/IconMenuButton.md +40 -502
- package/dist/components/navigation/InsetDrawer.md +281 -650
- package/dist/components/navigation/Link.md +31 -348
- package/dist/components/navigation/Menu.md +92 -285
- package/dist/components/navigation/MenuButton.md +55 -448
- package/dist/components/navigation/Pagination.md +47 -338
- package/dist/components/navigation/Stepper.md +160 -28
- package/dist/components/navigation/Tabs.md +57 -316
- package/dist/components/surfaces/Accordions.md +49 -804
- package/dist/components/surfaces/Card.md +97 -157
- package/dist/components/surfaces/Divider.md +83 -234
- package/dist/components/surfaces/Sheet.md +153 -328
- package/dist/guides/ThemeProvider.md +89 -0
- package/dist/guides/llms.txt +9 -0
- package/dist/index.browser.js +224 -0
- package/dist/index.browser.js.map +7 -0
- package/dist/index.cjs +726 -425
- package/dist/index.d.ts +1 -1
- package/dist/index.js +641 -396
- package/dist/llms.txt +9 -0
- package/framer/index.js +1 -163
- package/package.json +22 -17
|
@@ -8,10 +8,36 @@ Autocomplete is an enhanced input component that provides real-time suggestions
|
|
|
8
8
|
<Autocomplete options={['Option1', 'Option2']} />
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
| Field
|
|
12
|
-
|
|
|
13
|
-
|
|
|
14
|
-
|
|
|
11
|
+
| Field | Description | Default |
|
|
12
|
+
| --------------------- | ----------- | ------- |
|
|
13
|
+
| variant | — | — |
|
|
14
|
+
| color | — | — |
|
|
15
|
+
| size | — | — |
|
|
16
|
+
| placeholder | — | — |
|
|
17
|
+
| label | — | — |
|
|
18
|
+
| helperText | — | — |
|
|
19
|
+
| error | — | — |
|
|
20
|
+
| required | — | — |
|
|
21
|
+
| disabled | — | — |
|
|
22
|
+
| readOnly | — | — |
|
|
23
|
+
| multiple | — | — |
|
|
24
|
+
| freeSolo | — | — |
|
|
25
|
+
| disableClearable | — | — |
|
|
26
|
+
| autoHighlight | — | — |
|
|
27
|
+
| clearOnEscape | — | — |
|
|
28
|
+
| disableCloseOnSelect | — | — |
|
|
29
|
+
| openOnFocus | — | — |
|
|
30
|
+
| blurOnSelect | — | — |
|
|
31
|
+
| filterSelectedOptions | — | — |
|
|
32
|
+
| selectOnFocus | — | — |
|
|
33
|
+
| clearOnBlur | — | — |
|
|
34
|
+
| forcePopupIcon | — | — |
|
|
35
|
+
| loading | — | — |
|
|
36
|
+
| noOptionsText | — | — |
|
|
37
|
+
| loadingText | — | — |
|
|
38
|
+
| limitTags | — | — |
|
|
39
|
+
| value | — | — |
|
|
40
|
+
| defaultValue | — | — |
|
|
15
41
|
|
|
16
42
|
> ⚠️ **Usage Warning** ⚠️
|
|
17
43
|
>
|
|
@@ -22,6 +48,11 @@ Autocomplete is an enhanced input component that provides real-time suggestions
|
|
|
22
48
|
> - **Dropdown**: For action menus, not form value selection
|
|
23
49
|
> - **Input**: For free-text entry without predefined options
|
|
24
50
|
|
|
51
|
+
> 💡 **Use built-in form props**
|
|
52
|
+
>
|
|
53
|
+
> This component natively supports form elements such as `label` and `helperText` props.
|
|
54
|
+
> When building forms, use these built-in props instead of manually composing labels and helper text with Typography.
|
|
55
|
+
|
|
25
56
|
## Usage
|
|
26
57
|
|
|
27
58
|
```tsx
|
|
@@ -42,17 +73,19 @@ function CountrySelector() {
|
|
|
42
73
|
}
|
|
43
74
|
```
|
|
44
75
|
|
|
45
|
-
##
|
|
46
|
-
|
|
47
|
-
### Playground
|
|
76
|
+
## Variants
|
|
48
77
|
|
|
49
|
-
|
|
78
|
+
The `variant` prop controls the visual style of the autocomplete input. Available variants: `outlined` (default), `soft`, `solid`, `plain`.
|
|
50
79
|
|
|
51
80
|
```tsx
|
|
52
|
-
<
|
|
81
|
+
<Stack gap={2} p={2}>
|
|
82
|
+
{variants.map(variant => <Autocomplete key={variant} variant={variant} options={sampleOptions} placeholder={variant} label={variant} sx={{
|
|
83
|
+
maxWidth: 300
|
|
84
|
+
}} />)}
|
|
85
|
+
</Stack>
|
|
53
86
|
```
|
|
54
87
|
|
|
55
|
-
|
|
88
|
+
## Sizes
|
|
56
89
|
|
|
57
90
|
Available size options: `sm`, `md`, `lg`.
|
|
58
91
|
|
|
@@ -67,7 +100,139 @@ Available size options: `sm`, `md`, `lg`.
|
|
|
67
100
|
</div>
|
|
68
101
|
```
|
|
69
102
|
|
|
70
|
-
|
|
103
|
+
## Colors
|
|
104
|
+
|
|
105
|
+
The `color` prop changes the color scheme. Available colors: `primary`, `neutral`, `danger`, `success`, `warning`.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
<Stack gap={2} p={2}>
|
|
109
|
+
{colors.map(color => <Autocomplete key={color} color={color} options={sampleOptions} defaultValue="Apple" label={color} sx={{
|
|
110
|
+
maxWidth: 300
|
|
111
|
+
}} />)}
|
|
112
|
+
</Stack>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## States
|
|
116
|
+
|
|
117
|
+
Autocomplete supports several form states for different interaction contexts.
|
|
118
|
+
|
|
119
|
+
### Disabled
|
|
120
|
+
|
|
121
|
+
A disabled autocomplete cannot be focused or interacted with. Use for fields that are temporarily unavailable.
|
|
122
|
+
|
|
123
|
+
### Read Only
|
|
124
|
+
|
|
125
|
+
A read-only autocomplete can be focused and its value copied, but cannot be changed. Use when a value should be visible but not editable.
|
|
126
|
+
|
|
127
|
+
### Error
|
|
128
|
+
|
|
129
|
+
Set `error` along with `helperText` to indicate validation failures.
|
|
130
|
+
|
|
131
|
+
### Required
|
|
132
|
+
|
|
133
|
+
Set `required` to mark the field as required in a form.
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
<Stack gap={3} p={2} sx={{
|
|
137
|
+
maxWidth: 300
|
|
138
|
+
}}>
|
|
139
|
+
<Autocomplete label="Disabled" options={sampleOptions} defaultValue="Apple" disabled />
|
|
140
|
+
<Autocomplete label="Read Only" options={sampleOptions} defaultValue="Banana" readOnly />
|
|
141
|
+
<Autocomplete label="Error" options={sampleOptions} error helperText="This field is required" />
|
|
142
|
+
<Autocomplete label="Required" options={sampleOptions} required placeholder="Select a fruit..." />
|
|
143
|
+
</Stack>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Free Solo
|
|
147
|
+
|
|
148
|
+
The `freeSolo` prop allows users to type arbitrary values that are not in the options list. This is useful for inputs where the user can either pick a suggestion or enter a custom value.
|
|
149
|
+
|
|
150
|
+
> ⚠️ **Known limitation**: The component's internal `handleChange` accesses `newValue.value`, which is `undefined` for raw strings in freeSolo mode. Typed arbitrary values may not propagate correctly through `onChange`. Consider using `onInputChange` as an alternative for capturing free-text input.
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
<Stack gap={2} p={2} sx={{
|
|
154
|
+
maxWidth: 300
|
|
155
|
+
}}>
|
|
156
|
+
<Autocomplete label="Free Solo" options={sampleOptions} freeSolo placeholder="Type anything..." value={value} onChange={e => setValue(e.target.value)} />
|
|
157
|
+
<Typography level="body-sm" textColor="text.tertiary">
|
|
158
|
+
Current value: {value ?? '(empty)'}
|
|
159
|
+
</Typography>
|
|
160
|
+
<Typography level="body-xs" textColor="text.tertiary">
|
|
161
|
+
Note: freeSolo allows arbitrary text input. However, the component's onChange handler accesses
|
|
162
|
+
newValue.value which is undefined for raw strings, so typed values may not propagate correctly.
|
|
163
|
+
</Typography>
|
|
164
|
+
</Stack>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Disable Clearable
|
|
168
|
+
|
|
169
|
+
Set `disableClearable` to hide the clear (X) button. This forces the user to always have a selection.
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
<Autocomplete
|
|
173
|
+
options={sampleOptions}
|
|
174
|
+
defaultValue="Cherry"
|
|
175
|
+
disableClearable
|
|
176
|
+
label="Disable Clearable"
|
|
177
|
+
sx={{
|
|
178
|
+
maxWidth: 300
|
|
179
|
+
}}
|
|
180
|
+
/>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Custom Texts
|
|
184
|
+
|
|
185
|
+
Customize the messages shown when there are no matching options or while loading.
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
<Stack gap={3} direction="row" p={2}>
|
|
189
|
+
<Autocomplete label="Custom No Options Text" options={[]} noOptionsText="No fruits found — try a different search" placeholder="Search fruits..." sx={{
|
|
190
|
+
minWidth: 280
|
|
191
|
+
}} />
|
|
192
|
+
<Autocomplete label="Custom Loading Text" options={[]} loading loadingText="Fetching fruits from the orchard..." placeholder="Loading fruits..." sx={{
|
|
193
|
+
minWidth: 280
|
|
194
|
+
}} />
|
|
195
|
+
</Stack>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Multiple Selection
|
|
199
|
+
|
|
200
|
+
Enable `multiple` to allow selecting more than one option. Selected values are shown as chips.
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
<div style={{
|
|
204
|
+
display: 'flex',
|
|
205
|
+
flexDirection: 'column',
|
|
206
|
+
gap: '10px'
|
|
207
|
+
}}>
|
|
208
|
+
<Autocomplete {...args} size="sm" />
|
|
209
|
+
<Autocomplete {...args} size="md" />
|
|
210
|
+
<Autocomplete {...args} size="lg" />
|
|
211
|
+
</div>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Limit Tags
|
|
215
|
+
|
|
216
|
+
In multiple mode, use `limitTags` to control how many tags are visible before truncating. Use `filterSelectedOptions` to hide already-selected options from the dropdown.
|
|
217
|
+
|
|
218
|
+
> 💡 The component uses a custom `renderTags` implementation. JoyUI's `limitTags` truncates the array passed to `renderTags`, so tags will be limited, but the default "+N more" indicator text may not appear.
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
<Stack gap={3} p={2} sx={{
|
|
222
|
+
maxWidth: 400
|
|
223
|
+
}}>
|
|
224
|
+
<Stack gap={1}>
|
|
225
|
+
<Typography level="title-sm">limitTags=2</Typography>
|
|
226
|
+
<Autocomplete multiple options={sampleOptions} defaultValue={['Apple', 'Banana', 'Cherry', 'Date']} limitTags={2} label="Limit Tags" />
|
|
227
|
+
</Stack>
|
|
228
|
+
<Stack gap={1}>
|
|
229
|
+
<Typography level="title-sm">filterSelectedOptions</Typography>
|
|
230
|
+
<Autocomplete multiple options={sampleOptions} defaultValue={['Apple', 'Banana']} filterSelectedOptions label="Filter Selected Options" />
|
|
231
|
+
</Stack>
|
|
232
|
+
</Stack>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Option Groups
|
|
71
236
|
|
|
72
237
|
Group options into categories using the `groupBy` function.
|
|
73
238
|
|
|
@@ -95,7 +260,7 @@ Group options into categories using the `groupBy` function.
|
|
|
95
260
|
/>
|
|
96
261
|
```
|
|
97
262
|
|
|
98
|
-
|
|
263
|
+
## Custom Options
|
|
99
264
|
|
|
100
265
|
Options can include decorators for rich content display.
|
|
101
266
|
|
|
@@ -116,7 +281,28 @@ Options can include decorators for rich content display.
|
|
|
116
281
|
/>
|
|
117
282
|
```
|
|
118
283
|
|
|
119
|
-
|
|
284
|
+
## Secondary Text
|
|
285
|
+
|
|
286
|
+
Options can display a secondary line of text using the `secondaryText` property. This is useful for showing additional context like emails, phone numbers, or descriptions alongside the main label.
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
<Stack gap={4} direction="row" alignItems="flex-start" p={2}>
|
|
290
|
+
{sizes.map(size => <Stack key={size} gap={1}>
|
|
291
|
+
<span style={{
|
|
292
|
+
color: '#6366f1',
|
|
293
|
+
fontSize: 12
|
|
294
|
+
}}>{size}</span>
|
|
295
|
+
<Autocomplete placeholder="Placeholder" options={optionsWithSecondaryText} value={values[size]} onChange={e => setValues(prev => ({
|
|
296
|
+
...prev,
|
|
297
|
+
[size]: e.target.value
|
|
298
|
+
}))} sx={{
|
|
299
|
+
minWidth: 200
|
|
300
|
+
}} size={size} />
|
|
301
|
+
</Stack>)}
|
|
302
|
+
</Stack>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Loading State
|
|
120
306
|
|
|
121
307
|
Show a loading indicator while fetching options.
|
|
122
308
|
|
|
@@ -127,6 +313,8 @@ Show a loading indicator while fetching options.
|
|
|
127
313
|
/>
|
|
128
314
|
```
|
|
129
315
|
|
|
316
|
+
## Controlled / Uncontrolled
|
|
317
|
+
|
|
130
318
|
### Controlled
|
|
131
319
|
|
|
132
320
|
Manage value externally with controlled state.
|
|
@@ -153,6 +341,67 @@ Manage value externally with controlled state.
|
|
|
153
341
|
</Stack>
|
|
154
342
|
```
|
|
155
343
|
|
|
344
|
+
### Uncontrolled
|
|
345
|
+
|
|
346
|
+
Let the component manage its own state with `defaultValue`.
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
<Autocomplete
|
|
350
|
+
options={['Uncontrolled', 'Controlled']}
|
|
351
|
+
label="Component Type"
|
|
352
|
+
defaultValue="Controlled"
|
|
353
|
+
/>
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## Virtualization
|
|
357
|
+
|
|
358
|
+
Autocomplete automatically virtualizes long option lists for performance. This example renders 1,000 options efficiently.
|
|
359
|
+
|
|
360
|
+
```tsx
|
|
361
|
+
<Autocomplete
|
|
362
|
+
options={(() => {
|
|
363
|
+
const res: any[] = [];
|
|
364
|
+
for (let i = 0; i < 1000; i++) {
|
|
365
|
+
res.push(i);
|
|
366
|
+
}
|
|
367
|
+
return res;
|
|
368
|
+
})()}
|
|
369
|
+
/>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Behavior Props
|
|
373
|
+
|
|
374
|
+
These boolean props fine-tune interaction behavior. Toggle them in the Controls panel below to see their effects.
|
|
375
|
+
|
|
376
|
+
| Prop | Default | Effect |
|
|
377
|
+
| ---------------------- | -------- | -------------------------------------------------------------- |
|
|
378
|
+
| `autoHighlight` | `false` | Automatically highlights the first option when the popup opens |
|
|
379
|
+
| `clearOnEscape` | `false` | Clears the input value when the Escape key is pressed |
|
|
380
|
+
| `disableCloseOnSelect` | `false` | Keeps the popup open after selecting an option |
|
|
381
|
+
| `openOnFocus` | `false` | Opens the popup when the input receives focus |
|
|
382
|
+
| `blurOnSelect` | `false` | Blurs the input after an option is selected |
|
|
383
|
+
| `selectOnFocus` | `false` | Selects all input text when the input is focused |
|
|
384
|
+
| `clearOnBlur` | `true` | Clears the input text on blur if it doesn't match any option |
|
|
385
|
+
| `forcePopupIcon` | `'auto'` | Controls whether the popup icon is always shown |
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
<Autocomplete
|
|
389
|
+
options={sampleOptions}
|
|
390
|
+
label="Behavior Props"
|
|
391
|
+
placeholder="Toggle props in the Controls panel..."
|
|
392
|
+
autoHighlight={false}
|
|
393
|
+
clearOnEscape={false}
|
|
394
|
+
disableCloseOnSelect={false}
|
|
395
|
+
openOnFocus={false}
|
|
396
|
+
blurOnSelect={false}
|
|
397
|
+
selectOnFocus={false}
|
|
398
|
+
clearOnBlur
|
|
399
|
+
sx={{
|
|
400
|
+
maxWidth: 400
|
|
401
|
+
}}
|
|
402
|
+
/>
|
|
403
|
+
```
|
|
404
|
+
|
|
156
405
|
## When to Use
|
|
157
406
|
|
|
158
407
|
### ✅ Good Use Cases
|
|
@@ -227,7 +476,7 @@ function UserSearch({ onSelect }) {
|
|
|
227
476
|
label: user.name,
|
|
228
477
|
secondaryText: user.email,
|
|
229
478
|
startDecorator: <Avatar src={user.avatar} size="sm" />,
|
|
230
|
-
}))
|
|
479
|
+
})),
|
|
231
480
|
);
|
|
232
481
|
} finally {
|
|
233
482
|
setLoading(false);
|
|
@@ -304,7 +553,7 @@ function AddressAutocomplete({ onAddressSelect }) {
|
|
|
304
553
|
value: result.placeId,
|
|
305
554
|
label: result.formattedAddress,
|
|
306
555
|
secondaryText: result.city + ', ' + result.country,
|
|
307
|
-
}))
|
|
556
|
+
})),
|
|
308
557
|
);
|
|
309
558
|
} finally {
|
|
310
559
|
setLoading(false);
|
|
@@ -396,13 +645,7 @@ function ContactSelector() {
|
|
|
396
645
|
{ value: 'michael', label: 'Michael Chen', secondaryText: '(646) 555-0876' },
|
|
397
646
|
];
|
|
398
647
|
|
|
399
|
-
return
|
|
400
|
-
<Autocomplete
|
|
401
|
-
label="Contact"
|
|
402
|
-
placeholder="Search contacts..."
|
|
403
|
-
options={contacts}
|
|
404
|
-
/>
|
|
405
|
-
);
|
|
648
|
+
return <Autocomplete label="Contact" placeholder="Search contacts..." options={contacts} />;
|
|
406
649
|
}
|
|
407
650
|
```
|
|
408
651
|
|
|
@@ -443,30 +686,46 @@ function LazyAutocomplete({ fetchOptions }) {
|
|
|
443
686
|
|
|
444
687
|
### Key Props
|
|
445
688
|
|
|
446
|
-
| Prop
|
|
447
|
-
|
|
|
448
|
-
| `options`
|
|
449
|
-
| `value`
|
|
450
|
-
| `defaultValue`
|
|
451
|
-
| `onChange`
|
|
452
|
-
| `onInputChange`
|
|
453
|
-
| `label`
|
|
454
|
-
| `placeholder`
|
|
455
|
-
| `loading`
|
|
456
|
-
| `multiple`
|
|
457
|
-
| `groupBy`
|
|
458
|
-
| `size`
|
|
459
|
-
| `
|
|
689
|
+
| Prop | Type | Default | Description |
|
|
690
|
+
| ----------------------- | -------------------------------------------------------------- | -------------- | ----------------------------------------------------- |
|
|
691
|
+
| `options` | `string[] \| OptionObject[]` | `[]` | Array of options (strings or objects) |
|
|
692
|
+
| `value` | `string \| string[]` | - | Selected value(s) for controlled mode |
|
|
693
|
+
| `defaultValue` | `string \| string[]` | - | Initial value for uncontrolled mode |
|
|
694
|
+
| `onChange` | `(event: { target: { value } }) => void` | - | Callback when selection changes |
|
|
695
|
+
| `onInputChange` | `(event: { target: { value } }) => void` | - | Callback when input text changes |
|
|
696
|
+
| `label` | `ReactNode` | - | Label text above the input |
|
|
697
|
+
| `placeholder` | `string` | - | Placeholder text when empty |
|
|
698
|
+
| `loading` | `boolean` | `false` | Show loading indicator |
|
|
699
|
+
| `multiple` | `boolean` | `false` | Allow multiple selections |
|
|
700
|
+
| `groupBy` | `(option) => string` | - | Function to group options |
|
|
701
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Input size |
|
|
702
|
+
| `variant` | `'outlined' \| 'soft' \| 'solid' \| 'plain'` | `'outlined'` | Visual style variant |
|
|
703
|
+
| `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | `'neutral'` | Color scheme |
|
|
704
|
+
| `disabled` | `boolean` | `false` | Disable the input |
|
|
705
|
+
| `readOnly` | `boolean` | `false` | Make the input read-only (focusable but not editable) |
|
|
706
|
+
| `required` | `boolean` | `false` | Mark the field as required |
|
|
707
|
+
| `error` | `boolean` | `false` | Indicate an error state |
|
|
708
|
+
| `helperText` | `ReactNode` | - | Helper text below the input |
|
|
709
|
+
| `freeSolo` | `boolean` | `false` | Allow arbitrary values not in the options list |
|
|
710
|
+
| `disableClearable` | `boolean` | `false` | Hide the clear (X) button |
|
|
711
|
+
| `noOptionsText` | `ReactNode` | `'No options'` | Text when no options match |
|
|
712
|
+
| `loadingText` | `ReactNode` | `'Loading…'` | Text while loading |
|
|
713
|
+
| `limitTags` | `number` | `-1` | Max visible tags in multiple mode (-1 for unlimited) |
|
|
714
|
+
| `autoHighlight` | `boolean` | `false` | Auto-highlight first option |
|
|
715
|
+
| `clearOnEscape` | `boolean` | `false` | Clear value on Escape key |
|
|
716
|
+
| `disableCloseOnSelect` | `boolean` | `false` | Keep popup open after selection |
|
|
717
|
+
| `openOnFocus` | `boolean` | `false` | Open popup on focus |
|
|
718
|
+
| `filterSelectedOptions` | `boolean` | `false` | Hide selected options from dropdown (multiple mode) |
|
|
460
719
|
|
|
461
720
|
### Option Object Structure
|
|
462
721
|
|
|
463
722
|
```tsx
|
|
464
723
|
interface OptionObject {
|
|
465
|
-
value: string;
|
|
466
|
-
label: string;
|
|
467
|
-
secondaryText?: string;
|
|
724
|
+
value: string; // Unique value identifier
|
|
725
|
+
label: string; // Display text
|
|
726
|
+
secondaryText?: string; // Secondary line of text
|
|
468
727
|
startDecorator?: ReactNode; // Content before label
|
|
469
|
-
endDecorator?: ReactNode;
|
|
728
|
+
endDecorator?: ReactNode; // Content after label
|
|
470
729
|
}
|
|
471
730
|
|
|
472
731
|
// Example option objects
|
|
@@ -491,10 +750,7 @@ const options = [
|
|
|
491
750
|
|
|
492
751
|
```tsx
|
|
493
752
|
// Simplest usage with string array
|
|
494
|
-
<Autocomplete
|
|
495
|
-
label="Fruit"
|
|
496
|
-
options={['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry']}
|
|
497
|
-
/>
|
|
753
|
+
<Autocomplete label="Fruit" options={['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry']} />
|
|
498
754
|
```
|
|
499
755
|
|
|
500
756
|
### Controlled vs Uncontrolled
|
|
@@ -504,23 +760,12 @@ const options = [
|
|
|
504
760
|
function ControlledExample() {
|
|
505
761
|
const [value, setValue] = useState<string | undefined>();
|
|
506
762
|
|
|
507
|
-
return (
|
|
508
|
-
<Autocomplete
|
|
509
|
-
value={value}
|
|
510
|
-
onChange={(e) => setValue(e.target.value)}
|
|
511
|
-
options={options}
|
|
512
|
-
/>
|
|
513
|
-
);
|
|
763
|
+
return <Autocomplete value={value} onChange={(e) => setValue(e.target.value)} options={options} />;
|
|
514
764
|
}
|
|
515
765
|
|
|
516
766
|
// Uncontrolled - internal state management
|
|
517
767
|
function UncontrolledExample() {
|
|
518
|
-
return
|
|
519
|
-
<Autocomplete
|
|
520
|
-
defaultValue="option-1"
|
|
521
|
-
options={options}
|
|
522
|
-
/>
|
|
523
|
-
);
|
|
768
|
+
return <Autocomplete defaultValue="option-1" options={options} />;
|
|
524
769
|
}
|
|
525
770
|
```
|
|
526
771
|
|
|
@@ -554,12 +799,7 @@ function UncontrolledExample() {
|
|
|
554
799
|
### Multiple Selection
|
|
555
800
|
|
|
556
801
|
```tsx
|
|
557
|
-
<Autocomplete
|
|
558
|
-
multiple
|
|
559
|
-
value={['option-1', 'option-2']}
|
|
560
|
-
options={options}
|
|
561
|
-
onChange={(e) => setValues(e.target.value)}
|
|
562
|
-
/>
|
|
802
|
+
<Autocomplete multiple value={['option-1', 'option-2']} options={options} onChange={(e) => setValues(e.target.value)} />
|
|
563
803
|
```
|
|
564
804
|
|
|
565
805
|
## Accessibility
|
|
@@ -578,7 +818,7 @@ Autocomplete includes comprehensive accessibility features:
|
|
|
578
818
|
- **Arrow Down**: Open dropdown / move to next option
|
|
579
819
|
- **Arrow Up**: Move to previous option
|
|
580
820
|
- **Enter**: Select focused option
|
|
581
|
-
- **Escape**: Close dropdown
|
|
821
|
+
- **Escape**: Close dropdown (clears value if `clearOnEscape` is enabled)
|
|
582
822
|
- **Tab**: Move focus out of component
|
|
583
823
|
- **Home**: Jump to first option
|
|
584
824
|
- **End**: Jump to last option
|
|
@@ -589,7 +829,7 @@ Autocomplete includes comprehensive accessibility features:
|
|
|
589
829
|
```tsx
|
|
590
830
|
// Proper labeling for screen readers
|
|
591
831
|
<Autocomplete
|
|
592
|
-
label="Select a country"
|
|
832
|
+
label="Select a country" // Announces: "Select a country, combobox"
|
|
593
833
|
placeholder="Search..."
|
|
594
834
|
options={countries}
|
|
595
835
|
/>
|
|
@@ -603,6 +843,11 @@ Autocomplete includes comprehensive accessibility features:
|
|
|
603
843
|
- Focus returns to input when selecting an option
|
|
604
844
|
- Clear visual focus indicators on all interactive elements
|
|
605
845
|
|
|
846
|
+
### Read Only vs Disabled Semantics
|
|
847
|
+
|
|
848
|
+
- **`readOnly`**: The input is focusable, its value can be copied, and screen readers announce it as read-only. Use when a value should be visible but not editable.
|
|
849
|
+
- **`disabled`**: The input is not focusable and is skipped in tab order. Use when the field is temporarily unavailable.
|
|
850
|
+
|
|
606
851
|
## Best Practices
|
|
607
852
|
|
|
608
853
|
### ✅ Do
|
|
@@ -611,32 +856,21 @@ Autocomplete includes comprehensive accessibility features:
|
|
|
611
856
|
|
|
612
857
|
```tsx
|
|
613
858
|
// ✅ Good: Clear label and placeholder
|
|
614
|
-
<Autocomplete
|
|
615
|
-
label="Shipping Country"
|
|
616
|
-
placeholder="Type to search countries..."
|
|
617
|
-
options={countries}
|
|
618
|
-
/>
|
|
859
|
+
<Autocomplete label="Shipping Country" placeholder="Type to search countries..." options={countries} />
|
|
619
860
|
```
|
|
620
861
|
|
|
621
862
|
2. **Show loading state during async operations**: Keep users informed
|
|
622
863
|
|
|
623
864
|
```tsx
|
|
624
865
|
// ✅ Good: Loading indicator while fetching
|
|
625
|
-
<Autocomplete
|
|
626
|
-
options={options}
|
|
627
|
-
loading={isLoading}
|
|
628
|
-
placeholder={isLoading ? 'Loading...' : 'Search...'}
|
|
629
|
-
/>
|
|
866
|
+
<Autocomplete options={options} loading={isLoading} placeholder={isLoading ? 'Loading...' : 'Search...'} />
|
|
630
867
|
```
|
|
631
868
|
|
|
632
869
|
3. **Use grouping for better organization**: Help users scan large lists
|
|
633
870
|
|
|
634
871
|
```tsx
|
|
635
872
|
// ✅ Good: Logical grouping
|
|
636
|
-
<Autocomplete
|
|
637
|
-
options={allProducts}
|
|
638
|
-
groupBy={(product) => product.category}
|
|
639
|
-
/>
|
|
873
|
+
<Autocomplete options={allProducts} groupBy={(product) => product.category} />
|
|
640
874
|
```
|
|
641
875
|
|
|
642
876
|
4. **Debounce API searches**: Prevent excessive requests
|
|
@@ -649,6 +883,20 @@ useEffect(() => {
|
|
|
649
883
|
}, [query]);
|
|
650
884
|
```
|
|
651
885
|
|
|
886
|
+
5. **Use `disableCloseOnSelect` with `multiple`**: Keep the popup open for batch selection
|
|
887
|
+
|
|
888
|
+
```tsx
|
|
889
|
+
// ✅ Good: Popup stays open for multiple selections
|
|
890
|
+
<Autocomplete multiple disableCloseOnSelect options={tags} />
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
6. **Set `limitTags` for constrained layouts**: Prevent tag overflow
|
|
894
|
+
|
|
895
|
+
```tsx
|
|
896
|
+
// ✅ Good: Show at most 3 tags, truncate the rest
|
|
897
|
+
<Autocomplete multiple limitTags={3} options={allTags} />
|
|
898
|
+
```
|
|
899
|
+
|
|
652
900
|
### ❌ Don't
|
|
653
901
|
|
|
654
902
|
1. **Don't use for small option sets**: Use Select instead
|
|
@@ -686,20 +934,36 @@ useEffect(() => {
|
|
|
686
934
|
|
|
687
935
|
```tsx
|
|
688
936
|
// ✅ Good: Handle empty results
|
|
689
|
-
<Autocomplete
|
|
690
|
-
options={filteredOptions}
|
|
691
|
-
noOptionsText="No matches found. Try a different search."
|
|
692
|
-
/>
|
|
937
|
+
<Autocomplete options={filteredOptions} noOptionsText="No matches found. Try a different search." />
|
|
693
938
|
```
|
|
694
939
|
|
|
695
940
|
4. **Don't block interaction during initial load**: Show placeholders
|
|
696
941
|
|
|
697
942
|
```tsx
|
|
698
943
|
// ❌ Bad: Blocking the entire form
|
|
699
|
-
{
|
|
944
|
+
{
|
|
945
|
+
loading ? <Spinner /> : <Autocomplete options={options} />;
|
|
946
|
+
}
|
|
700
947
|
|
|
701
948
|
// ✅ Good: Allow interaction with loading state
|
|
702
|
-
<Autocomplete options={options} loading={loading}
|
|
949
|
+
<Autocomplete options={options} loading={loading} />;
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
5. **Don't rely on `freeSolo` for validated input without extra handling**: Validate free-text values
|
|
953
|
+
|
|
954
|
+
```tsx
|
|
955
|
+
// ❌ Bad: No validation for free-text input
|
|
956
|
+
<Autocomplete freeSolo options={emails} />
|
|
957
|
+
|
|
958
|
+
// ✅ Good: Validate free-text input via onInputChange
|
|
959
|
+
<Autocomplete
|
|
960
|
+
freeSolo
|
|
961
|
+
options={emails}
|
|
962
|
+
onInputChange={(e) => {
|
|
963
|
+
const value = e.target.value;
|
|
964
|
+
setError(!isValidEmail(value));
|
|
965
|
+
}}
|
|
966
|
+
/>
|
|
703
967
|
```
|
|
704
968
|
|
|
705
969
|
## Performance Considerations
|
|
@@ -710,9 +974,7 @@ Autocomplete automatically virtualizes long lists for performance:
|
|
|
710
974
|
|
|
711
975
|
```tsx
|
|
712
976
|
// Handles 1000+ options efficiently
|
|
713
|
-
<Autocomplete
|
|
714
|
-
options={Array.from({ length: 1000 }, (_, i) => `Option ${i + 1}`)}
|
|
715
|
-
/>
|
|
977
|
+
<Autocomplete options={Array.from({ length: 1000 }, (_, i) => `Option ${i + 1}`)} />
|
|
716
978
|
```
|
|
717
979
|
|
|
718
980
|
### Debounce Search Requests
|
|
@@ -739,16 +1001,10 @@ function SearchAutocomplete({ fetchOptions }) {
|
|
|
739
1001
|
setLoading(false);
|
|
740
1002
|
}
|
|
741
1003
|
}, 300),
|
|
742
|
-
[fetchOptions]
|
|
1004
|
+
[fetchOptions],
|
|
743
1005
|
);
|
|
744
1006
|
|
|
745
|
-
return (
|
|
746
|
-
<Autocomplete
|
|
747
|
-
options={options}
|
|
748
|
-
loading={loading}
|
|
749
|
-
onInputChange={(e) => debouncedFetch(e.target.value)}
|
|
750
|
-
/>
|
|
751
|
-
);
|
|
1007
|
+
return <Autocomplete options={options} loading={loading} onInputChange={(e) => debouncedFetch(e.target.value)} />;
|
|
752
1008
|
}
|
|
753
1009
|
```
|
|
754
1010
|
|
|
@@ -764,10 +1020,10 @@ const options = useMemo(
|
|
|
764
1020
|
label: item.name,
|
|
765
1021
|
startDecorator: <StatusChip status={item.status} />,
|
|
766
1022
|
})),
|
|
767
|
-
[data]
|
|
1023
|
+
[data],
|
|
768
1024
|
);
|
|
769
1025
|
|
|
770
|
-
<Autocomplete options={options}
|
|
1026
|
+
<Autocomplete options={options} />;
|
|
771
1027
|
```
|
|
772
1028
|
|
|
773
1029
|
### Lazy Load Options
|
|
@@ -793,14 +1049,7 @@ function LazyLoadAutocomplete() {
|
|
|
793
1049
|
}
|
|
794
1050
|
};
|
|
795
1051
|
|
|
796
|
-
return
|
|
797
|
-
<Autocomplete
|
|
798
|
-
options={options}
|
|
799
|
-
loading={loading}
|
|
800
|
-
onFocus={loadOptions}
|
|
801
|
-
onOpen={loadOptions}
|
|
802
|
-
/>
|
|
803
|
-
);
|
|
1052
|
+
return <Autocomplete options={options} loading={loading} onFocus={loadOptions} onOpen={loadOptions} />;
|
|
804
1053
|
}
|
|
805
1054
|
```
|
|
806
1055
|
|
|
@@ -828,7 +1077,7 @@ const options = useMemo(
|
|
|
828
1077
|
label: user.name,
|
|
829
1078
|
startDecorator: <MemoizedOption option={user} />,
|
|
830
1079
|
})),
|
|
831
|
-
[users]
|
|
1080
|
+
[users],
|
|
832
1081
|
);
|
|
833
1082
|
```
|
|
834
1083
|
|