@boxcustodia/library 2.0.0-alpha.12 → 2.0.0-alpha.14
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/index.cjs.js +1 -138
- package/dist/index.d.ts +1087 -720
- package/dist/index.es.js +7011 -56097
- package/dist/theme.css +1 -1
- package/package.json +34 -26
- package/src/__doc__/Examples.tsx +1 -1
- package/src/__doc__/Intro.mdx +3 -3
- package/src/__doc__/Tabs.mdx +112 -0
- package/src/__doc__/V2.mdx +1246 -0
- package/src/components/accordion/accordion.stories.tsx +143 -0
- package/src/components/accordion/accordion.tsx +135 -0
- package/src/components/accordion/index.ts +1 -0
- package/src/components/alert/alert.stories.tsx +24 -4
- package/src/components/alert/alert.tsx +17 -9
- package/src/components/alert-dialog/alert-dialog.stories.tsx +24 -0
- package/src/components/alert-dialog/alert-dialog.test.tsx +1 -1
- package/src/components/alert-dialog/alert-dialog.tsx +58 -10
- package/src/components/auto-complete/auto-complete.stories.tsx +616 -200
- package/src/components/auto-complete/auto-complete.tsx +420 -68
- package/src/components/auto-complete/index.ts +0 -1
- package/src/components/avatar/avatar.stories.tsx +162 -21
- package/src/components/avatar/avatar.tsx +79 -20
- package/src/components/button/button.stories.tsx +219 -294
- package/src/components/button/button.test.tsx +10 -17
- package/src/components/button/button.tsx +78 -19
- package/src/components/button/components/base-button.tsx +30 -53
- package/src/components/button/index.ts +0 -1
- package/src/components/calendar/calendar.stories.tsx +1 -1
- package/src/components/calendar/calendar.tsx +4 -4
- package/src/components/card/card.stories.tsx +141 -69
- package/src/components/card/card.tsx +155 -54
- package/src/components/center/center.stories.tsx +22 -39
- package/src/components/checkbox/checkbox.stories.tsx +25 -5
- package/src/components/checkbox/checkbox.tsx +76 -15
- package/src/components/checkbox-group/checkbox-group.stories.tsx +116 -28
- package/src/components/checkbox-group/checkbox-group.tsx +84 -3
- package/src/components/combobox/combobox.stories.tsx +33 -23
- package/src/components/combobox/combobox.tsx +99 -77
- package/src/components/date-picker/date-input.stories.tsx +14 -6
- package/src/components/date-picker/date-input.tsx +2 -2
- package/src/components/date-picker/date-picker.model.ts +13 -4
- package/src/components/date-picker/date-picker.stories.tsx +38 -12
- package/src/components/date-picker/date-picker.tsx +28 -14
- package/src/components/dialog/dialog.stories.tsx +18 -0
- package/src/components/dialog/dialog.test.tsx +1 -1
- package/src/components/dialog/dialog.tsx +51 -20
- package/src/components/divider/divider.stories.tsx +126 -51
- package/src/components/divider/divider.tsx +16 -16
- package/src/components/dropzone/dropzone.stories.tsx +71 -90
- package/src/components/dropzone/dropzone.tsx +383 -105
- package/src/components/dropzone/index.ts +0 -1
- package/src/components/empty/empty.stories.tsx +165 -0
- package/src/components/empty/empty.tsx +156 -0
- package/src/components/empty/index.ts +1 -0
- package/src/components/field/field.stories.tsx +227 -4
- package/src/components/field/field.tsx +77 -42
- package/src/components/form/form.stories.tsx +320 -197
- package/src/components/form/form.tsx +3 -23
- package/src/components/index.ts +2 -6
- package/src/components/input/input.stories.tsx +5 -5
- package/src/components/input/input.tsx +4 -4
- package/src/components/kbd/kbd.stories.tsx +1 -0
- package/src/components/label/label.stories.tsx +16 -0
- package/src/components/label/label.tsx +13 -2
- package/src/components/loader/loader.stories.tsx +7 -5
- package/src/components/loader/loader.tsx +8 -3
- package/src/components/menu/menu-primitives.tsx +207 -196
- package/src/components/menu/menu.stories.tsx +276 -146
- package/src/components/menu/menu.tsx +146 -54
- package/src/components/number-input/number-input.stories.tsx +27 -4
- package/src/components/number-input/number-input.test.tsx +2 -2
- package/src/components/number-input/number-input.tsx +31 -33
- package/src/components/otp/index.ts +1 -0
- package/src/components/otp/otp.stories.tsx +209 -0
- package/src/components/otp/otp.tsx +100 -0
- package/src/components/pagination/index.ts +1 -0
- package/src/components/pagination/pagination.model.ts +2 -0
- package/src/components/pagination/pagination.stories.tsx +154 -59
- package/src/components/pagination/pagination.test.tsx +122 -57
- package/src/components/pagination/pagination.tsx +575 -77
- package/src/components/password/password.stories.tsx +18 -3
- package/src/components/password/password.tsx +29 -9
- package/src/components/popover/popover.stories.tsx +26 -5
- package/src/components/popover/popover.tsx +15 -23
- package/src/components/progress/progress.stories.tsx +1 -0
- package/src/components/radio-group/index.ts +1 -0
- package/src/components/radio-group/radio-group.stories.tsx +251 -0
- package/src/components/radio-group/radio-group.tsx +212 -0
- package/src/components/scroll-area/scroll-area.stories.tsx +1 -0
- package/src/components/select/select.stories.tsx +118 -19
- package/src/components/select/select.tsx +67 -62
- package/src/components/skeleton/skeleton.stories.tsx +1 -0
- package/src/components/stack/stack.stories.tsx +179 -89
- package/src/components/stack/stack.tsx +2 -2
- package/src/components/stepper/index.ts +1 -1
- package/src/components/stepper/stepper.stories.tsx +767 -83
- package/src/components/stepper/stepper.test.tsx +18 -18
- package/src/components/stepper/stepper.tsx +554 -0
- package/src/components/switch/switch.stories.tsx +15 -1
- package/src/components/switch/switch.tsx +17 -4
- package/src/components/table/index.ts +0 -2
- package/src/components/table/table.stories.tsx +131 -18
- package/src/components/table/table.test.tsx +1 -1
- package/src/components/table/table.tsx +183 -77
- package/src/components/tabs/tabs.stories.tsx +373 -155
- package/src/components/tabs/tabs.test.tsx +12 -12
- package/src/components/tabs/tabs.tsx +72 -149
- package/src/components/tag/index.ts +0 -1
- package/src/components/tag/tag.stories.tsx +155 -120
- package/src/components/tag/tag.tsx +47 -95
- package/src/components/textarea/textarea.stories.tsx +8 -22
- package/src/components/textarea/textarea.tsx +17 -79
- package/src/components/timeline/timeline.stories.tsx +323 -42
- package/src/components/timeline/timeline.tsx +359 -132
- package/src/components/toast/toast.stories.tsx +1 -0
- package/src/components/tooltip/tooltip.tsx +11 -9
- package/src/components/tree/index.ts +0 -1
- package/src/components/tree/tree.stories.tsx +365 -408
- package/src/components/tree/tree.test.tsx +163 -0
- package/src/components/tree/tree.tsx +212 -36
- package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +5 -5
- package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +1 -3
- package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +6 -6
- package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +1 -1
- package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +1 -1
- package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +1 -1
- package/src/hooks/usePagination/usePagination.tsx +36 -24
- package/src/styles/theme.css +1 -1
- package/src/utils/form.tsx +67 -37
- package/src/utils/index.ts +1 -1
- package/src/__doc__/Migration.mdx +0 -475
- package/src/components/auto-complete/auto-complete-primitives.tsx +0 -155
- package/src/components/background-image/background-image.stories.tsx +0 -21
- package/src/components/background-image/background-image.test.tsx +0 -29
- package/src/components/background-image/background-image.tsx +0 -23
- package/src/components/background-image/index.ts +0 -1
- package/src/components/button/button.variants.ts +0 -44
- package/src/components/button/components/loader-overlay.tsx +0 -21
- package/src/components/button/components/loading-icon.tsx +0 -47
- package/src/components/dropzone/upload-primitives.tsx +0 -310
- package/src/components/dropzone/use-dropzone.ts +0 -122
- package/src/components/empty-state/empty-state.stories.tsx +0 -56
- package/src/components/empty-state/empty-state.tsx +0 -39
- package/src/components/empty-state/index.ts +0 -1
- package/src/components/heading/heading.stories.tsx +0 -74
- package/src/components/heading/heading.tsx +0 -28
- package/src/components/heading/heading.variants.ts +0 -27
- package/src/components/heading/index.ts +0 -1
- package/src/components/kbd/kbd.variants.ts +0 -26
- package/src/components/menu/util/render-menu-item.tsx +0 -54
- package/src/components/multi-select/hooks/use-multi-select.ts +0 -66
- package/src/components/multi-select/index.ts +0 -1
- package/src/components/multi-select/multi-select.stories.tsx +0 -294
- package/src/components/multi-select/multi-select.tsx +0 -300
- package/src/components/multi-select/multi-select.variants.ts +0 -22
- package/src/components/pagination/components/pagination-option.tsx +0 -27
- package/src/components/show/index.ts +0 -1
- package/src/components/show/show.stories.tsx +0 -197
- package/src/components/show/show.test.tsx +0 -41
- package/src/components/show/show.tsx +0 -16
- package/src/components/stepper/Stepper.tsx +0 -190
- package/src/components/stepper/context/stepper-context.tsx +0 -11
- package/src/components/table/table-primitives.tsx +0 -122
- package/src/components/table/table.model.ts +0 -20
- package/src/components/table-pagination/index.ts +0 -2
- package/src/components/table-pagination/table-pagination.model.ts +0 -2
- package/src/components/table-pagination/table-pagination.stories.tsx +0 -23
- package/src/components/table-pagination/table-pagination.test.tsx +0 -32
- package/src/components/table-pagination/table-pagination.tsx +0 -108
- package/src/components/tabs/context/tabs-context.tsx +0 -14
- package/src/components/tag/tag.variants.ts +0 -31
- package/src/components/timeline/timeline-status.ts +0 -5
- package/src/components/tree/hooks/use-controllable-tree-state.ts +0 -80
- package/src/components/tree/tree-primitives.tsx +0 -126
|
@@ -1,241 +1,657 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { BuildingIcon, SearchIcon, UserIcon } from "lucide-react";
|
|
3
|
+
import { useRef, useState, useTransition } from "react";
|
|
2
4
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
5
|
+
Autocomplete,
|
|
6
|
+
AutocompleteCollection,
|
|
7
|
+
AutocompleteEmpty,
|
|
8
|
+
AutocompleteGroup,
|
|
9
|
+
AutocompleteGroupLabel,
|
|
10
|
+
AutocompleteInput,
|
|
11
|
+
AutocompleteItem,
|
|
12
|
+
AutocompleteList,
|
|
13
|
+
type AutocompleteOption,
|
|
14
|
+
AutocompletePopup,
|
|
15
|
+
AutocompleteRoot,
|
|
16
|
+
AutocompleteRow,
|
|
17
|
+
AutocompleteSeparator,
|
|
18
|
+
AutocompleteStatus,
|
|
19
|
+
AutocompleteValue,
|
|
20
|
+
useAutocompleteFilter,
|
|
21
|
+
} from "./auto-complete";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Autocomplete / combobox built on [Base UI Autocomplete](https://base-ui.com/react/components/autocomplete).
|
|
25
|
+
* Provides a composite `<Autocomplete>` for standard use cases and individual primitives
|
|
26
|
+
* for full structural control (groups, custom layouts, async search).
|
|
27
|
+
*
|
|
28
|
+
* Key behaviors:
|
|
29
|
+
* - `autoHighlight` is `true` by default in `AutocompleteRoot` — the first filtered item is
|
|
30
|
+
* highlighted as the user types without requiring an explicit selection.
|
|
31
|
+
* - `adornment` on `AutocompleteInput` is mutually exclusive: `"trigger"` renders the chevron
|
|
32
|
+
* button, `"clear"` renders the X button. Setting both is not possible by design.
|
|
33
|
+
* - In the composite `Autocomplete`, `onValueChange` fires with the full `TOption` object (or `null`
|
|
34
|
+
* on clear), not just the string value. `value` and `defaultValue` also accept `TOption | null`.
|
|
35
|
+
* - `itemToStringValue` on `AutocompleteRoot` controls what is shown in the input after selection.
|
|
36
|
+
* The composite sets it to `(opt) => opt.label` automatically.
|
|
37
|
+
* - `filter={null}` disables client-side filtering entirely — use it for server-side search.
|
|
38
|
+
* - `AutocompleteStatus` collapses to zero height when its children are empty (`empty:m-0 empty:p-0`).
|
|
39
|
+
* Pass `undefined` (not an empty string) to hide it.
|
|
40
|
+
* - `AutocompleteEmpty` is shown by Base UI only when the list has no visible items.
|
|
41
|
+
*
|
|
42
|
+
* Reference: [Base UI – Autocomplete](https://base-ui.com/react/components/autocomplete)
|
|
43
|
+
*/
|
|
44
|
+
const meta: Meta<typeof Autocomplete> = {
|
|
45
|
+
title: "Components/Autocomplete",
|
|
46
|
+
component: Autocomplete,
|
|
47
|
+
parameters: { layout: "centered" },
|
|
48
|
+
decorators: [
|
|
49
|
+
(Story) => (
|
|
50
|
+
<div className="w-72">
|
|
51
|
+
<Story />
|
|
52
|
+
</div>
|
|
53
|
+
),
|
|
54
|
+
],
|
|
55
|
+
args: {
|
|
56
|
+
items: [
|
|
57
|
+
{ value: "apple", label: "Apple" },
|
|
58
|
+
{ value: "banana", label: "Banana" },
|
|
59
|
+
{ value: "cherry", label: "Cherry" },
|
|
60
|
+
{ value: "grape", label: "Grape" },
|
|
61
|
+
{ value: "kiwi", label: "Kiwi" },
|
|
62
|
+
{ value: "mango", label: "Mango" },
|
|
63
|
+
{ value: "orange", label: "Orange" },
|
|
64
|
+
{ value: "peach", label: "Peach" },
|
|
65
|
+
{ value: "pear", label: "Pear" },
|
|
66
|
+
{ value: "strawberry", label: "Strawberry" },
|
|
67
|
+
],
|
|
68
|
+
placeholder: "Search...",
|
|
69
|
+
emptyMessage: "No results",
|
|
70
|
+
},
|
|
71
|
+
argTypes: {
|
|
72
|
+
onValueChange: { control: false },
|
|
73
|
+
renderOption: { control: false },
|
|
74
|
+
startAddon: { control: false },
|
|
75
|
+
classNames: { control: false },
|
|
76
|
+
},
|
|
77
|
+
tags: ["beta"],
|
|
41
78
|
};
|
|
42
79
|
|
|
43
80
|
export default meta;
|
|
44
|
-
type Story = StoryObj<typeof
|
|
81
|
+
type Story = StoryObj<typeof Autocomplete>;
|
|
82
|
+
|
|
83
|
+
export const Default: Story = {};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* `className` styles the input field. `classNames` exposes the popup, list,
|
|
87
|
+
* item, empty, and status slots for fine-grained tweaks without dropping to
|
|
88
|
+
* the primitive composition.
|
|
89
|
+
*
|
|
90
|
+
* For deeper customization (custom layouts, extra slots, async patterns) use
|
|
91
|
+
* the primitives directly — they are the real extension point.
|
|
92
|
+
*/
|
|
93
|
+
export const WithClassNames: Story = {
|
|
94
|
+
args: {
|
|
95
|
+
className: "w-80",
|
|
96
|
+
classNames: {
|
|
97
|
+
popup: "max-h-60",
|
|
98
|
+
item: "py-2",
|
|
99
|
+
empty: "italic",
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The trigger button opens and closes the popup on click — useful when
|
|
106
|
+
* the user wants to browse all options without typing.
|
|
107
|
+
*/
|
|
108
|
+
export const WithTrigger: Story = {
|
|
109
|
+
args: {
|
|
110
|
+
adornment: "trigger",
|
|
111
|
+
},
|
|
112
|
+
};
|
|
45
113
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
114
|
+
/**
|
|
115
|
+
* The clear button appears whenever the input has a value and resets it on click.
|
|
116
|
+
* `adornment` is mutually exclusive — `"clear"` and `"trigger"` cannot both be active.
|
|
117
|
+
*/
|
|
118
|
+
export const WithClear: Story = {
|
|
119
|
+
args: {
|
|
120
|
+
adornment: "clear",
|
|
121
|
+
},
|
|
122
|
+
};
|
|
51
123
|
|
|
52
|
-
export const
|
|
124
|
+
export const WithStartAddon: Story = {
|
|
53
125
|
args: {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
126
|
+
startAddon: <SearchIcon />,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const Disabled: Story = {
|
|
131
|
+
args: { disabled: true },
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const ReadOnly: Story = {
|
|
135
|
+
args: { readOnly: true, defaultValue: { value: "mango", label: "Mango" } },
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* `renderOption` receives the full option object typed as `TOption extends AutocompleteOption`
|
|
140
|
+
* and returns a `ReactNode`. Use it to render icons, metadata, descriptions, or any layout.
|
|
141
|
+
* When `renderOption` is provided, `option.label` is NOT rendered automatically.
|
|
142
|
+
*
|
|
143
|
+
* Extend `AutocompleteOption` to add extra fields:
|
|
144
|
+
* ```ts
|
|
145
|
+
* interface UserOption extends AutocompleteOption {
|
|
146
|
+
* role: "admin" | "user" | "company";
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export const WithCustomOption: Story = {
|
|
151
|
+
render: () => {
|
|
152
|
+
interface UserOption {
|
|
153
|
+
value: string;
|
|
154
|
+
label: string;
|
|
155
|
+
role: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const users: UserOption[] = [
|
|
159
|
+
{ value: "ana", label: "Ana García", role: "admin" },
|
|
160
|
+
{ value: "carlos", label: "Carlos López", role: "user" },
|
|
161
|
+
{ value: "maria", label: "María Fernández", role: "admin" },
|
|
162
|
+
{ value: "acme", label: "Acme Corp.", role: "company" },
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
const RoleIcon = ({ role }: { role: string }) =>
|
|
166
|
+
role === "company" ? (
|
|
167
|
+
<BuildingIcon className="size-4 shrink-0 text-muted-foreground" />
|
|
168
|
+
) : (
|
|
169
|
+
<UserIcon className="size-4 shrink-0 text-muted-foreground" />
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<Autocomplete
|
|
174
|
+
items={users}
|
|
175
|
+
placeholder="Search user..."
|
|
176
|
+
emptyMessage="No users found"
|
|
177
|
+
adornment="clear"
|
|
178
|
+
startAddon={<SearchIcon />}
|
|
179
|
+
renderOption={(u) => (
|
|
180
|
+
<span className="flex items-center gap-2">
|
|
181
|
+
<RoleIcon role={u.role} />
|
|
182
|
+
<span className="flex-1">{u.label}</span>
|
|
183
|
+
<span className="text-xs capitalize text-muted-foreground">
|
|
184
|
+
{u.role}
|
|
185
|
+
</span>
|
|
186
|
+
</span>
|
|
187
|
+
)}
|
|
188
|
+
/>
|
|
189
|
+
);
|
|
57
190
|
},
|
|
58
191
|
};
|
|
59
192
|
|
|
60
193
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
194
|
+
* `onValueChange` fires with the full `TOption` object (or `null` when cleared).
|
|
195
|
+
* `value` and `defaultValue` also accept `TOption | null`, matching the
|
|
196
|
+
* same pattern as `Select` and `Combobox` in this library.
|
|
197
|
+
*
|
|
198
|
+
* To clear programmatically, set `value={null}`.
|
|
199
|
+
*/
|
|
200
|
+
export const Controlled: Story = {
|
|
201
|
+
render: () => {
|
|
202
|
+
const items = [
|
|
203
|
+
{ value: "apple", label: "Apple" },
|
|
204
|
+
{ value: "banana", label: "Banana" },
|
|
205
|
+
{ value: "cherry", label: "Cherry" },
|
|
206
|
+
{ value: "grape", label: "Grape" },
|
|
207
|
+
{ value: "mango", label: "Mango" },
|
|
208
|
+
{ value: "orange", label: "Orange" },
|
|
209
|
+
];
|
|
210
|
+
const [selected, setSelected] = useState<AutocompleteOption | null>(null);
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<div className="flex flex-col gap-4">
|
|
214
|
+
<Autocomplete
|
|
215
|
+
items={items}
|
|
216
|
+
value={selected}
|
|
217
|
+
onValueChange={setSelected}
|
|
218
|
+
adornment="clear"
|
|
219
|
+
placeholder="Search fruit..."
|
|
220
|
+
emptyMessage="No results"
|
|
221
|
+
/>
|
|
222
|
+
<p className="text-sm text-muted-foreground">
|
|
223
|
+
Selected:{" "}
|
|
224
|
+
<strong>
|
|
225
|
+
{selected ? `${selected.label} (${selected.value})` : "—"}
|
|
226
|
+
</strong>
|
|
227
|
+
</p>
|
|
228
|
+
</div>
|
|
229
|
+
);
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* For server-side search, set `filter={null}` on `AutocompleteRoot` to disable client-side
|
|
235
|
+
* filtering entirely — the caller owns the `items` prop and updates it on each keystroke.
|
|
236
|
+
*
|
|
237
|
+
* Pattern:
|
|
238
|
+
* - `useTransition` exposes a `isPending` flag without blocking the UI.
|
|
239
|
+
* - `AbortController` cancels the previous request when the user types again.
|
|
240
|
+
* - `useAutocompleteFilter` provides the same `contains` logic used internally,
|
|
241
|
+
* so you can apply it on the server response if needed.
|
|
242
|
+
* - Hide `AutocompleteEmpty` while `isPending` to avoid a flash of "no results".
|
|
243
|
+
*
|
|
244
|
+
* ```tsx
|
|
245
|
+
* <AutocompleteRoot
|
|
246
|
+
* items={results}
|
|
247
|
+
* filter={null}
|
|
248
|
+
* itemToStringValue={(item) => item.label}
|
|
249
|
+
* onValueChange={search}
|
|
250
|
+
* >
|
|
251
|
+
* <AutocompleteInput placeholder="Search..." adornment="clear"/>
|
|
252
|
+
* <AutocompletePopup>
|
|
253
|
+
* <AutocompleteStatus>{isPending ? "Searching..." : undefined}</AutocompleteStatus>
|
|
254
|
+
* <AutocompleteList>
|
|
255
|
+
* {(item) => (
|
|
256
|
+
* <AutocompleteItem key={item.value} value={item}>
|
|
257
|
+
* {item.label}
|
|
258
|
+
* </AutocompleteItem>
|
|
259
|
+
* )}
|
|
260
|
+
* </AutocompleteList>
|
|
261
|
+
* {!isPending && <AutocompleteEmpty>No results</AutocompleteEmpty>}
|
|
262
|
+
* </AutocompletePopup>
|
|
263
|
+
* </AutocompleteRoot>
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
export const AsyncSearch: Story = {
|
|
267
|
+
render: () => {
|
|
268
|
+
const allFruits = [
|
|
269
|
+
{ value: "apple", label: "Apple" },
|
|
270
|
+
{ value: "banana", label: "Banana" },
|
|
271
|
+
{ value: "cherry", label: "Cherry" },
|
|
272
|
+
{ value: "grape", label: "Grape" },
|
|
273
|
+
{ value: "kiwi", label: "Kiwi" },
|
|
274
|
+
{ value: "mango", label: "Mango" },
|
|
275
|
+
{ value: "orange", label: "Orange" },
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
const [results, setResults] = useState<typeof allFruits>([]);
|
|
279
|
+
const [isPending, startTransition] = useTransition();
|
|
280
|
+
const abortRef = useRef<AbortController | null>(null);
|
|
281
|
+
const { contains } = useAutocompleteFilter();
|
|
282
|
+
|
|
283
|
+
const search = (query: string) => {
|
|
284
|
+
abortRef.current?.abort();
|
|
285
|
+
const controller = new AbortController();
|
|
286
|
+
abortRef.current = controller;
|
|
287
|
+
|
|
288
|
+
if (!query) {
|
|
289
|
+
setResults([]);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
startTransition(async () => {
|
|
294
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
295
|
+
if (controller.signal.aborted) return;
|
|
296
|
+
startTransition(() => {
|
|
297
|
+
setResults(allFruits.filter((f) => contains(f.label, query)));
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
return (
|
|
303
|
+
<AutocompleteRoot items={results} filter={null} onValueChange={search}>
|
|
304
|
+
<AutocompleteInput placeholder="Search fruit..." adornment="clear" />
|
|
305
|
+
<AutocompletePopup>
|
|
306
|
+
<AutocompleteStatus>
|
|
307
|
+
{isPending ? "Searching..." : undefined}
|
|
308
|
+
</AutocompleteStatus>
|
|
309
|
+
<AutocompleteList>
|
|
310
|
+
{(fruit) => (
|
|
311
|
+
<AutocompleteItem key={fruit.value} value={fruit}>
|
|
312
|
+
{fruit.label}
|
|
313
|
+
</AutocompleteItem>
|
|
314
|
+
)}
|
|
315
|
+
</AutocompleteList>
|
|
316
|
+
{!isPending && <AutocompleteEmpty>No results</AutocompleteEmpty>}
|
|
317
|
+
</AutocompletePopup>
|
|
318
|
+
</AutocompleteRoot>
|
|
319
|
+
);
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Grouped items require the primitive composition pattern — the composite `Autocomplete`
|
|
325
|
+
* does not support groups. Pass group objects to `AutocompleteRoot.items`, where each group
|
|
326
|
+
* has a nested `items` array. Use `AutocompleteCollection` inside each `AutocompleteGroup`
|
|
327
|
+
* so Base UI can filter and highlight items within groups independently.
|
|
328
|
+
*
|
|
329
|
+
* `AutocompleteGroupLabel` is sticky and not selectable. `[[role=group]+&]:mt-1.5` on
|
|
330
|
+
* `AutocompleteGroup` adds spacing between consecutive groups automatically.
|
|
331
|
+
*
|
|
64
332
|
* ```tsx
|
|
65
|
-
* <AutocompleteRoot>
|
|
66
|
-
* <AutocompleteInput placeholder="Buscar..." />
|
|
333
|
+
* <AutocompleteRoot items={groups}>
|
|
67
334
|
* <AutocompleteList>
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
335
|
+
* {(group) => (
|
|
336
|
+
* <AutocompleteGroup key={group.value} items={group.items}>
|
|
337
|
+
* <AutocompleteGroupLabel>{group.label}</AutocompleteGroupLabel>
|
|
338
|
+
* <AutocompleteCollection>
|
|
339
|
+
* {(item) => (
|
|
340
|
+
* <AutocompleteItem key={item.value} value={item}>
|
|
341
|
+
* {item.label}
|
|
342
|
+
* </AutocompleteItem>
|
|
343
|
+
* )}
|
|
344
|
+
* </AutocompleteCollection>
|
|
345
|
+
* </AutocompleteGroup>
|
|
346
|
+
* )}
|
|
74
347
|
* </AutocompleteList>
|
|
75
348
|
* </AutocompleteRoot>
|
|
76
|
-
*
|
|
77
|
-
|
|
78
|
-
export const
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
351
|
+
export const WithGroups: Story = {
|
|
79
352
|
render: () => {
|
|
353
|
+
interface FoodItem {
|
|
354
|
+
value: string;
|
|
355
|
+
label: string;
|
|
356
|
+
}
|
|
357
|
+
interface FoodGroup {
|
|
358
|
+
value: string;
|
|
359
|
+
items: FoodItem[];
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const groups: FoodGroup[] = [
|
|
363
|
+
{
|
|
364
|
+
value: "fruits",
|
|
365
|
+
items: [
|
|
366
|
+
{ value: "apple", label: "Apple" },
|
|
367
|
+
{ value: "banana", label: "Banana" },
|
|
368
|
+
{ value: "orange", label: "Orange" },
|
|
369
|
+
],
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
value: "vegetables",
|
|
373
|
+
items: [
|
|
374
|
+
{ value: "carrot", label: "Carrot" },
|
|
375
|
+
{ value: "spinach", label: "Spinach" },
|
|
376
|
+
{ value: "broccoli", label: "Broccoli" },
|
|
377
|
+
],
|
|
378
|
+
},
|
|
379
|
+
];
|
|
380
|
+
|
|
80
381
|
return (
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
<
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
382
|
+
<AutocompleteRoot items={groups}>
|
|
383
|
+
<AutocompleteInput placeholder="Search food..." adornment="trigger" />
|
|
384
|
+
<AutocompletePopup>
|
|
385
|
+
<AutocompleteList>
|
|
386
|
+
{(group: FoodGroup) => (
|
|
387
|
+
<AutocompleteGroup key={group.value} items={group.items}>
|
|
388
|
+
<AutocompleteGroupLabel className="capitalize">
|
|
389
|
+
{group.value}
|
|
390
|
+
</AutocompleteGroupLabel>
|
|
391
|
+
<AutocompleteCollection>
|
|
392
|
+
{(item: FoodItem) => (
|
|
393
|
+
<AutocompleteItem key={item.value} value={item}>
|
|
394
|
+
{item.label}
|
|
395
|
+
</AutocompleteItem>
|
|
396
|
+
)}
|
|
397
|
+
</AutocompleteCollection>
|
|
398
|
+
</AutocompleteGroup>
|
|
399
|
+
)}
|
|
400
|
+
</AutocompleteList>
|
|
401
|
+
<AutocompleteEmpty>No results</AutocompleteEmpty>
|
|
402
|
+
</AutocompletePopup>
|
|
403
|
+
</AutocompleteRoot>
|
|
93
404
|
);
|
|
94
405
|
},
|
|
95
406
|
};
|
|
96
407
|
|
|
97
|
-
|
|
408
|
+
/**
|
|
409
|
+
* Base UI's `render` prop replaces the underlying DOM element while preserving all
|
|
410
|
+
* autocomplete behavior (keyboard navigation, ARIA attributes, filtering, selection).
|
|
411
|
+
*
|
|
412
|
+
* Pass `render={<textarea />}` to `AutocompleteInput` to enable multiline text entry.
|
|
413
|
+
* The override works because `{...props}` is spread after the default
|
|
414
|
+
* `render={<input autoComplete="off" />}` inside `AutocompleteInput`.
|
|
415
|
+
*
|
|
416
|
+
* Override `h-10` from `inputBaseClasses` via `className="h-auto"` since textarea
|
|
417
|
+
* height is determined by `rows` rather than a fixed value.
|
|
418
|
+
*/
|
|
419
|
+
export const customRender: Story = {
|
|
420
|
+
render: () => (
|
|
421
|
+
<AutocompleteRoot
|
|
422
|
+
items={[
|
|
423
|
+
{ value: "apple", label: "Apple" },
|
|
424
|
+
{ value: "banana", label: "Banana" },
|
|
425
|
+
{ value: "cherry", label: "Cherry" },
|
|
426
|
+
{ value: "grape", label: "Grape" },
|
|
427
|
+
{ value: "kiwi", label: "Kiwi" },
|
|
428
|
+
]}
|
|
429
|
+
>
|
|
430
|
+
<AutocompleteInput
|
|
431
|
+
placeholder="Search..."
|
|
432
|
+
render={<textarea rows={3} />}
|
|
433
|
+
className="h-auto resize-none"
|
|
434
|
+
/>
|
|
435
|
+
<AutocompletePopup>
|
|
436
|
+
<AutocompleteList>
|
|
437
|
+
{(item) => (
|
|
438
|
+
<AutocompleteItem key={item.value} value={item}>
|
|
439
|
+
{item.label}
|
|
440
|
+
</AutocompleteItem>
|
|
441
|
+
)}
|
|
442
|
+
</AutocompleteList>
|
|
443
|
+
<AutocompleteEmpty>No results</AutocompleteEmpty>
|
|
444
|
+
</AutocompletePopup>
|
|
445
|
+
</AutocompleteRoot>
|
|
446
|
+
),
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* `triggerProps.render` replaces the trigger's `<button>` element.
|
|
451
|
+
* Base UI transfers all accessibility attributes (aria-*, data-*, event handlers)
|
|
452
|
+
* to the provided element, so the open/close behavior is fully preserved.
|
|
453
|
+
*
|
|
454
|
+
* `triggerProps.children` replaces the default `ChevronsUpDown` icon.
|
|
455
|
+
* `triggerProps.className` is merged with (not replaced by) the default trigger classes,
|
|
456
|
+
* so positioning and sizing stay intact.
|
|
457
|
+
*/
|
|
458
|
+
export const WithCustomTrigger: Story = {
|
|
459
|
+
render: () => (
|
|
460
|
+
<AutocompleteRoot
|
|
461
|
+
items={[
|
|
462
|
+
{ value: "apple", label: "Apple" },
|
|
463
|
+
{ value: "banana", label: "Banana" },
|
|
464
|
+
{ value: "cherry", label: "Cherry" },
|
|
465
|
+
{ value: "grape", label: "Grape" },
|
|
466
|
+
{ value: "kiwi", label: "Kiwi" },
|
|
467
|
+
]}
|
|
468
|
+
>
|
|
469
|
+
<AutocompleteInput
|
|
470
|
+
adornment="trigger"
|
|
471
|
+
placeholder="Search fruit..."
|
|
472
|
+
triggerProps={{
|
|
473
|
+
render: <button type="button" />,
|
|
474
|
+
className:
|
|
475
|
+
"px-2 text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
476
|
+
children: "▾",
|
|
477
|
+
}}
|
|
478
|
+
/>
|
|
479
|
+
<AutocompletePopup>
|
|
480
|
+
<AutocompleteList>
|
|
481
|
+
{(item) => (
|
|
482
|
+
<AutocompleteItem key={item.value} value={item}>
|
|
483
|
+
{item.label}
|
|
484
|
+
</AutocompleteItem>
|
|
485
|
+
)}
|
|
486
|
+
</AutocompleteList>
|
|
487
|
+
<AutocompleteEmpty>No results</AutocompleteEmpty>
|
|
488
|
+
</AutocompletePopup>
|
|
489
|
+
</AutocompleteRoot>
|
|
490
|
+
),
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* `AutocompleteRow` groups items into a horizontal row for multi-column list layouts.
|
|
495
|
+
* Useful for compact grids like color pickers, flag selectors, or emoji pickers.
|
|
496
|
+
* Each item inside the row is individually selectable and keyboard-navigable.
|
|
497
|
+
*
|
|
498
|
+
* Pass rows as the `items` array — each entry is a row, and the row's items are
|
|
499
|
+
* rendered with `AutocompleteItem` inside.
|
|
500
|
+
*/
|
|
501
|
+
export const WithRow: Story = {
|
|
98
502
|
render: () => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
503
|
+
interface ColorOption {
|
|
504
|
+
value: string;
|
|
505
|
+
label: string;
|
|
506
|
+
hex: string;
|
|
507
|
+
}
|
|
508
|
+
type ColorRow = ColorOption[];
|
|
509
|
+
|
|
510
|
+
const colorRows: ColorRow[] = [
|
|
511
|
+
[
|
|
512
|
+
{ value: "red", label: "Red", hex: "#ef4444" },
|
|
513
|
+
{ value: "orange", label: "Orange", hex: "#f97316" },
|
|
514
|
+
{ value: "yellow", label: "Yellow", hex: "#eab308" },
|
|
515
|
+
],
|
|
516
|
+
[
|
|
517
|
+
{ value: "green", label: "Green", hex: "#22c55e" },
|
|
518
|
+
{ value: "blue", label: "Blue", hex: "#3b82f6" },
|
|
519
|
+
{ value: "purple", label: "Purple", hex: "#a855f7" },
|
|
520
|
+
],
|
|
521
|
+
];
|
|
103
522
|
|
|
104
523
|
return (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<AutoCompleteItem>
|
|
134
|
-
<User />
|
|
135
|
-
<span>Mi perfil</span>
|
|
136
|
-
<AutoCompleteShortcut>P</AutoCompleteShortcut>
|
|
137
|
-
</AutoCompleteItem>
|
|
138
|
-
<AutoCompleteItem>
|
|
139
|
-
<Settings />
|
|
140
|
-
<span>Configuración</span>
|
|
141
|
-
<AutoCompleteShortcut>C</AutoCompleteShortcut>
|
|
142
|
-
</AutoCompleteItem>
|
|
143
|
-
</AutoCompleteGroup>
|
|
144
|
-
</AutoCompleteList>
|
|
145
|
-
</AutoCompleteDialog>
|
|
146
|
-
</>
|
|
524
|
+
<AutocompleteRoot
|
|
525
|
+
items={colorRows}
|
|
526
|
+
itemToStringValue={(row) => (row as ColorRow)[0]?.label ?? ""}
|
|
527
|
+
>
|
|
528
|
+
<AutocompleteInput placeholder="Search color..." />
|
|
529
|
+
<AutocompletePopup>
|
|
530
|
+
<AutocompleteList>
|
|
531
|
+
{(row: ColorRow) => (
|
|
532
|
+
<AutocompleteRow key={row[0].value}>
|
|
533
|
+
{row.map((color) => (
|
|
534
|
+
<AutocompleteItem
|
|
535
|
+
key={color.value}
|
|
536
|
+
value={color}
|
|
537
|
+
className="flex-col gap-1 rounded-md p-2 text-center text-xs"
|
|
538
|
+
>
|
|
539
|
+
<span
|
|
540
|
+
className="block size-6 rounded-full border"
|
|
541
|
+
style={{ background: color.hex }}
|
|
542
|
+
/>
|
|
543
|
+
{color.label}
|
|
544
|
+
</AutocompleteItem>
|
|
545
|
+
))}
|
|
546
|
+
</AutocompleteRow>
|
|
547
|
+
)}
|
|
548
|
+
</AutocompleteList>
|
|
549
|
+
<AutocompleteEmpty>No results</AutocompleteEmpty>
|
|
550
|
+
</AutocompletePopup>
|
|
551
|
+
</AutocompleteRoot>
|
|
147
552
|
);
|
|
148
553
|
},
|
|
149
554
|
};
|
|
150
555
|
|
|
151
|
-
|
|
152
|
-
|
|
556
|
+
/**
|
|
557
|
+
* `AutocompleteValue` renders the selected item's label outside the input.
|
|
558
|
+
* Use it when you need to display the current selection independently —
|
|
559
|
+
* e.g., inside a custom trigger, a badge, or a read-only display area.
|
|
560
|
+
*
|
|
561
|
+
* It renders nothing when no item is selected, so no conditional rendering is needed.
|
|
562
|
+
* The displayed text is determined by `itemToStringValue` on `AutocompleteRoot`.
|
|
563
|
+
*/
|
|
564
|
+
export const WithValueDisplay: Story = {
|
|
153
565
|
render: () => {
|
|
154
|
-
const
|
|
566
|
+
const countries = [
|
|
567
|
+
{ value: "ar", label: "Argentina" },
|
|
568
|
+
{ value: "br", label: "Brazil" },
|
|
569
|
+
{ value: "cl", label: "Chile" },
|
|
570
|
+
{ value: "co", label: "Colombia" },
|
|
571
|
+
{ value: "mx", label: "Mexico" },
|
|
572
|
+
{ value: "uy", label: "Uruguay" },
|
|
573
|
+
];
|
|
155
574
|
|
|
156
575
|
return (
|
|
157
|
-
<
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
</AutoCompleteItem>
|
|
178
|
-
<AutoCompleteItem>
|
|
179
|
-
<Slack />
|
|
180
|
-
Slack
|
|
181
|
-
</AutoCompleteItem>
|
|
182
|
-
<AutoCompleteItem>
|
|
183
|
-
<Github />
|
|
184
|
-
Github
|
|
185
|
-
</AutoCompleteItem>
|
|
186
|
-
<AutoCompleteItem>
|
|
187
|
-
<FlameKindling />
|
|
188
|
-
Señal de humo
|
|
189
|
-
</AutoCompleteItem>
|
|
190
|
-
</AutoCompleteGroup>
|
|
191
|
-
</AutoCompleteList>
|
|
192
|
-
</AutoCompleteRoot>
|
|
193
|
-
</MenuSubContent>
|
|
194
|
-
</MenuGroup>
|
|
195
|
-
</MenuSub>
|
|
196
|
-
</MenuContent>
|
|
197
|
-
</MenuRoot>
|
|
576
|
+
<AutocompleteRoot
|
|
577
|
+
items={countries}
|
|
578
|
+
itemToStringValue={(c) => (c as (typeof countries)[0]).label}
|
|
579
|
+
>
|
|
580
|
+
<AutocompleteInput placeholder="Search country..." adornment="clear" />
|
|
581
|
+
<div className="mt-2 flex items-center gap-2 text-sm text-muted-foreground">
|
|
582
|
+
<span>Selected:</span>
|
|
583
|
+
<AutocompleteValue />
|
|
584
|
+
</div>
|
|
585
|
+
<AutocompletePopup>
|
|
586
|
+
<AutocompleteList>
|
|
587
|
+
{(country) => (
|
|
588
|
+
<AutocompleteItem key={country.value} value={country}>
|
|
589
|
+
{country.label}
|
|
590
|
+
</AutocompleteItem>
|
|
591
|
+
)}
|
|
592
|
+
</AutocompleteList>
|
|
593
|
+
<AutocompleteEmpty>No results</AutocompleteEmpty>
|
|
594
|
+
</AutocompletePopup>
|
|
595
|
+
</AutocompleteRoot>
|
|
198
596
|
);
|
|
199
597
|
},
|
|
200
598
|
};
|
|
201
599
|
|
|
202
|
-
|
|
203
|
-
|
|
600
|
+
/**
|
|
601
|
+
* Direct composition with all primitives for full structural control.
|
|
602
|
+
* Use when the composite `Autocomplete` is not enough — custom element ordering,
|
|
603
|
+
* extra slots between parts, non-standard layouts, or `AutocompleteSeparator` usage.
|
|
604
|
+
*
|
|
605
|
+
* `AutocompleteSeparator` uses `last:hidden` to suppress a trailing divider automatically.
|
|
606
|
+
*
|
|
607
|
+
* ```tsx
|
|
608
|
+
* <AutocompleteRoot items={items}>
|
|
609
|
+
* <AutocompleteInput placeholder="Search..." adornment="clear"/>
|
|
610
|
+
* <AutocompletePopup>
|
|
611
|
+
* <AutocompleteStatus>{status}</AutocompleteStatus>
|
|
612
|
+
* <AutocompleteList>
|
|
613
|
+
* {(item) => (
|
|
614
|
+
* <>
|
|
615
|
+
* <AutocompleteItem key={item.value} value={item}>
|
|
616
|
+
* {item.label}
|
|
617
|
+
* </AutocompleteItem>
|
|
618
|
+
* <AutocompleteSeparator />
|
|
619
|
+
* </>
|
|
620
|
+
* )}
|
|
621
|
+
* </AutocompleteList>
|
|
622
|
+
* <AutocompleteEmpty>No results</AutocompleteEmpty>
|
|
623
|
+
* </AutocompletePopup>
|
|
624
|
+
* </AutocompleteRoot>
|
|
625
|
+
* ```
|
|
626
|
+
*/
|
|
627
|
+
export const Primitive: Story = {
|
|
204
628
|
render: () => {
|
|
629
|
+
const countries = [
|
|
630
|
+
{ value: "ar", label: "Argentina" },
|
|
631
|
+
{ value: "br", label: "Brazil" },
|
|
632
|
+
{ value: "cl", label: "Chile" },
|
|
633
|
+
{ value: "co", label: "Colombia" },
|
|
634
|
+
{ value: "mx", label: "Mexico" },
|
|
635
|
+
{ value: "uy", label: "Uruguay" },
|
|
636
|
+
];
|
|
637
|
+
|
|
205
638
|
return (
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
</AutoCompleteItem>
|
|
223
|
-
<AutoCompleteItem>
|
|
224
|
-
<Slack />
|
|
225
|
-
Slack
|
|
226
|
-
</AutoCompleteItem>
|
|
227
|
-
<AutoCompleteItem>
|
|
228
|
-
<Github />
|
|
229
|
-
Github
|
|
230
|
-
</AutoCompleteItem>
|
|
231
|
-
<AutoCompleteItem>
|
|
232
|
-
<FlameKindling />
|
|
233
|
-
Señal de humo
|
|
234
|
-
</AutoCompleteItem>
|
|
235
|
-
</AutoCompleteGroup>
|
|
236
|
-
</AutoCompleteList>
|
|
237
|
-
</AutoCompleteRoot>
|
|
238
|
-
</Popover>
|
|
639
|
+
<AutocompleteRoot items={countries}>
|
|
640
|
+
<AutocompleteInput placeholder="Search country..." adornment="clear" />
|
|
641
|
+
<AutocompletePopup>
|
|
642
|
+
<AutocompleteList>
|
|
643
|
+
{(country) => (
|
|
644
|
+
<>
|
|
645
|
+
<AutocompleteItem key={country.value} value={country}>
|
|
646
|
+
{country.label}
|
|
647
|
+
</AutocompleteItem>
|
|
648
|
+
<AutocompleteSeparator />
|
|
649
|
+
</>
|
|
650
|
+
)}
|
|
651
|
+
</AutocompleteList>
|
|
652
|
+
<AutocompleteEmpty>No results</AutocompleteEmpty>
|
|
653
|
+
</AutocompletePopup>
|
|
654
|
+
</AutocompleteRoot>
|
|
239
655
|
);
|
|
240
656
|
},
|
|
241
657
|
};
|