@utilitywarehouse/hearth-react 0.28.7 → 0.29.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/README.md +34 -20
- package/SKILL.md +355 -0
- package/dist/{chunk-TLCA3FQZ.js → chunk-ABES5BZY.js} +2 -2
- package/dist/{chunk-OHPQ5IRM.cjs → chunk-Y2CHQFKQ.cjs} +2 -2
- package/dist/{chunk-OHPQ5IRM.cjs.map → chunk-Y2CHQFKQ.cjs.map} +1 -1
- package/dist/components/CardAccordion/CardAccordion.context.d.ts.map +1 -1
- package/dist/components/ExpandableCard/ExpandableCard.cjs +1 -1
- package/dist/components/ExpandableCard/ExpandableCard.js +1 -1
- package/dist/components/ProgressBar/ProgressBar.cjs +1 -1
- package/dist/components/ProgressBar/ProgressBar.js +1 -1
- package/dist/helpers/get-classname-styles.d.ts.map +1 -1
- package/dist/helpers/logger.d.ts.map +1 -1
- package/dist/helpers/merge-ids.d.ts.map +1 -1
- package/dist/hooks/use-ids.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +14 -10
- package/public/llms/components/accordion.md +321 -0
- package/public/llms/components/alert.md +217 -0
- package/public/llms/components/avatar.md +112 -0
- package/public/llms/components/badge.md +158 -0
- package/public/llms/components/body-text.md +200 -0
- package/public/llms/components/box.md +148 -0
- package/public/llms/components/breadcrumbs.md +97 -0
- package/public/llms/components/button.md +595 -0
- package/public/llms/components/card-accordion.md +277 -0
- package/public/llms/components/card.md +985 -0
- package/public/llms/components/checkbox-group.md +193 -0
- package/public/llms/components/checkbox-tile.md +116 -0
- package/public/llms/components/checkbox.md +108 -0
- package/public/llms/components/combobox.md +360 -0
- package/public/llms/components/container.md +162 -0
- package/public/llms/components/currency-input.md +85 -0
- package/public/llms/components/date-input.md +90 -0
- package/public/llms/components/date-picker.md +159 -0
- package/public/llms/components/description-list.md +149 -0
- package/public/llms/components/detail-text.md +89 -0
- package/public/llms/components/divider.md +88 -0
- package/public/llms/components/em.md +43 -0
- package/public/llms/components/expandable-card.md +231 -0
- package/public/llms/components/flex.md +197 -0
- package/public/llms/components/grid.md +244 -0
- package/public/llms/components/heading.md +65 -0
- package/public/llms/components/helper-text.md +27 -0
- package/public/llms/components/highlight-banner.md +94 -0
- package/public/llms/components/icon-button.md +516 -0
- package/public/llms/components/icon-container.md +247 -0
- package/public/llms/components/inline-link.md +190 -0
- package/public/llms/components/label.md +28 -0
- package/public/llms/components/link.md +236 -0
- package/public/llms/components/list.md +715 -0
- package/public/llms/components/menu.md +270 -0
- package/public/llms/components/modal.md +328 -0
- package/public/llms/components/pagination.md +138 -0
- package/public/llms/components/password-input.md +93 -0
- package/public/llms/components/progress-bar.md +139 -0
- package/public/llms/components/progress-stepper.md +147 -0
- package/public/llms/components/radio-group.md +487 -0
- package/public/llms/components/search-input.md +132 -0
- package/public/llms/components/section-header.md +82 -0
- package/public/llms/components/select.md +148 -0
- package/public/llms/components/skeleton.md +282 -0
- package/public/llms/components/spinner.md +59 -0
- package/public/llms/components/strong.md +49 -0
- package/public/llms/components/switch.md +106 -0
- package/public/llms/components/table.md +230 -0
- package/public/llms/components/tabs.md +320 -0
- package/public/llms/components/text-area.md +141 -0
- package/public/llms/components/text-input.md +228 -0
- package/public/llms/components/toast.md +323 -0
- package/public/llms/components/toggle-button-card.md +513 -0
- package/public/llms/components/tooltip.md +188 -0
- package/public/llms/components/unstyled-icon-button.md +175 -0
- package/public/llms/components/validation-text.md +29 -0
- package/public/llms/components/verification-input.md +96 -0
- package/public/llms/docs/changelog.md +1430 -0
- package/public/llms/docs/common-props/align-self.md +90 -0
- package/public/llms/docs/common-props/border.md +308 -0
- package/public/llms/docs/common-props/colour.md +221 -0
- package/public/llms/docs/common-props/flex-items.md +91 -0
- package/public/llms/docs/common-props/gap.md +111 -0
- package/public/llms/docs/common-props/grid-items.md +96 -0
- package/public/llms/docs/common-props/margin.md +105 -0
- package/public/llms/docs/common-props/opacity.md +100 -0
- package/public/llms/docs/common-props/order.md +90 -0
- package/public/llms/docs/common-props/overflow.md +89 -0
- package/public/llms/docs/common-props/padding.md +102 -0
- package/public/llms/docs/common-props/position.md +92 -0
- package/public/llms/docs/common-props/size.md +93 -0
- package/public/llms/docs/common-props/spacing.md +97 -0
- package/public/llms/docs/common-props/text.md +35 -0
- package/public/llms/docs/common-props/z-index.md +88 -0
- package/public/llms/docs/design-tokens.md +72 -0
- package/public/llms/docs/getting-started.md +117 -0
- package/public/llms/docs/layout.md +135 -0
- package/public/llms/docs/migrating.md +302 -0
- package/public/llms/docs/responsive-design/breakpoints.md +119 -0
- package/public/llms/docs/responsive-design/media-queries.md +89 -0
- package/public/llms/docs/responsive-design/responsive-props.md +37 -0
- package/public/llms.txt +97 -0
- package/scripts/init-ai.js +142 -0
- package/styles.css +1 -1
- /package/dist/{chunk-TLCA3FQZ.js.map → chunk-ABES5BZY.js.map} +0 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
# Combobox
|
|
2
|
+
|
|
3
|
+
`Combobox` allows users to both type to filter results and select from a predefined list.
|
|
4
|
+
|
|
5
|
+
- [Usage](#usage)
|
|
6
|
+
- [Trigger only on type](#trigger-only-on-type)
|
|
7
|
+
- [No options found](#no-options-found)
|
|
8
|
+
- [Status text](#status-text)
|
|
9
|
+
- [useComboboxFilter](#usecomboboxfilter)
|
|
10
|
+
- [Virtualised](#virtualised)
|
|
11
|
+
- [API](#api)
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Combobox {...args} items={fruits} />
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
Pass your array of options to the `items` prop, these will be rendered using
|
|
20
|
+
the `ComboboxItem` component.
|
|
21
|
+
|
|
22
|
+
You can use `ComboboxItem` yourself if you need more control over the rendered
|
|
23
|
+
option content.
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
const fruits = ['Apple', 'Banana', 'Orange'];
|
|
27
|
+
|
|
28
|
+
[...]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
<Combobox>
|
|
32
|
+
{fruits.map(fruit => (
|
|
33
|
+
<ComboboxItem key={fruit} value={fruit}>
|
|
34
|
+
{fruit}
|
|
35
|
+
</ComboboxItem>
|
|
36
|
+
))}
|
|
37
|
+
</Combobox>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Trigger only on type
|
|
41
|
+
|
|
42
|
+
By default the dropdown will open when the input is clicked. You can change
|
|
43
|
+
this behaviour, so that the dropdown is only opened when a user starts typing,
|
|
44
|
+
with the `triggerOnlyOnType` prop. This will also remove the trigger icon button.
|
|
45
|
+
|
|
46
|
+
- Set `triggerOnlyOnType` to `false` where there is a predefined list of options for a user to choose.
|
|
47
|
+
- Set `triggerOnlyOnType` to `true` where the user is required to type before surfacing options. For example, postcode or address lookups.
|
|
48
|
+
|
|
49
|
+
## No options found
|
|
50
|
+
|
|
51
|
+
If a user searches and no options are found, a disabled option will appear in
|
|
52
|
+
the dropdown to communicate this. You can customise this using the
|
|
53
|
+
`noOptionsFoundText` prop.
|
|
54
|
+
|
|
55
|
+
This requires that you pass options via the `items` prop. If you are rendering
|
|
56
|
+
the options yourself, using `ComboboxItem`, you can use the `ComboboxEmpty`
|
|
57
|
+
component to display when there are no options available.
|
|
58
|
+
|
|
59
|
+
## Status text
|
|
60
|
+
|
|
61
|
+
Use the `statusText` prop to display a status message whose content changes are
|
|
62
|
+
announced politely to screen readers. Useful for conveying the status of an
|
|
63
|
+
asynchronously loaded list.
|
|
64
|
+
|
|
65
|
+
## useComboboxFilter
|
|
66
|
+
|
|
67
|
+
The `useComboboxFilter` hook provides robust string matching using `Intl.Collator`.
|
|
68
|
+
It returns three filter functions — `contains`, `startsWith`, and `endsWith` —
|
|
69
|
+
that can be passed to the `filter` prop on `Combobox`.
|
|
70
|
+
|
|
71
|
+
When using `useComboboxFilter`, pass the `items` prop to provide the full list
|
|
72
|
+
of options to the `Combobox`, and pass the chosen filter function to the `filter`
|
|
73
|
+
prop. The `Combobox` will handle filtering the items internally.
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { Combobox, useComboboxFilter } from '@utilitywarehouse/hearth-react';
|
|
77
|
+
|
|
78
|
+
const fruits = ['Apple', 'Banana', 'Cherry', 'Grape', 'Mango', 'Orange'];
|
|
79
|
+
|
|
80
|
+
const [value, setValue] = React.useState<string | null>(null);
|
|
81
|
+
const { contains } = useComboboxFilter({ value });
|
|
82
|
+
|
|
83
|
+
<Combobox label="Fruit" items={fruits} filter={contains} value={value} onValueChange={setValue} />;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
<Combobox
|
|
88
|
+
{...args}
|
|
89
|
+
label="Fruit (contains)"
|
|
90
|
+
items={fruits}
|
|
91
|
+
filter={contains}
|
|
92
|
+
value={value}
|
|
93
|
+
onValueChange={v => setValue(v as string | null)}
|
|
94
|
+
/>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The `startsWith` and `endsWith` functions work the same way but match only from
|
|
98
|
+
the beginning or end of each item's label respectively.
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<Combobox
|
|
102
|
+
{...args}
|
|
103
|
+
label="Fruit (startsWith)"
|
|
104
|
+
items={fruits}
|
|
105
|
+
filter={startsWith}
|
|
106
|
+
value={value}
|
|
107
|
+
onValueChange={v => setValue(v as string | null)}
|
|
108
|
+
/>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
For more advanced scenarios, such as virtualised lists where you need to manage
|
|
112
|
+
the filtered items yourself, you can call the filter functions directly:
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
const { contains } = useComboboxFilter({ value });
|
|
116
|
+
|
|
117
|
+
const filteredItems = React.useMemo(() => {
|
|
118
|
+
return allItems.filter(item => contains(item, searchValue, getItemLabel));
|
|
119
|
+
}, [contains, searchValue]);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Virtualised
|
|
123
|
+
|
|
124
|
+
Efficiently handle large datasets using a virtualization library like `@tanstack/react-virtual`.
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
<Combobox
|
|
128
|
+
virtualized
|
|
129
|
+
label="Search 10,000 items"
|
|
130
|
+
items={virtualizedItems}
|
|
131
|
+
filteredItems={filteredItems}
|
|
132
|
+
open={open}
|
|
133
|
+
onOpenChange={setOpen}
|
|
134
|
+
inputValue={searchValue}
|
|
135
|
+
onInputValueChange={setSearchValue}
|
|
136
|
+
value={value}
|
|
137
|
+
onValueChange={setValue}
|
|
138
|
+
itemToStringLabel={getItemLabel}
|
|
139
|
+
onItemHighlighted={(item: unknown, { reason, index }: { reason: string; index: number }) => {
|
|
140
|
+
if (!item) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const isStart = index === 0;
|
|
145
|
+
const isEnd = index === filteredItems.length - 1;
|
|
146
|
+
const shouldScroll = reason === 'none' || (reason === 'keyboard' && (isStart || isEnd));
|
|
147
|
+
|
|
148
|
+
if (shouldScroll) {
|
|
149
|
+
queueMicrotask(() => {
|
|
150
|
+
virtualizer.scrollToIndex(index, { align: isEnd ? 'start' : 'end' });
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}}
|
|
154
|
+
>
|
|
155
|
+
{filteredItems.length > 0 && (
|
|
156
|
+
<div
|
|
157
|
+
role="presentation"
|
|
158
|
+
ref={handleScrollElementRef}
|
|
159
|
+
style={{
|
|
160
|
+
height: `min(22rem, ${totalSize}px)`,
|
|
161
|
+
width: '100%',
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
<div role="presentation" style={{ height: totalSize }}>
|
|
165
|
+
{virtualizer.getVirtualItems().map(virtualItem => {
|
|
166
|
+
const item = filteredItems[virtualItem.index];
|
|
167
|
+
if (!item) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<ComboboxItem
|
|
173
|
+
key={virtualItem.key}
|
|
174
|
+
index={virtualItem.index}
|
|
175
|
+
data-index={virtualItem.index}
|
|
176
|
+
ref={virtualizer.measureElement}
|
|
177
|
+
value={item}
|
|
178
|
+
aria-setsize={filteredItems.length}
|
|
179
|
+
aria-posinset={virtualItem.index + 1}
|
|
180
|
+
>
|
|
181
|
+
{item.name}
|
|
182
|
+
</ComboboxItem>
|
|
183
|
+
);
|
|
184
|
+
})}
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
)}
|
|
188
|
+
</Combobox>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
import { Combobox, ComboboxItem, useComboboxFilter } from '@utilitywarehouse/hearth-react';
|
|
193
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
194
|
+
|
|
195
|
+
[...]
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
const [open, setOpen] = React.useState(false);
|
|
199
|
+
const [searchValue, setSearchValue] = React.useState('');
|
|
200
|
+
const [value, setValue] = React.useState<VirtualizedItem | null>(null);
|
|
201
|
+
|
|
202
|
+
const deferredSearchValue = React.useDeferredValue(searchValue);
|
|
203
|
+
|
|
204
|
+
const scrollElementRef = React.useRef<HTMLDivElement | null>(null);
|
|
205
|
+
|
|
206
|
+
const { contains } = useComboboxFilter({ value });
|
|
207
|
+
|
|
208
|
+
const resolvedSearchValue =
|
|
209
|
+
searchValue === '' || deferredSearchValue === '' ? searchValue : deferredSearchValue;
|
|
210
|
+
|
|
211
|
+
const filteredItems = React.useMemo(() => {
|
|
212
|
+
return virtualizedItems.filter(item => contains(item, resolvedSearchValue, getItemLabel));
|
|
213
|
+
}, [contains, resolvedSearchValue]);
|
|
214
|
+
|
|
215
|
+
const virtualizer = useVirtualizer({
|
|
216
|
+
enabled: open,
|
|
217
|
+
count: filteredItems.length,
|
|
218
|
+
getScrollElement: () => scrollElementRef.current,
|
|
219
|
+
estimateSize: () => 32,
|
|
220
|
+
overscan: 20,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const handleScrollElementRef = React.useCallback(
|
|
224
|
+
(element: HTMLDivElement | null) => {
|
|
225
|
+
scrollElementRef.current = element;
|
|
226
|
+
if (element) {
|
|
227
|
+
virtualizer.measure();
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
[virtualizer]
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const totalSize = virtualizer.getTotalSize();
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<Combobox
|
|
237
|
+
virtualized
|
|
238
|
+
label="Search 10,000 items"
|
|
239
|
+
items={virtualizedItems}
|
|
240
|
+
filteredItems={filteredItems}
|
|
241
|
+
open={open}
|
|
242
|
+
onOpenChange={setOpen}
|
|
243
|
+
inputValue={searchValue}
|
|
244
|
+
onInputValueChange={setSearchValue}
|
|
245
|
+
value={value}
|
|
246
|
+
onValueChange={setValue}
|
|
247
|
+
itemToStringLabel={getItemLabel}
|
|
248
|
+
onItemHighlighted={(item: any, { reason, index }: any) => {
|
|
249
|
+
if (!item) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const isStart = index === 0;
|
|
254
|
+
const isEnd = index === filteredItems.length - 1;
|
|
255
|
+
const shouldScroll = reason === 'none' || (reason === 'keyboard' && (isStart || isEnd));
|
|
256
|
+
|
|
257
|
+
if (shouldScroll) {
|
|
258
|
+
queueMicrotask(() => {
|
|
259
|
+
virtualizer.scrollToIndex(index, { align: isEnd ? 'start' : 'end' });
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}}
|
|
263
|
+
>
|
|
264
|
+
{filteredItems.length > 0 && (
|
|
265
|
+
<div
|
|
266
|
+
role="presentation"
|
|
267
|
+
ref={handleScrollElementRef}
|
|
268
|
+
style={{
|
|
269
|
+
height: `min(22rem, ${totalSize}px)`,
|
|
270
|
+
width: '100%',
|
|
271
|
+
}}
|
|
272
|
+
>
|
|
273
|
+
<div role="presentation" style={{ height: totalSize }}>
|
|
274
|
+
{virtualizer.getVirtualItems().map(virtualItem => {
|
|
275
|
+
const item = filteredItems[virtualItem.index];
|
|
276
|
+
if (!item) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<ComboboxItem
|
|
282
|
+
key={virtualItem.key}
|
|
283
|
+
index={virtualItem.index}
|
|
284
|
+
data-index={virtualItem.index}
|
|
285
|
+
ref={virtualizer.measureElement}
|
|
286
|
+
value={item}
|
|
287
|
+
aria-setsize={filteredItems.length}
|
|
288
|
+
aria-posinset={virtualItem.index + 1}
|
|
289
|
+
>
|
|
290
|
+
{item.name}
|
|
291
|
+
</ComboboxItem>
|
|
292
|
+
);
|
|
293
|
+
})}
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
)}
|
|
297
|
+
</Combobox>
|
|
298
|
+
)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## API
|
|
302
|
+
|
|
303
|
+
This component is based on the [Base UI Combobox](https://base-ui.com/react/components/combobox#) and supports the following common props:
|
|
304
|
+
|
|
305
|
+
- Margin
|
|
306
|
+
|
|
307
|
+
| Prop | Type | Default | Description |
|
|
308
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
309
|
+
| `triggerOnlyOnType` | `boolean` | — | |
|
|
310
|
+
| `noOptionsFoundText` | `string` | — | |
|
|
311
|
+
| `statusText` | `string` | — | Displays a status message whose content changes are announced politely to screen readers. Useful for conveying the status of an asynchronously loaded list. |
|
|
312
|
+
| `loading` | `boolean` | — | Displays a loading indicator in the input field to show that options are being fetched or updated asynchronously. |
|
|
313
|
+
| `autoComplete` | `string` | — | Provides a hint to the browser for autofill. @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/autocomplete |
|
|
314
|
+
| `itemToStringLabel` | `((itemValue: Value) => string)` | — | When the item values are objects (`<Combobox.Item value={object}>`), this function converts the object value to a string representation for display in the input. If the shape of the object is `{ value, label }`, the label will be used automatically without needing to specify this prop. |
|
|
315
|
+
| `itemToStringValue` | `((itemValue: Value) => string)` | — | When the item values are objects (`<Combobox.Item value={object}>`), this function converts the object value to a string representation for form submission. If the shape of the object is `{ value, label }`, the value will be used automatically without needing to specify this prop. |
|
|
316
|
+
| `isItemEqualToValue` | `((itemValue: Value, value: Value) => boolean)` | — | Custom comparison logic used to determine if a combobox item value matches the current selected value. Useful when item values are objects without matching referentially. Defaults to `Object.is` comparison. |
|
|
317
|
+
| `onOpenChange` | `((open: boolean, eventDetails: ChangeEventDetails) => void)` | — | Event handler called when the popup is opened or closed. |
|
|
318
|
+
| `onInputValueChange` | `((inputValue: string, eventDetails: ChangeEventDetails) => void)` | — | Event handler called when the input value changes. |
|
|
319
|
+
| `name` | `string` | — | Identifies the field when a form is submitted. |
|
|
320
|
+
| `form` | `string` | — | Identifies the form that owns the internal input. Useful when the combobox is rendered outside the form. |
|
|
321
|
+
| `id` | `string` | — | The id of the component. |
|
|
322
|
+
| `required` | `boolean` | `false` | Whether the user must choose a value before submitting a form. |
|
|
323
|
+
| `readOnly` | `boolean` | `false` | Whether the user should be unable to choose a different option from the popup. |
|
|
324
|
+
| `disabled` | `boolean` | `false` | Whether the component should ignore user interaction. |
|
|
325
|
+
| `defaultOpen` | `boolean` | `false` | Whether the popup is initially open. To render a controlled popup, use the `open` prop instead. |
|
|
326
|
+
| `open` | `boolean` | — | Whether the popup is currently open. Use when controlled. |
|
|
327
|
+
| `onOpenChangeComplete` | `((open: boolean) => void)` | — | Event handler called after any animations complete when the popup is opened or closed. |
|
|
328
|
+
| `loopFocus` | `boolean` | `true` | Whether to loop keyboard focus back to the input when the end of the list is reached while using the arrow keys. The first item can then be reached by pressing <kbd>ArrowDown</kbd> again from the input, or the last item can be reached by pressing <kbd>ArrowUp</kbd> from the input. The input is always included in the focus loop per [ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/). When disabled, focus does not move when on the last element and the user presses <kbd>ArrowDown</kbd>, or when on the first element and the user presses <kbd>ArrowUp</kbd>. |
|
|
329
|
+
| `inputValue` | `string \| number \| readonly string[]` | — | The input value of the combobox. Use when controlled. |
|
|
330
|
+
| `defaultInputValue` | `string \| number \| readonly string[]` | — | The uncontrolled input value when initially rendered. To render a controlled input, use the `inputValue` prop instead. |
|
|
331
|
+
| `inputRef` | `Ref<HTMLInputElement>` | — | A ref to the hidden input element. |
|
|
332
|
+
| `grid` | `boolean` | `false` | Whether list items are presented in a grid layout. When enabled, arrow keys navigate across rows and columns inferred from DOM rows. |
|
|
333
|
+
| `items` | `readonly any[] \| readonly Group<any>[]` | — | The items to be displayed in the list. Can be either a flat array of items or an array of groups with items. |
|
|
334
|
+
| `filteredItems` | `readonly any[] \| readonly Group<any>[]` | — | Filtered items to display in the list. When provided, the list will use these items instead of filtering the `items` prop internally. Use when you want to control filtering logic externally with the `useFilter()` hook. |
|
|
335
|
+
| `filter` | `((itemValue: Value, query: string, itemToString?: ((itemValue: Value) => string) \| undefined) => boolean) \| null` | — | Filter function used to match items vs input query. |
|
|
336
|
+
| `virtualized` | `boolean` | `false` | Whether the items are being externally virtualized. |
|
|
337
|
+
| `inline` | `boolean` | `false` | Whether the list is rendered inline without using the popup. |
|
|
338
|
+
| `modal` | `boolean` | `false` | Determines if the popup enters a modal state when open. - `true`: user interaction is limited to the popup: document page scroll is locked and pointer interactions on outside elements are disabled. - `false`: user interaction with the rest of the document is allowed. |
|
|
339
|
+
| `limit` | `number` | `-1` | The maximum number of items to display in the list. |
|
|
340
|
+
| `locale` | `LocalesArgument` | — | The locale to use for string comparison. Defaults to the user's runtime locale. |
|
|
341
|
+
| `multiple` | `boolean` | `false` | Whether multiple items can be selected. |
|
|
342
|
+
| `defaultValue` | `ComboboxValueType<Value, Multiple> \| null` | — | The uncontrolled selected value of the combobox when it's initially rendered. To render a controlled combobox, use the `value` prop instead. |
|
|
343
|
+
| `value` | `ComboboxValueType<Value, Multiple> \| null` | — | The selected value of the combobox. Use when controlled. |
|
|
344
|
+
| `onValueChange` | `((value: ComboboxValueType<Value, Multiple> \| (Multiple extends true ? never : null), eventDetails: ChangeEventDetails) => void)` | — | Event handler called when the selected value of the combobox changes. |
|
|
345
|
+
| `labelId` | `string` | — | |
|
|
346
|
+
| `helperTextId` | `string` | — | |
|
|
347
|
+
| `validationTextId` | `string` | — | |
|
|
348
|
+
| `label` | `string` | — | The label for the form field, describing its purpose. |
|
|
349
|
+
| `hideLabel` | `boolean` | — | Visually hide the label. |
|
|
350
|
+
| `labelVariant` | `"body" \| "heading"` | — | Change the label variant |
|
|
351
|
+
| `helperText` | `string` | — | Optional helper text to provide additional context or instructions. |
|
|
352
|
+
| `validationText` | `string` | — | Text to display when the `validationStatus` is set. |
|
|
353
|
+
| `validationStatus` | `"valid" \| "invalid"` | — | Indicates the validation status. |
|
|
354
|
+
| `margin` | `Responsive<"auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000" \| `var(--h-${string})`>` | — | |
|
|
355
|
+
| `marginTop` | `Responsive<"auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000" \| `var(--h-${string})`>` | — | |
|
|
356
|
+
| `marginRight` | `Responsive<"auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000" \| `var(--h-${string})`>` | — | |
|
|
357
|
+
| `marginBottom` | `Responsive<"auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000" \| `var(--h-${string})`>` | — | |
|
|
358
|
+
| `marginLeft` | `Responsive<"auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000" \| `var(--h-${string})`>` | — | |
|
|
359
|
+
| `marginX` | `Responsive<"auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000" \| `var(--h-${string})`>` | — | |
|
|
360
|
+
| `marginY` | `Responsive<"auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000" \| `var(--h-${string})`>` | — | |
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Container
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<Container {...args}>
|
|
5
|
+
<Placeholder
|
|
6
|
+
width="100%"
|
|
7
|
+
height="84px"
|
|
8
|
+
backgroundColor="secondary"
|
|
9
|
+
borderColor="subtle"
|
|
10
|
+
borderWidth="1"
|
|
11
|
+
/>
|
|
12
|
+
<Placeholder
|
|
13
|
+
width="100%"
|
|
14
|
+
height="100px"
|
|
15
|
+
backgroundColor="secondary"
|
|
16
|
+
borderColor="subtle"
|
|
17
|
+
borderWidth="1"
|
|
18
|
+
/>
|
|
19
|
+
<Placeholder
|
|
20
|
+
width="100%"
|
|
21
|
+
height={{ mobile: '544px', desktop: '383px' }}
|
|
22
|
+
backgroundColor="secondary"
|
|
23
|
+
borderColor="subtle"
|
|
24
|
+
borderWidth="1"
|
|
25
|
+
/>
|
|
26
|
+
</Container>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Alternatives
|
|
30
|
+
|
|
31
|
+
- Box - For primitive styling and layouts
|
|
32
|
+
- Grid - For grid layouts
|
|
33
|
+
- Flex - For stacked, inline or flexbox based
|
|
34
|
+
layouts
|
|
35
|
+
|
|
36
|
+
## Semantic HTML
|
|
37
|
+
|
|
38
|
+
By default `Container` renders a `div` element, this can be customised using
|
|
39
|
+
the `asChild` prop.
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
<Container asChild>
|
|
43
|
+
<main>...</main>
|
|
44
|
+
</Container>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Max width
|
|
48
|
+
|
|
49
|
+
By default `Container` has a max-width of `100%` for screen widths below the
|
|
50
|
+
desktop breakpoint, and for desktop screens, and above, the max-width is
|
|
51
|
+
defined by the container width design token.
|
|
52
|
+
|
|
53
|
+
If you need to, you can apply your app-specific max-width, usually only at the
|
|
54
|
+
`desktop` breakpoint.
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<Container maxWidth={{ desktop: '1096px' }}>{...}</Container>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Align
|
|
61
|
+
|
|
62
|
+
By default `Container` aligns its content in the center, you can change this
|
|
63
|
+
with the `align` prop.
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
<Container align='start'>{...}</Container>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Spacing
|
|
70
|
+
|
|
71
|
+
As well as the `gap` prop for flexbox spacing, there is a `spacing` prop which
|
|
72
|
+
uses fewer steps and is already optimised for responsive design. This prop will
|
|
73
|
+
be overridden by the `gap` prop.
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<Container spacing="lg">
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Show/hide content
|
|
80
|
+
|
|
81
|
+
The `display` prop is responsive, you can use this to show or hide content responsively.
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
<Container display={{ mobile: 'none', tablet: 'flex' }}>hide on mobile screens</Container>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## API
|
|
88
|
+
|
|
89
|
+
This component is based on the `div` element and supports the following common props:
|
|
90
|
+
|
|
91
|
+
- AlignSelf
|
|
92
|
+
- Gap
|
|
93
|
+
- Padding
|
|
94
|
+
- Margin
|
|
95
|
+
- Position
|
|
96
|
+
- Size
|
|
97
|
+
- Background colour
|
|
98
|
+
- Flex item
|
|
99
|
+
- Z-Index
|
|
100
|
+
- Overflow
|
|
101
|
+
- Order
|
|
102
|
+
|
|
103
|
+
| Prop | Type | Default | Description |
|
|
104
|
+
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
105
|
+
| `spacing` | `"none" \| "2xs" \| "xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl"` | — | Set responsive spacing between child elements. |
|
|
106
|
+
| `asChild` | `boolean` | — | |
|
|
107
|
+
| `display` | `Responsive<"flex" \| "none" \| "inline-flex">` | — | |
|
|
108
|
+
| `direction` | `Responsive<"row" \| "column" \| "row-reverse" \| "column-reverse">` | — | |
|
|
109
|
+
| `wrap` | `Responsive<"wrap" \| "nowrap" \| "wrap-reverse">` | — | |
|
|
110
|
+
| `alignContent` | `Responsive<"center" \| "start" \| "end" \| "stretch" \| "between" \| "around" \| "evenly">` | — | |
|
|
111
|
+
| `alignItems` | `Responsive<"center" \| "start" \| "end" \| "baseline" \| "stretch">` | — | |
|
|
112
|
+
| `alignSelf` | `Responsive<"center" \| "start" \| "end" \| "stretch">` | — | |
|
|
113
|
+
| `backgroundColor` | `"primary" \| "secondary" \| "brand" \| `var(--h-${string})`` | — | |
|
|
114
|
+
| `borderRadius` | `Responsive<"none" \| "inherit" \| "xs" \| "sm" \| "md" \| "lg" \| "xl" \| "full">` | — | |
|
|
115
|
+
| `borderRadiusTopLeftNone` | `boolean` | — | |
|
|
116
|
+
| `borderRadiusTopRightNone` | `boolean` | — | |
|
|
117
|
+
| `borderRadiusBottomLeftNone` | `boolean` | — | |
|
|
118
|
+
| `borderRadiusBottomRightNone` | `boolean` | — | |
|
|
119
|
+
| `borderRadiusTopNone` | `boolean` | — | |
|
|
120
|
+
| `borderRadiusRightNone` | `boolean` | — | |
|
|
121
|
+
| `borderRadiusBottomNone` | `boolean` | — | |
|
|
122
|
+
| `borderRadiusLeftNone` | `boolean` | — | |
|
|
123
|
+
| `flex` | `Responsive<string>` | — | |
|
|
124
|
+
| `flexBasis` | `Responsive<string>` | — | |
|
|
125
|
+
| `flexShrink` | `Responsive<string>` | — | |
|
|
126
|
+
| `flexGrow` | `Responsive<string>` | — | |
|
|
127
|
+
| `gap` | `Responsive<`var(--h-${string})` \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
128
|
+
| `rowGap` | `Responsive<`var(--h-${string})` \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
129
|
+
| `columnGap` | `Responsive<`var(--h-${string})` \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
130
|
+
| `justifyContent` | `Responsive<"center" \| "start" \| "end" \| "stretch" \| "between" \| "around" \| "evenly">` | — | For flexboxes, the stretch value behaves as flex-start or start. This is because, in flexboxes, stretching is controlled using the flex-grow property. |
|
|
131
|
+
| `margin` | `Responsive<`var(--h-${string})` \| "auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
132
|
+
| `marginTop` | `Responsive<`var(--h-${string})` \| "auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
133
|
+
| `marginRight` | `Responsive<`var(--h-${string})` \| "auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
134
|
+
| `marginBottom` | `Responsive<`var(--h-${string})` \| "auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
135
|
+
| `marginLeft` | `Responsive<`var(--h-${string})` \| "auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
136
|
+
| `marginX` | `Responsive<`var(--h-${string})` \| "auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
137
|
+
| `marginY` | `Responsive<`var(--h-${string})` \| "auto" \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
138
|
+
| `order` | `Responsive<string>` | — | |
|
|
139
|
+
| `overflow` | `Responsive<"hidden" \| "auto" \| "visible" \| "clip" \| "scroll">` | — | |
|
|
140
|
+
| `overflowX` | `Responsive<"hidden" \| "auto" \| "visible" \| "clip" \| "scroll">` | — | |
|
|
141
|
+
| `overflowY` | `Responsive<"hidden" \| "auto" \| "visible" \| "clip" \| "scroll">` | — | |
|
|
142
|
+
| `padding` | `Responsive<`var(--h-${string})` \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
143
|
+
| `paddingTop` | `Responsive<`var(--h-${string})` \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
144
|
+
| `paddingRight` | `Responsive<`var(--h-${string})` \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
145
|
+
| `paddingBottom` | `Responsive<`var(--h-${string})` \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
146
|
+
| `paddingLeft` | `Responsive<`var(--h-${string})` \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
147
|
+
| `paddingX` | `Responsive<`var(--h-${string})` \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
148
|
+
| `paddingY` | `Responsive<`var(--h-${string})` \| "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">` | — | |
|
|
149
|
+
| `position` | `Responsive<"static" \| "relative" \| "absolute" \| "fixed" \| "sticky">` | — | |
|
|
150
|
+
| `inset` | `Responsive<Union<string, "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">>` | — | |
|
|
151
|
+
| `top` | `Responsive<Union<string, "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">>` | — | |
|
|
152
|
+
| `right` | `Responsive<Union<string, "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">>` | — | |
|
|
153
|
+
| `bottom` | `Responsive<Union<string, "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">>` | — | |
|
|
154
|
+
| `left` | `Responsive<Union<string, "0" \| "25" \| "50" \| "75" \| "100" \| "150" \| "175" \| "200" \| "250" \| "300" \| "350" \| "400" \| "500" \| "600" \| "700" \| "800" \| "900" \| "1000">>` | — | |
|
|
155
|
+
| `width` | `Responsive<string>` | — | |
|
|
156
|
+
| `maxWidth` | `Responsive<string>` | — | |
|
|
157
|
+
| `minWidth` | `Responsive<string>` | — | |
|
|
158
|
+
| `height` | `Responsive<string>` | — | |
|
|
159
|
+
| `maxHeight` | `Responsive<string>` | — | |
|
|
160
|
+
| `minHeight` | `Responsive<string>` | — | |
|
|
161
|
+
| `zIndex` | `Responsive<string>` | — | |
|
|
162
|
+
| `align` | `Responsive<"center" \| "start" \| "end">` | `center` | |
|